forked from vitrine/wmaker
Rewrite WINGs/notification.c in Rust.
This commit is contained in:
@@ -73,7 +73,6 @@ libWUtil_la_SOURCES = \
|
||||
menuparser.h \
|
||||
menuparser_macros.c \
|
||||
misc.c \
|
||||
notification.c \
|
||||
proplist.c \
|
||||
userdefaults.c \
|
||||
userdefaults.h \
|
||||
|
||||
@@ -599,8 +599,6 @@ void WMTreeWalk(WMTreeNode *aNode, WMTreeWalkProc * walk, void *data);
|
||||
/* ---[ WINGs/notification.c ]---------------------------------------------------- */
|
||||
|
||||
|
||||
void WMReleaseNotification(WMNotification *notification);
|
||||
|
||||
void* WMGetNotificationClientData(WMNotification *notification);
|
||||
|
||||
void* WMGetNotificationObject(WMNotification *notification);
|
||||
|
||||
@@ -1,278 +0,0 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "WUtil.h"
|
||||
#include "WINGsP.h"
|
||||
|
||||
|
||||
typedef struct W_Notification {
|
||||
const char *name;
|
||||
void *object;
|
||||
void *clientData;
|
||||
int refCount;
|
||||
} Notification;
|
||||
|
||||
|
||||
static WMNotification* WMRetainNotification(WMNotification *notification);
|
||||
|
||||
const char *WMGetNotificationName(WMNotification * notification)
|
||||
{
|
||||
return notification->name;
|
||||
}
|
||||
|
||||
void *WMGetNotificationObject(WMNotification * notification)
|
||||
{
|
||||
return notification->object;
|
||||
}
|
||||
|
||||
void *WMGetNotificationClientData(WMNotification * notification)
|
||||
{
|
||||
return notification->clientData;
|
||||
}
|
||||
|
||||
WMNotification *WMCreateNotification(const char *name, void *object, void *clientData)
|
||||
{
|
||||
Notification *nPtr;
|
||||
|
||||
nPtr = wmalloc(sizeof(Notification));
|
||||
nPtr->name = name;
|
||||
nPtr->object = object;
|
||||
nPtr->clientData = clientData;
|
||||
nPtr->refCount = 1;
|
||||
|
||||
return nPtr;
|
||||
}
|
||||
|
||||
void WMReleaseNotification(WMNotification * notification)
|
||||
{
|
||||
notification->refCount--;
|
||||
|
||||
if (notification->refCount < 1) {
|
||||
wfree(notification);
|
||||
}
|
||||
}
|
||||
|
||||
static WMNotification *WMRetainNotification(WMNotification * notification)
|
||||
{
|
||||
notification->refCount++;
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
/***************** Notification Center *****************/
|
||||
|
||||
typedef struct NotificationObserver {
|
||||
WMNotificationObserverAction *observerAction;
|
||||
void *observer;
|
||||
|
||||
const char *name;
|
||||
void *object;
|
||||
|
||||
struct NotificationObserver *prev; /* for tables */
|
||||
struct NotificationObserver *next;
|
||||
struct NotificationObserver *nextAction; /* for observerTable */
|
||||
} NotificationObserver;
|
||||
|
||||
typedef struct W_NotificationCenter {
|
||||
WMHashTable *nameTable; /* names -> observer lists */
|
||||
WMHashTable *objectTable; /* object -> observer lists */
|
||||
NotificationObserver *nilList; /* obervers that catch everything */
|
||||
|
||||
WMHashTable *observerTable; /* observer -> NotificationObserver */
|
||||
} NotificationCenter;
|
||||
|
||||
/* default (and only) center */
|
||||
static NotificationCenter *notificationCenter = NULL;
|
||||
|
||||
void W_InitNotificationCenter(void)
|
||||
{
|
||||
notificationCenter = wmalloc(sizeof(NotificationCenter));
|
||||
notificationCenter->nameTable = WMCreateStringHashTable();
|
||||
notificationCenter->objectTable = WMCreateIdentityHashTable();
|
||||
notificationCenter->nilList = NULL;
|
||||
notificationCenter->observerTable = WMCreateIdentityHashTable();
|
||||
}
|
||||
|
||||
void W_ReleaseNotificationCenter(void)
|
||||
{
|
||||
if (notificationCenter) {
|
||||
if (notificationCenter->nameTable)
|
||||
WMFreeHashTable(notificationCenter->nameTable);
|
||||
if (notificationCenter->objectTable)
|
||||
WMFreeHashTable(notificationCenter->objectTable);
|
||||
if (notificationCenter->observerTable)
|
||||
WMFreeHashTable(notificationCenter->observerTable);
|
||||
|
||||
wfree(notificationCenter);
|
||||
notificationCenter = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WMAddNotificationObserver(WMNotificationObserverAction * observerAction,
|
||||
void *observer, const char *name, void *object)
|
||||
{
|
||||
NotificationObserver *oRec, *rec;
|
||||
|
||||
oRec = wmalloc(sizeof(NotificationObserver));
|
||||
oRec->observerAction = observerAction;
|
||||
oRec->observer = observer;
|
||||
oRec->name = name;
|
||||
oRec->object = object;
|
||||
oRec->next = NULL;
|
||||
oRec->prev = NULL;
|
||||
|
||||
/* put this action in the list of actions for this observer */
|
||||
rec = (NotificationObserver *) WMHashInsert(notificationCenter->observerTable, observer, oRec);
|
||||
|
||||
if (rec) {
|
||||
/* if this is not the first action for the observer */
|
||||
oRec->nextAction = rec;
|
||||
} else {
|
||||
oRec->nextAction = NULL;
|
||||
}
|
||||
|
||||
if (!name && !object) {
|
||||
/* catch-all */
|
||||
oRec->next = notificationCenter->nilList;
|
||||
if (notificationCenter->nilList) {
|
||||
notificationCenter->nilList->prev = oRec;
|
||||
}
|
||||
notificationCenter->nilList = oRec;
|
||||
} else if (!name) {
|
||||
/* any message coming from object */
|
||||
rec = (NotificationObserver *) WMHashInsert(notificationCenter->objectTable, object, oRec);
|
||||
oRec->next = rec;
|
||||
if (rec) {
|
||||
rec->prev = oRec;
|
||||
}
|
||||
} else {
|
||||
/* name && (object || !object) */
|
||||
rec = (NotificationObserver *) WMHashInsert(notificationCenter->nameTable, name, oRec);
|
||||
oRec->next = rec;
|
||||
if (rec) {
|
||||
rec->prev = oRec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WMPostNotification(WMNotification * notification)
|
||||
{
|
||||
NotificationObserver *orec, *tmp;
|
||||
|
||||
WMRetainNotification(notification);
|
||||
|
||||
/* tell the observers that want to know about a particular message */
|
||||
orec = (NotificationObserver *) WMHashGet(notificationCenter->nameTable, notification->name);
|
||||
|
||||
while (orec) {
|
||||
tmp = orec->next;
|
||||
|
||||
if (!orec->object || !notification->object || orec->object == notification->object) {
|
||||
/* tell the observer */
|
||||
if (orec->observerAction) {
|
||||
(*orec->observerAction) (orec->observer, notification);
|
||||
}
|
||||
}
|
||||
|
||||
orec = tmp;
|
||||
}
|
||||
|
||||
/* tell the observers that want to know about an object */
|
||||
orec = (NotificationObserver *) WMHashGet(notificationCenter->objectTable, notification->object);
|
||||
|
||||
while (orec) {
|
||||
tmp = orec->next;
|
||||
|
||||
/* tell the observer */
|
||||
if (orec->observerAction) {
|
||||
(*orec->observerAction) (orec->observer, notification);
|
||||
}
|
||||
orec = tmp;
|
||||
}
|
||||
|
||||
/* tell the catch all observers */
|
||||
orec = notificationCenter->nilList;
|
||||
while (orec) {
|
||||
tmp = orec->next;
|
||||
|
||||
/* tell the observer */
|
||||
if (orec->observerAction) {
|
||||
(*orec->observerAction) (orec->observer, notification);
|
||||
}
|
||||
orec = tmp;
|
||||
}
|
||||
|
||||
WMReleaseNotification(notification);
|
||||
}
|
||||
|
||||
void WMRemoveNotificationObserver(void *observer)
|
||||
{
|
||||
NotificationObserver *orec, *tmp, *rec;
|
||||
|
||||
/* get the list of actions the observer is doing */
|
||||
orec = (NotificationObserver *) WMHashGet(notificationCenter->observerTable, observer);
|
||||
|
||||
/*
|
||||
* FOREACH orec IN actionlist for observer
|
||||
* DO
|
||||
* remove from respective lists/tables
|
||||
* free
|
||||
* END
|
||||
*/
|
||||
while (orec) {
|
||||
tmp = orec->nextAction;
|
||||
|
||||
if (!orec->name && !orec->object) {
|
||||
/* catch-all */
|
||||
if (notificationCenter->nilList == orec)
|
||||
notificationCenter->nilList = orec->next;
|
||||
} else if (!orec->name) {
|
||||
/* any message coming from object */
|
||||
rec = (NotificationObserver *) WMHashGet(notificationCenter->objectTable, orec->object);
|
||||
if (rec == orec) {
|
||||
/* replace table entry */
|
||||
if (orec->next) {
|
||||
WMHashInsert(notificationCenter->objectTable, orec->object, orec->next);
|
||||
} else {
|
||||
WMHashRemove(notificationCenter->objectTable, orec->object);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* name && (object || !object) */
|
||||
rec = (NotificationObserver *) WMHashGet(notificationCenter->nameTable, orec->name);
|
||||
if (rec == orec) {
|
||||
/* replace table entry */
|
||||
if (orec->next) {
|
||||
WMHashInsert(notificationCenter->nameTable, orec->name, orec->next);
|
||||
} else {
|
||||
WMHashRemove(notificationCenter->nameTable, orec->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (orec->prev)
|
||||
orec->prev->next = orec->next;
|
||||
if (orec->next)
|
||||
orec->next->prev = orec->prev;
|
||||
|
||||
wfree(orec);
|
||||
|
||||
orec = tmp;
|
||||
}
|
||||
|
||||
WMHashRemove(notificationCenter->observerTable, observer);
|
||||
}
|
||||
|
||||
void WMPostNotificationName(const char *name, void *object, void *clientData)
|
||||
{
|
||||
WMNotification *notification;
|
||||
|
||||
notification = WMCreateNotification(name, object, clientData);
|
||||
|
||||
WMPostNotification(notification);
|
||||
|
||||
WMReleaseNotification(notification);
|
||||
}
|
||||
@@ -44,9 +44,6 @@ void WMInitializeApplication(const char *applicationName, int *argc, char **argv
|
||||
WMApplication.argv[i] = wstrdup(argv[i]);
|
||||
}
|
||||
WMApplication.argv[i] = NULL;
|
||||
|
||||
/* initialize notification center */
|
||||
W_InitNotificationCenter();
|
||||
}
|
||||
|
||||
void WMReleaseApplication(void) {
|
||||
@@ -60,7 +57,7 @@ void WMReleaseApplication(void) {
|
||||
*/
|
||||
w_save_defaults_changes();
|
||||
|
||||
W_ReleaseNotificationCenter();
|
||||
W_ClearNotificationCenter();
|
||||
|
||||
if (WMApplication.applicationName) {
|
||||
wfree(WMApplication.applicationName);
|
||||
|
||||
@@ -10,6 +10,7 @@ RUST_SOURCES = \
|
||||
src/hash_table.rs \
|
||||
src/lib.rs \
|
||||
src/memory.rs \
|
||||
src/notification.rs \
|
||||
src/prop_list.rs \
|
||||
src/string.rs
|
||||
src/tree.rs
|
||||
|
||||
@@ -5,6 +5,7 @@ pub mod defines;
|
||||
pub mod find_file;
|
||||
pub mod hash_table;
|
||||
pub mod memory;
|
||||
pub mod notification;
|
||||
pub mod prop_list;
|
||||
pub mod string;
|
||||
pub mod tree;
|
||||
|
||||
@@ -0,0 +1,368 @@
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
ffi::{c_void, CStr},
|
||||
hash,
|
||||
ptr::{self, NonNull},
|
||||
sync::{Mutex, OnceLock},
|
||||
};
|
||||
|
||||
/// Lightweight message from object to another. When a notification is sent,
|
||||
/// registered [`Action`]s are called.
|
||||
///
|
||||
/// Use [`ffi::WMAddNotificationObserver`] or [`NotificationCenter::register`]
|
||||
/// to request notifications.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// `Notification` encapsulates two data pointers. The Rust implementation
|
||||
/// explicitly supports notifications across threads. To uphold Rust's safety
|
||||
/// rules, `Notification`s must only be created with pointers that can be sent
|
||||
/// across threads. The [`Sendable`] struct is provided to guarantee this.
|
||||
///
|
||||
/// ## Rust rewrite notes
|
||||
///
|
||||
/// This was originally a reference-counted structure, but it is so lightweight
|
||||
/// (consisting of three pointers) that the Rust version is `Copy`. This
|
||||
/// simplifies things --- each Rust recipient of a `Notification` owns it, and
|
||||
/// there is no need to coordinate how it is cleaned up.
|
||||
///
|
||||
/// In unported C code, a notificaton's `name` may be compared against a string
|
||||
/// constant using pointer equality rather than string equality. This has
|
||||
/// negative implications for sending notifications across the Rust/C
|
||||
/// boundary. For the time being, notifications are generated and received by
|
||||
/// code written in C, but care should be taken that notification names are
|
||||
/// checked properly when porting notification registration in the future. (We
|
||||
/// will ideally move to a different data type for identifying notifications,
|
||||
/// too.)
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Notification {
|
||||
name: &'static CStr,
|
||||
/// The object that generated the notification. This may be `None` for
|
||||
/// notifications that are about global state.
|
||||
source: Option<Sendable>,
|
||||
/// Optional side-channel data provided to notification listeners.
|
||||
client_data: Option<Sendable>,
|
||||
}
|
||||
|
||||
/// Callback that notifies `observer` (which may be null) of `notification` (which won't be).
|
||||
pub type Action = unsafe extern "C" fn(observer: *mut c_void, notification: *const Notification);
|
||||
|
||||
/// Wraps a type-erased pointer (which it does not own) and marks it as `Send`.
|
||||
///
|
||||
/// The `Send`-ability of the wrapped pointer must be guaranteed code that
|
||||
/// instantiates a `Sendable`.
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub struct Sendable {
|
||||
ptr: NonNull<c_void>,
|
||||
}
|
||||
|
||||
impl Sendable {
|
||||
/// Creates a `Sendable` wrapping `ptr`.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// `ptr` must be safe to send across threads.
|
||||
pub unsafe fn new(ptr: NonNull<c_void>) -> Self {
|
||||
Sendable { ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl hash::Hash for Sendable {
|
||||
fn hash<H: hash::Hasher>(&self, h: &mut H) {
|
||||
h.write_usize(self.ptr.as_ptr().addr());
|
||||
}
|
||||
}
|
||||
|
||||
// Guaranteed by `Sendable::new`.
|
||||
unsafe impl Send for Sendable {}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct NotificationCenter {
|
||||
/// Notification subscriptions that match on name and source.
|
||||
exact: HashMap<(&'static CStr, Sendable), Vec<(Option<Sendable>, Action)>>,
|
||||
/// Notification subscriptions that match on name.
|
||||
by_name: HashMap<&'static CStr, Vec<(Option<Sendable>, Action)>>,
|
||||
/// Notification subscriptions that match on source.
|
||||
by_source: HashMap<Sendable, Vec<(Option<Sendable>, Action)>>,
|
||||
/// Notification subscriptions that match all notifications.
|
||||
universal: Vec<(Option<Sendable>, Action)>,
|
||||
}
|
||||
|
||||
// It is safe to send NotificationCenter across threads as long as the contract
|
||||
// on Sendable is respected.
|
||||
unsafe impl Send for NotificationCenter {}
|
||||
|
||||
impl NotificationCenter {
|
||||
/// Creates a new `NotificationCenter`.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Provides access to the default, process-wide notification center. The
|
||||
/// FFI C API uses this notification center. This is protected behind a
|
||||
/// mutex that is held while `f` is run, so panicking inside of `f` should
|
||||
/// be avoided.
|
||||
pub fn with_global_default<R>(f: impl FnOnce(&mut Self) -> R) -> R {
|
||||
static INSTANCE: OnceLock<Mutex<NotificationCenter>> = OnceLock::new();
|
||||
f(&mut INSTANCE
|
||||
.get_or_init(|| Mutex::new(NotificationCenter::new()))
|
||||
.try_lock()
|
||||
.unwrap())
|
||||
}
|
||||
|
||||
/// Registers `action` to be invoked and invoked on `observer` when
|
||||
/// notifications named `name` are fired from `source`.
|
||||
pub fn register_exact(
|
||||
&mut self,
|
||||
name: &'static CStr,
|
||||
source: Sendable,
|
||||
observer: Option<Sendable>,
|
||||
action: Action,
|
||||
) {
|
||||
match self.exact.entry((name, source)) {
|
||||
Entry::Occupied(mut o) => {
|
||||
o.get_mut().push((observer, action));
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(vec![(observer, action)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers `action` to be invoked on `observer` when notifications are
|
||||
/// fired by `source` (regardless of the notification name).
|
||||
pub fn register_by_source(
|
||||
&mut self,
|
||||
source: Sendable,
|
||||
observer: Option<Sendable>,
|
||||
action: Action,
|
||||
) {
|
||||
match self.by_source.entry(source) {
|
||||
Entry::Occupied(mut o) => {
|
||||
o.get_mut().push((observer, action));
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(vec![(observer, action)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers `action` to be invoked on `observer` for all notifications
|
||||
/// named `name`.
|
||||
pub fn register_by_name(
|
||||
&mut self,
|
||||
name: &'static CStr,
|
||||
observer: Option<Sendable>,
|
||||
action: Action,
|
||||
) {
|
||||
match self.by_name.entry(name) {
|
||||
Entry::Occupied(mut o) => {
|
||||
o.get_mut().push((observer, action));
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(vec![(observer, action)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers `action` to be invoked on `observer` for all notifications,
|
||||
/// regardless of the notification's name or source.
|
||||
pub fn register_universal(&mut self, observer: Option<Sendable>, action: Action) {
|
||||
self.universal.push((observer, action));
|
||||
}
|
||||
|
||||
/// Dispatches `notification` with registered actions.
|
||||
pub fn dispatch(&mut self, notification: Notification) {
|
||||
if let Some(observers) = self.by_name.get_mut(notification.name) {
|
||||
for (observer, action) in observers {
|
||||
let observer = observer.map(|x| x.ptr.as_ptr()).unwrap_or(ptr::null_mut());
|
||||
unsafe {
|
||||
(action)(observer, ¬ification);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(source) = notification.source {
|
||||
if let Some(observers) = self.exact.get_mut(&(notification.name, source)) {
|
||||
for (observer, action) in observers {
|
||||
let observer = observer.map(|x| x.ptr.as_ptr()).unwrap_or(ptr::null_mut());
|
||||
unsafe {
|
||||
(action)(observer, ¬ification);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(observers) = self.by_source.get_mut(&source) {
|
||||
for (observer, action) in observers {
|
||||
let observer = observer.map(|x| x.ptr.as_ptr()).unwrap_or(ptr::null_mut());
|
||||
unsafe {
|
||||
(action)(observer, ¬ification);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (observer, action) in &mut self.universal {
|
||||
let observer = observer.map(|x| x.ptr.as_ptr()).unwrap_or(ptr::null_mut());
|
||||
unsafe {
|
||||
(action)(observer, ¬ification);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes all notification subscriptions that would notify `observer` if they fired.
|
||||
pub fn remove_observer(&mut self, observer: Sendable) {
|
||||
self.exact.retain(|_, values| {
|
||||
values.retain(|(o, _)| *o != Some(observer));
|
||||
!values.is_empty()
|
||||
});
|
||||
self.by_name.retain(|_, values| {
|
||||
values.retain(|(o, _)| *o != Some(observer));
|
||||
!values.is_empty()
|
||||
});
|
||||
self.by_source.retain(|_, values| {
|
||||
values.retain(|(o, _)| *o != Some(observer));
|
||||
!values.is_empty()
|
||||
});
|
||||
self.universal.retain(|(o, _)| *o != Some(observer));
|
||||
}
|
||||
|
||||
/// Clears all registered notification listeners and resets `self` to its
|
||||
/// default state.
|
||||
pub fn clear(&mut self) {
|
||||
*self = Self::default();
|
||||
}
|
||||
}
|
||||
|
||||
pub mod ffi {
|
||||
use super::{Action, Notification, NotificationCenter, Sendable};
|
||||
|
||||
use std::{
|
||||
ffi::{c_char, c_void, CStr},
|
||||
ptr::{self, NonNull},
|
||||
};
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn WMGetNotificationClientData(
|
||||
notification: *mut Notification,
|
||||
) -> *mut c_void {
|
||||
if notification.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
unsafe {
|
||||
(*notification)
|
||||
.client_data
|
||||
.map(|x| x.ptr.as_ptr())
|
||||
.unwrap_or(ptr::null_mut())
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn WMGetNotificationObject(
|
||||
notification: *mut Notification,
|
||||
) -> *mut c_void {
|
||||
if notification.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
unsafe {
|
||||
(*notification)
|
||||
.source
|
||||
.map(|x| x.ptr.as_ptr())
|
||||
.unwrap_or(ptr::null_mut())
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn WMGetNotificationName(
|
||||
notification: *mut Notification,
|
||||
) -> *const c_char {
|
||||
if notification.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
unsafe { (*notification).name.as_ptr() }
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn WMAddNotificationObserver(
|
||||
action: Option<Action>,
|
||||
observer: *mut c_void,
|
||||
name: *const c_char,
|
||||
object: *mut c_void,
|
||||
) {
|
||||
let Some(action) = action else {
|
||||
return;
|
||||
};
|
||||
let observer = NonNull::new(observer).map(|x| unsafe { Sendable::new(x) });
|
||||
let source = NonNull::new(object);
|
||||
NotificationCenter::with_global_default(|c| {
|
||||
if name.is_null() {
|
||||
match source {
|
||||
Some(source) => {
|
||||
c.register_by_source(unsafe { Sendable::new(source) }, observer, action);
|
||||
}
|
||||
None => c.register_universal(observer, action),
|
||||
}
|
||||
} else {
|
||||
let name = unsafe { CStr::from_ptr(name) };
|
||||
match source {
|
||||
Some(source) => {
|
||||
c.register_exact(name, unsafe { Sendable::new(source) }, observer, action);
|
||||
}
|
||||
None => c.register_by_name(name, observer, action),
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn WMRemoveNotificationObserver(observer: *mut c_void) {
|
||||
let Some(observer) = NonNull::new(observer) else {
|
||||
return;
|
||||
};
|
||||
|
||||
NotificationCenter::with_global_default(|c| {
|
||||
c.remove_observer(unsafe { Sendable::new(observer) })
|
||||
});
|
||||
}
|
||||
|
||||
/// Posts a notification from `object` with the given `name` and `client_data`.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// `name` must be a non-null string constant or some other pointer with a
|
||||
/// static lifetime.
|
||||
///
|
||||
/// `object` and `client_data` must be safe to send across threads (per the
|
||||
/// contract of [`Sendable`]).
|
||||
///
|
||||
/// ## Rust rewrite notes
|
||||
///
|
||||
/// This originally took a heap-allocated `*mut Notification`, but now the
|
||||
/// constructed `Notification` parameters are passed directly.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn WMPostNotificationName(
|
||||
name: *const c_char,
|
||||
object: *mut c_void,
|
||||
client_data: *mut c_void,
|
||||
) {
|
||||
if name.is_null() {
|
||||
return;
|
||||
}
|
||||
let name = unsafe { CStr::from_ptr(name) };
|
||||
let source = NonNull::new(object).map(|x| unsafe { Sendable::new(x) });
|
||||
let client_data = NonNull::new(client_data).map(|x| unsafe { Sendable::new(x) });
|
||||
NotificationCenter::with_global_default(|c| {
|
||||
c.dispatch(Notification {
|
||||
name,
|
||||
source,
|
||||
client_data,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Resets all notifications for the global notification center. Used by
|
||||
/// `WApplication` teardown code. This is a private, WINGs-internal API.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn W_ClearNotificationCenter() {
|
||||
NotificationCenter::with_global_default(|c| c.clear());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user