syncing with latst changes, because cvs will be moved
[wmaker-crm.git] / WINGs / notification.c
blobc70e98007c1741db3731717283c9ac47698d3ad3
2 #include <stdlib.h>
3 #include <assert.h>
4 #include <stdio.h>
5 #include <string.h>
7 #include "WUtil.h"
9 #include "llist.h"
11 typedef struct W_Notification {
12 char *name;
13 void *object;
14 void *clientData;
15 int refCount;
16 } Notification;
19 extern void W_FlushASAPNotificationQueue();
22 char*
23 WMGetNotificationName(WMNotification *notification)
25 return notification->name;
29 void*
30 WMGetNotificationObject(WMNotification *notification)
32 return notification->object;
36 void*
37 WMGetNotificationClientData(WMNotification *notification)
39 return notification->clientData;
43 WMNotification*
44 WMCreateNotification(char *name, void *object, void *clientData)
46 Notification *nPtr;
48 nPtr = wmalloc(sizeof(Notification));
50 nPtr->name = name;
51 nPtr->object = object;
52 nPtr->clientData = clientData;
54 nPtr->refCount = 1;
56 return nPtr;
60 void
61 WMReleaseNotification(WMNotification *notification)
63 notification->refCount--;
65 if (notification->refCount < 1) {
66 free(notification);
71 WMNotification*
72 WMRetainNotification(WMNotification *notification)
74 notification->refCount++;
76 return notification;
80 /***************** Notification Center *****************/
82 typedef struct NotificationObserver {
83 WMNotificationObserverAction *observerAction;
84 void *observer;
86 char *name;
87 void *object;
89 struct NotificationObserver *prev; /* for tables */
90 struct NotificationObserver *next;
91 struct NotificationObserver *nextAction; /* for observerTable */
92 } NotificationObserver;
95 typedef struct W_NotificationCenter {
96 WMHashTable *nameTable; /* names -> observer lists */
97 WMHashTable *objectTable; /* object -> observer lists */
98 NotificationObserver *nilList; /* obervers that catch everything */
100 WMHashTable *observerTable; /* observer -> NotificationObserver */
101 } NotificationCenter;
104 /* default (and only) center */
105 static NotificationCenter *notificationCenter = NULL;
108 void
109 W_InitNotificationCenter(void)
111 notificationCenter = wmalloc(sizeof(NotificationCenter));
113 notificationCenter->nameTable = WMCreateHashTable(WMStringPointerHashCallbacks);
114 notificationCenter->objectTable = WMCreateHashTable(WMIntHashCallbacks);
115 notificationCenter->nilList = NULL;
117 notificationCenter->observerTable = WMCreateHashTable(WMIntHashCallbacks);
121 void
122 WMAddNotificationObserver(WMNotificationObserverAction *observerAction,
123 void *observer, char *name, void *object)
125 NotificationObserver *oRec, *rec;
127 oRec = wmalloc(sizeof(NotificationObserver));
128 oRec->observerAction = observerAction;
129 oRec->observer = observer;
130 oRec->name = name;
131 oRec->object = object;
132 oRec->next = NULL;
133 oRec->prev = NULL;
136 /* put this action in the list of actions for this observer */
137 rec = WMHashInsert(notificationCenter->observerTable, observer, oRec);
139 if (rec) {
140 /* if this is not the first action for the observer */
141 oRec->nextAction = rec;
142 } else {
143 oRec->nextAction = NULL;
146 if (!name && !object) {
147 /* catch-all */
148 oRec->next = notificationCenter->nilList;
149 if (notificationCenter->nilList) {
150 notificationCenter->nilList->prev = oRec;
152 notificationCenter->nilList = oRec;
153 } else if (!name) {
154 /* any message coming from object */
155 rec = WMHashInsert(notificationCenter->objectTable, object, oRec);
156 oRec->next = rec;
157 if (rec) {
158 rec->prev = oRec;
160 } else {
161 /* name && (object || !object) */
162 rec = WMHashInsert(notificationCenter->nameTable, name, oRec);
163 oRec->next = rec;
164 if (rec) {
165 rec->prev = oRec;
171 void
172 WMPostNotification(WMNotification *notification)
174 NotificationObserver *orec, *tmp;
176 WMRetainNotification(notification);
178 /* tell the observers that want to know about a particular message */
179 orec = WMHashGet(notificationCenter->nameTable, notification->name);
181 while (orec) {
182 tmp = orec->next;
184 if (!orec->object || orec->object == notification->object) {
185 /* tell the observer */
186 if (orec->observerAction) {
187 (*orec->observerAction)(orec->observer, notification);
191 orec = tmp;
194 /* tell the observers that want to know about an object */
195 orec = WMHashGet(notificationCenter->objectTable, notification->object);
197 while (orec) {
198 tmp = orec->next;
200 /* tell the observer */
201 if (orec->observerAction) {
202 (*orec->observerAction)(orec->observer, notification);
204 orec = tmp;
207 /* tell the catch all observers */
208 orec = notificationCenter->nilList;
209 while (orec) {
210 tmp = orec->next;
212 /* tell the observer */
213 if (orec->observerAction) {
214 (*orec->observerAction)(orec->observer, notification);
216 orec = tmp;
219 WMReleaseNotification(notification);
221 W_FlushASAPNotificationQueue();
225 void
226 WMRemoveNotificationObserver(void *observer)
228 NotificationObserver *orec, *tmp, *rec;
230 /* get the list of actions the observer is doing */
231 orec = WMHashGet(notificationCenter->observerTable, observer);
234 * FOREACH orec IN actionlist for observer
235 * DO
236 * remove from respective lists/tables
237 * free
238 * END
240 while (orec) {
241 tmp = orec->nextAction;
243 if (!orec->name && !orec->object) {
244 /* catch-all */
245 if (notificationCenter->nilList==orec)
246 notificationCenter->nilList = orec->next;
247 } else if (!orec->name) {
248 /* any message coming from object */
249 rec = WMHashGet(notificationCenter->objectTable, orec->object);
250 if (rec==orec) {
251 /* replace table entry */
252 if (orec->next) {
253 WMHashInsert(notificationCenter->objectTable, orec->object,
254 orec->next);
255 } else {
256 WMHashRemove(notificationCenter->objectTable, orec->object);
259 } else {
260 /* name && (object || !object) */
261 rec = WMHashGet(notificationCenter->nameTable, orec->name);
262 if (rec==orec) {
263 /* replace table entry */
264 if (orec->next) {
265 WMHashInsert(notificationCenter->nameTable, orec->name,
266 orec->next);
267 } else {
268 WMHashRemove(notificationCenter->nameTable, orec->name);
272 if (orec->prev)
273 orec->prev->next = orec->next;
274 if (orec->next)
275 orec->next->prev = orec->prev;
277 free(orec);
279 orec = tmp;
282 WMHashRemove(notificationCenter->observerTable, observer);
286 void
287 WMRemoveNotificationObserverWithName(void *observer, char *name, void *object)
289 NotificationObserver *orec, *tmp, *rec;
290 NotificationObserver *newList = NULL;
292 /* get the list of actions the observer is doing */
293 orec = WMHashGet(notificationCenter->observerTable, observer);
295 WMHashRemove(notificationCenter->observerTable, observer);
297 /* rebuild the list of actions for the observer */
299 while (orec) {
300 tmp = orec->nextAction;
301 if (orec->name == name && orec->object == object) {
302 if (!name && !object) {
303 if (notificationCenter->nilList == orec)
304 notificationCenter->nilList = orec->next;
305 } else if (!name) {
306 rec = WMHashGet(notificationCenter->objectTable, orec->object);
307 if (rec==orec) {
308 assert(rec->prev==NULL);
309 /* replace table entry */
310 if (orec->next) {
311 WMHashInsert(notificationCenter->objectTable,
312 orec->object, orec->next);
313 } else {
314 WMHashRemove(notificationCenter->objectTable,
315 orec->object);
318 } else {
319 rec = WMHashGet(notificationCenter->nameTable, orec->name);
320 if (rec==orec) {
321 assert(rec->prev==NULL);
322 /* replace table entry */
323 if (orec->next) {
324 WMHashInsert(notificationCenter->nameTable,
325 orec->name, orec->next);
326 } else {
327 WMHashRemove(notificationCenter->nameTable,
328 orec->name);
333 if (orec->prev)
334 orec->prev->next = orec->next;
335 if (orec->next)
336 orec->next->prev = orec->prev;
337 free(orec);
338 } else {
339 /* append this action in the new action list */
340 orec->nextAction = NULL;
341 if (!newList) {
342 newList = orec;
343 } else {
344 NotificationObserver *p;
346 p = newList;
347 while (p->nextAction) {
348 p = p->nextAction;
350 p->nextAction = orec;
353 orec = tmp;
356 /* reinsert the list to the table */
357 if (newList) {
358 WMHashInsert(notificationCenter->observerTable, observer, newList);
363 void
364 WMPostNotificationName(char *name, void *object, void *clientData)
366 WMNotification *notification;
368 notification = WMCreateNotification(name, object, clientData);
370 WMPostNotification(notification);
372 WMReleaseNotification(notification);
377 /**************** Notification Queues ****************/
380 typedef struct W_NotificationQueue {
381 list_t *asapQueue;
382 list_t *idleQueue;
384 struct W_NotificationQueue *next;
385 } NotificationQueue;
388 static WMNotificationQueue *notificationQueueList = NULL;
390 /* default queue */
391 static WMNotificationQueue *notificationQueue = NULL;
394 WMNotificationQueue*
395 WMGetDefaultNotificationQueue(void)
397 if (!notificationQueue)
398 notificationQueue = WMCreateNotificationQueue();
400 return notificationQueue;
404 WMNotificationQueue*
405 WMCreateNotificationQueue(void)
407 NotificationQueue *queue;
409 queue = wmalloc(sizeof(NotificationQueue));
411 queue->asapQueue = NULL;
412 queue->idleQueue = NULL;
413 queue->next = notificationQueueList;
415 notificationQueueList = queue;
417 return queue;
422 void
423 WMEnqueueNotification(WMNotificationQueue *queue, WMNotification *notification,
424 WMPostingStyle postingStyle)
426 WMEnqueueCoalesceNotification(queue, notification, postingStyle,
427 WNCOnName|WNCOnSender);
431 static int
432 matchName(void *a, void *b)
434 WMNotification *n1 = (WMNotification*)a;
435 WMNotification *n2 = (WMNotification*)b;
437 return strcmp(n1->name, n2->name);
441 static int
442 matchSender(void *a, void *b)
444 WMNotification *n1 = (WMNotification*)a;
445 WMNotification *n2 = (WMNotification*)b;
447 return (n1->object == n2->object);
451 void
452 WMDequeueNotificationMatching(WMNotificationQueue *queue,
453 WMNotification *notification, unsigned mask)
455 void *n;
457 if (mask & WNCOnName) {
458 while ((n = lfind(notification->name, queue->asapQueue, matchName))) {
459 queue->asapQueue = lremove(queue->asapQueue, n);
460 WMReleaseNotification((WMNotification*)n);
462 while ((n = lfind(notification->name, queue->idleQueue, matchName))) {
463 queue->idleQueue = lremove(queue->idleQueue, n);
464 WMReleaseNotification((WMNotification*)n);
467 if (mask & WNCOnSender) {
468 while ((n = lfind(notification->name, queue->asapQueue, matchSender))) {
469 queue->asapQueue = lremove(queue->asapQueue, n);
470 WMReleaseNotification((WMNotification*)n);
472 while ((n = lfind(notification->name, queue->idleQueue, matchSender))) {
473 queue->idleQueue = lremove(queue->idleQueue, n);
474 WMReleaseNotification((WMNotification*)n);
480 void
481 WMEnqueueCoalesceNotification(WMNotificationQueue *queue,
482 WMNotification *notification,
483 WMPostingStyle postingStyle,
484 unsigned coalesceMask)
486 if (coalesceMask != WNCNone)
487 WMDequeueNotificationMatching(queue, notification, coalesceMask);
489 switch (postingStyle) {
490 case WMPostNow:
491 WMPostNotification(notification);
492 break;
494 case WMPostASAP:
495 queue->asapQueue = lappend(queue->asapQueue,
496 lcons(notification, NULL));
497 break;
499 case WMPostWhenIdle:
500 queue->idleQueue = lappend(queue->idleQueue,
501 lcons(notification, NULL));
502 break;
507 void
508 W_FlushASAPNotificationQueue()
510 WMNotificationQueue *queue = notificationQueueList;
512 while (queue) {
513 while (queue->asapQueue) {
514 WMPostNotification((WMNotification*)lhead(queue->asapQueue));
515 queue->asapQueue = lremovehead(queue->asapQueue);
521 void
522 W_FlushIdleNotificationQueue()
524 WMNotificationQueue *queue = notificationQueueList;
526 while (queue) {
527 while (queue->idleQueue) {
528 WMPostNotification((WMNotification*)lhead(queue->idleQueue));
529 queue->idleQueue = lremovehead(queue->idleQueue);