mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-19 12:28:22 +01:00
551 lines
12 KiB
C
551 lines
12 KiB
C
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "WUtil.h"
|
|
|
|
|
|
typedef struct W_Notification {
|
|
char *name;
|
|
void *object;
|
|
void *clientData;
|
|
int refCount;
|
|
} Notification;
|
|
|
|
|
|
extern void W_FlushASAPNotificationQueue();
|
|
|
|
|
|
char*
|
|
WMGetNotificationName(WMNotification *notification)
|
|
{
|
|
return notification->name;
|
|
}
|
|
|
|
|
|
void*
|
|
WMGetNotificationObject(WMNotification *notification)
|
|
{
|
|
return notification->object;
|
|
}
|
|
|
|
|
|
void*
|
|
WMGetNotificationClientData(WMNotification *notification)
|
|
{
|
|
return notification->clientData;
|
|
}
|
|
|
|
|
|
WMNotification*
|
|
WMCreateNotification(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;
|
|
|
|
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, 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 = 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 = WMHashInsert(notificationCenter->objectTable, object, oRec);
|
|
oRec->next = rec;
|
|
if (rec) {
|
|
rec->prev = oRec;
|
|
}
|
|
} else {
|
|
/* name && (object || !object) */
|
|
rec = 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 = 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 = 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 = 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 = 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 = 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, char *name, void *object)
|
|
{
|
|
NotificationObserver *orec, *tmp, *rec;
|
|
NotificationObserver *newList = NULL;
|
|
|
|
/* get the list of actions the observer is doing */
|
|
orec = 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 = 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 = 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(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;
|
|
}
|
|
}
|
|
|