#include #include #include #include #include "WUtil.h" typedef struct W_Notification { const char *name; void *object; void *clientData; int refCount; } Notification; extern void W_FlushASAPNotificationQueue(); 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); } } 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 = WMCreateHashTable(WMStringPointerHashCallbacks); notificationCenter->objectTable = WMCreateHashTable(WMIntHashCallbacks); notificationCenter->nilList = NULL; notificationCenter->observerTable = WMCreateHashTable(WMIntHashCallbacks); } 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 WMRemoveNotificationObserverWithName(void *observer, const char *name, void *object) { NotificationObserver *orec, *tmp, *rec; NotificationObserver *newList = NULL; /* get the list of actions the observer is doing */ orec = (NotificationObserver*)WMHashGet(notificationCenter->observerTable, observer); WMHashRemove(notificationCenter->observerTable, observer); /* rebuild the list of actions for the observer */ while (orec) { tmp = orec->nextAction; if (orec->name == name && orec->object == object) { if (!name && !object) { if (notificationCenter->nilList == orec) notificationCenter->nilList = orec->next; } else if (!name) { rec = (NotificationObserver*)WMHashGet(notificationCenter->objectTable, orec->object); if (rec==orec) { assert(rec->prev==NULL); /* replace table entry */ if (orec->next) { WMHashInsert(notificationCenter->objectTable, orec->object, orec->next); } else { WMHashRemove(notificationCenter->objectTable, orec->object); } } } else { rec = (NotificationObserver*)WMHashGet(notificationCenter->nameTable, orec->name); if (rec==orec) { assert(rec->prev==NULL); /* 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); } else { /* append this action in the new action list */ orec->nextAction = NULL; if (!newList) { newList = orec; } else { NotificationObserver *p; p = newList; while (p->nextAction) { p = p->nextAction; } p->nextAction = orec; } } orec = tmp; } /* reinsert the list to the table */ if (newList) { WMHashInsert(notificationCenter->observerTable, observer, newList); } } void WMPostNotificationName(const char *name, void *object, void *clientData) { WMNotification *notification; notification = WMCreateNotification(name, object, clientData); WMPostNotification(notification); WMReleaseNotification(notification); } /**************** Notification Queues ****************/ typedef struct W_NotificationQueue { WMBag *asapQueue; WMBag *idleQueue; struct W_NotificationQueue *next; } NotificationQueue; static WMNotificationQueue *notificationQueueList = NULL; /* default queue */ static WMNotificationQueue *notificationQueue = NULL; WMNotificationQueue* WMGetDefaultNotificationQueue(void) { if (!notificationQueue) notificationQueue = WMCreateNotificationQueue(); return notificationQueue; } WMNotificationQueue* WMCreateNotificationQueue(void) { NotificationQueue *queue; queue = wmalloc(sizeof(NotificationQueue)); queue->asapQueue = WMCreateBag(8); queue->idleQueue = WMCreateBag(8); queue->next = notificationQueueList; notificationQueueList = queue; return queue; } void WMEnqueueNotification(WMNotificationQueue *queue, WMNotification *notification, WMPostingStyle postingStyle) { WMEnqueueCoalesceNotification(queue, notification, postingStyle, WNCOnName|WNCOnSender); } void WMDequeueNotificationMatching(WMNotificationQueue *queue, WMNotification *notification, unsigned mask) { WMBagIterator i; WMNotification *tmp; if ((mask & WNCOnName) && (mask & WNCOnSender)) { WM_ITERATE_BAG(queue->asapQueue, tmp, i) { if (notification->object == tmp->object && strcmp(notification->name, tmp->name) == 0) { WMRemoveFromBag(queue->asapQueue, tmp); WMReleaseNotification(tmp); break; } } WM_ITERATE_BAG(queue->idleQueue, tmp, i) { if (notification->object == tmp->object && strcmp(notification->name, tmp->name) == 0) { WMRemoveFromBag(queue->idleQueue, tmp); WMReleaseNotification(tmp); break; } } } else if (mask & WNCOnName) { WM_ITERATE_BAG(queue->asapQueue, tmp, i) { if (strcmp(notification->name, tmp->name) == 0) { WMRemoveFromBag(queue->asapQueue, tmp); WMReleaseNotification(tmp); break; } } WM_ITERATE_BAG(queue->idleQueue, tmp, i) { if (strcmp(notification->name, tmp->name) == 0) { WMRemoveFromBag(queue->idleQueue, tmp); WMReleaseNotification(tmp); break; } } } else if (mask & WNCOnSender) { WM_ITERATE_BAG(queue->asapQueue, tmp, i) { if (notification->object == tmp->object) { WMRemoveFromBag(queue->asapQueue, tmp); WMReleaseNotification(tmp); break; } } WM_ITERATE_BAG(queue->idleQueue, tmp, i) { if (notification->object == tmp->object) { WMRemoveFromBag(queue->idleQueue, tmp); WMReleaseNotification(tmp); break; } } } } void WMEnqueueCoalesceNotification(WMNotificationQueue *queue, WMNotification *notification, WMPostingStyle postingStyle, unsigned coalesceMask) { if (coalesceMask != WNCNone) WMDequeueNotificationMatching(queue, notification, coalesceMask); switch (postingStyle) { case WMPostNow: WMPostNotification(notification); WMReleaseNotification(notification); break; case WMPostASAP: WMPutInBag(queue->asapQueue, notification); break; case WMPostWhenIdle: WMPutInBag(queue->idleQueue, notification); break; } } void W_FlushASAPNotificationQueue() { WMNotificationQueue *queue = notificationQueueList; while (queue) { while (WMGetBagItemCount(queue->asapQueue)) { WMNotification *tmp = WMGetFromBag(queue->asapQueue, 0); WMPostNotification(tmp); WMReleaseNotification(tmp); WMDeleteFromBag(queue->asapQueue, 0); } queue = queue->next; } } void W_FlushIdleNotificationQueue() { WMNotificationQueue *queue = notificationQueueList; while (queue) { while (WMGetBagItemCount(queue->idleQueue)) { WMNotification *tmp = WMGetFromBag(queue->idleQueue, 0); WMPostNotification(tmp); WMReleaseNotification(tmp); WMDeleteFromBag(queue->idleQueue, 0); } queue = queue->next; } }