Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / notification.c
1
2 #include <stdlib.h>
3 #include <assert.h>
4 #include <stdio.h>
5 #include <string.h>
6
7 #include "WUtil.h"
8
9 typedef struct W_Notification {
10         const char *name;
11         void *object;
12         void *clientData;
13         int refCount;
14 } Notification;
15
16 extern void W_FlushASAPNotificationQueue();
17
18 const char *WMGetNotificationName(WMNotification * notification)
19 {
20         return notification->name;
21 }
22
23 void *WMGetNotificationObject(WMNotification * notification)
24 {
25         return notification->object;
26 }
27
28 void *WMGetNotificationClientData(WMNotification * notification)
29 {
30         return notification->clientData;
31 }
32
33 WMNotification *WMCreateNotification(const char *name, void *object, void *clientData)
34 {
35         Notification *nPtr;
36
37         nPtr = wmalloc(sizeof(Notification));
38
39         nPtr->name = name;
40         nPtr->object = object;
41         nPtr->clientData = clientData;
42
43         nPtr->refCount = 1;
44
45         return nPtr;
46 }
47
48 void WMReleaseNotification(WMNotification * notification)
49 {
50         notification->refCount--;
51
52         if (notification->refCount < 1) {
53                 wfree(notification);
54         }
55 }
56
57 WMNotification *WMRetainNotification(WMNotification * notification)
58 {
59         notification->refCount++;
60
61         return notification;
62 }
63
64 /***************** Notification Center *****************/
65
66 typedef struct NotificationObserver {
67         WMNotificationObserverAction *observerAction;
68         void *observer;
69
70         const char *name;
71         void *object;
72
73         struct NotificationObserver *prev;      /* for tables */
74         struct NotificationObserver *next;
75         struct NotificationObserver *nextAction;        /* for observerTable */
76 } NotificationObserver;
77
78 typedef struct W_NotificationCenter {
79         WMHashTable *nameTable; /* names -> observer lists */
80         WMHashTable *objectTable;       /* object -> observer lists */
81         NotificationObserver *nilList;  /* obervers that catch everything */
82
83         WMHashTable *observerTable;     /* observer -> NotificationObserver */
84 } NotificationCenter;
85
86 /* default (and only) center */
87 static NotificationCenter *notificationCenter = NULL;
88
89 void W_InitNotificationCenter(void)
90 {
91         notificationCenter = wmalloc(sizeof(NotificationCenter));
92
93         notificationCenter->nameTable = WMCreateHashTable(WMStringPointerHashCallbacks);
94         notificationCenter->objectTable = WMCreateHashTable(WMIntHashCallbacks);
95         notificationCenter->nilList = NULL;
96
97         notificationCenter->observerTable = WMCreateHashTable(WMIntHashCallbacks);
98 }
99
100 void
101 WMAddNotificationObserver(WMNotificationObserverAction * observerAction,
102                           void *observer, const char *name, void *object)
103 {
104         NotificationObserver *oRec, *rec;
105
106         oRec = wmalloc(sizeof(NotificationObserver));
107         oRec->observerAction = observerAction;
108         oRec->observer = observer;
109         oRec->name = name;
110         oRec->object = object;
111         oRec->next = NULL;
112         oRec->prev = NULL;
113
114         /* put this action in the list of actions for this observer */
115         rec = (NotificationObserver *) WMHashInsert(notificationCenter->observerTable, observer, oRec);
116
117         if (rec) {
118                 /* if this is not the first action for the observer */
119                 oRec->nextAction = rec;
120         } else {
121                 oRec->nextAction = NULL;
122         }
123
124         if (!name && !object) {
125                 /* catch-all */
126                 oRec->next = notificationCenter->nilList;
127                 if (notificationCenter->nilList) {
128                         notificationCenter->nilList->prev = oRec;
129                 }
130                 notificationCenter->nilList = oRec;
131         } else if (!name) {
132                 /* any message coming from object */
133                 rec = (NotificationObserver *) WMHashInsert(notificationCenter->objectTable, object, oRec);
134                 oRec->next = rec;
135                 if (rec) {
136                         rec->prev = oRec;
137                 }
138         } else {
139                 /* name && (object || !object) */
140                 rec = (NotificationObserver *) WMHashInsert(notificationCenter->nameTable, name, oRec);
141                 oRec->next = rec;
142                 if (rec) {
143                         rec->prev = oRec;
144                 }
145         }
146 }
147
148 void WMPostNotification(WMNotification * notification)
149 {
150         NotificationObserver *orec, *tmp;
151
152         WMRetainNotification(notification);
153
154         /* tell the observers that want to know about a particular message */
155         orec = (NotificationObserver *) WMHashGet(notificationCenter->nameTable, notification->name);
156
157         while (orec) {
158                 tmp = orec->next;
159
160                 if (!orec->object || !notification->object || orec->object == notification->object) {
161                         /* tell the observer */
162                         if (orec->observerAction) {
163                                 (*orec->observerAction) (orec->observer, notification);
164                         }
165                 }
166
167                 orec = tmp;
168         }
169
170         /* tell the observers that want to know about an object */
171         orec = (NotificationObserver *) WMHashGet(notificationCenter->objectTable, notification->object);
172
173         while (orec) {
174                 tmp = orec->next;
175
176                 /* tell the observer */
177                 if (orec->observerAction) {
178                         (*orec->observerAction) (orec->observer, notification);
179                 }
180                 orec = tmp;
181         }
182
183         /* tell the catch all observers */
184         orec = notificationCenter->nilList;
185         while (orec) {
186                 tmp = orec->next;
187
188                 /* tell the observer */
189                 if (orec->observerAction) {
190                         (*orec->observerAction) (orec->observer, notification);
191                 }
192                 orec = tmp;
193         }
194
195         WMReleaseNotification(notification);
196 }
197
198 void WMRemoveNotificationObserver(void *observer)
199 {
200         NotificationObserver *orec, *tmp, *rec;
201
202         /* get the list of actions the observer is doing */
203         orec = (NotificationObserver *) WMHashGet(notificationCenter->observerTable, observer);
204
205         /*
206          * FOREACH orec IN actionlist for observer
207          * DO
208          *   remove from respective lists/tables
209          *   free
210          * END
211          */
212         while (orec) {
213                 tmp = orec->nextAction;
214
215                 if (!orec->name && !orec->object) {
216                         /* catch-all */
217                         if (notificationCenter->nilList == orec)
218                                 notificationCenter->nilList = orec->next;
219                 } else if (!orec->name) {
220                         /* any message coming from object */
221                         rec = (NotificationObserver *) WMHashGet(notificationCenter->objectTable, orec->object);
222                         if (rec == orec) {
223                                 /* replace table entry */
224                                 if (orec->next) {
225                                         WMHashInsert(notificationCenter->objectTable, orec->object, orec->next);
226                                 } else {
227                                         WMHashRemove(notificationCenter->objectTable, orec->object);
228                                 }
229                         }
230                 } else {
231                         /* name && (object || !object) */
232                         rec = (NotificationObserver *) WMHashGet(notificationCenter->nameTable, orec->name);
233                         if (rec == orec) {
234                                 /* replace table entry */
235                                 if (orec->next) {
236                                         WMHashInsert(notificationCenter->nameTable, orec->name, orec->next);
237                                 } else {
238                                         WMHashRemove(notificationCenter->nameTable, orec->name);
239                                 }
240                         }
241                 }
242                 if (orec->prev)
243                         orec->prev->next = orec->next;
244                 if (orec->next)
245                         orec->next->prev = orec->prev;
246
247                 wfree(orec);
248
249                 orec = tmp;
250         }
251
252         WMHashRemove(notificationCenter->observerTable, observer);
253 }
254
255 void WMRemoveNotificationObserverWithName(void *observer, const char *name, void *object)
256 {
257         NotificationObserver *orec, *tmp, *rec;
258         NotificationObserver *newList = NULL;
259
260         /* get the list of actions the observer is doing */
261         orec = (NotificationObserver *) WMHashGet(notificationCenter->observerTable, observer);
262
263         WMHashRemove(notificationCenter->observerTable, observer);
264
265         /* rebuild the list of actions for the observer */
266
267         while (orec) {
268                 tmp = orec->nextAction;
269                 if (orec->name == name && orec->object == object) {
270                         if (!name && !object) {
271                                 if (notificationCenter->nilList == orec)
272                                         notificationCenter->nilList = orec->next;
273                         } else if (!name) {
274                                 rec =
275                                     (NotificationObserver *) WMHashGet(notificationCenter->objectTable,
276                                                                        orec->object);
277                                 if (rec == orec) {
278                                         assert(rec->prev == NULL);
279                                         /* replace table entry */
280                                         if (orec->next) {
281                                                 WMHashInsert(notificationCenter->objectTable,
282                                                              orec->object, orec->next);
283                                         } else {
284                                                 WMHashRemove(notificationCenter->objectTable, orec->object);
285                                         }
286                                 }
287                         } else {
288                                 rec = (NotificationObserver *) WMHashGet(notificationCenter->nameTable,
289                                                                          orec->name);
290                                 if (rec == orec) {
291                                         assert(rec->prev == NULL);
292                                         /* replace table entry */
293                                         if (orec->next) {
294                                                 WMHashInsert(notificationCenter->nameTable,
295                                                              orec->name, orec->next);
296                                         } else {
297                                                 WMHashRemove(notificationCenter->nameTable, orec->name);
298                                         }
299                                 }
300                         }
301
302                         if (orec->prev)
303                                 orec->prev->next = orec->next;
304                         if (orec->next)
305                                 orec->next->prev = orec->prev;
306                         wfree(orec);
307                 } else {
308                         /* append this action in the new action list */
309                         orec->nextAction = NULL;
310                         if (!newList) {
311                                 newList = orec;
312                         } else {
313                                 NotificationObserver *p;
314
315                                 p = newList;
316                                 while (p->nextAction) {
317                                         p = p->nextAction;
318                                 }
319                                 p->nextAction = orec;
320                         }
321                 }
322                 orec = tmp;
323         }
324
325         /* reinsert the list to the table */
326         if (newList) {
327                 WMHashInsert(notificationCenter->observerTable, observer, newList);
328         }
329 }
330
331 void WMPostNotificationName(const char *name, void *object, void *clientData)
332 {
333         WMNotification *notification;
334
335         notification = WMCreateNotification(name, object, clientData);
336
337         WMPostNotification(notification);
338
339         WMReleaseNotification(notification);
340 }
341
342 /**************** Notification Queues ****************/
343
344 typedef struct W_NotificationQueue {
345         WMArray *asapQueue;
346         WMArray *idleQueue;
347
348         struct W_NotificationQueue *next;
349 } NotificationQueue;
350
351 static WMNotificationQueue *notificationQueueList = NULL;
352
353 /* default queue */
354 static WMNotificationQueue *notificationQueue = NULL;
355
356 WMNotificationQueue *WMGetDefaultNotificationQueue(void)
357 {
358         if (!notificationQueue)
359                 notificationQueue = WMCreateNotificationQueue();
360
361         return notificationQueue;
362 }
363
364 WMNotificationQueue *WMCreateNotificationQueue(void)
365 {
366         NotificationQueue *queue;
367
368         queue = wmalloc(sizeof(NotificationQueue));
369
370         queue->asapQueue = WMCreateArrayWithDestructor(8, (WMFreeDataProc *) WMReleaseNotification);
371         queue->idleQueue = WMCreateArrayWithDestructor(8, (WMFreeDataProc *) WMReleaseNotification);
372         queue->next = notificationQueueList;
373
374         notificationQueueList = queue;
375
376         return queue;
377 }
378
379 void WMEnqueueNotification(WMNotificationQueue * queue, WMNotification * notification, WMPostingStyle postingStyle)
380 {
381         WMEnqueueCoalesceNotification(queue, notification, postingStyle, WNCOnName | WNCOnSender);
382 }
383
384 #define NOTIF ((WMNotification*)cdata)
385 #define ITEM  ((WMNotification*)item)
386
387 static int matchSenderAndName(void *item, void *cdata)
388 {
389         return (NOTIF->object == ITEM->object && strcmp(NOTIF->name, ITEM->name) == 0);
390 }
391
392 static int matchSender(void *item, void *cdata)
393 {
394         return (NOTIF->object == ITEM->object);
395 }
396
397 static int matchName(void *item, void *cdata)
398 {
399         return (strcmp(NOTIF->name, ITEM->name) == 0);
400 }
401
402 #undef NOTIF
403 #undef ITEM
404
405 void WMDequeueNotificationMatching(WMNotificationQueue * queue, WMNotification * notification, unsigned mask)
406 {
407         WMMatchDataProc *matchFunc;
408
409         if ((mask & WNCOnName) && (mask & WNCOnSender))
410                 matchFunc = matchSenderAndName;
411         else if (mask & WNCOnName)
412                 matchFunc = matchName;
413         else if (mask & WNCOnSender)
414                 matchFunc = matchSender;
415         else
416                 return;
417
418         WMRemoveFromArrayMatching(queue->asapQueue, matchFunc, notification);
419         WMRemoveFromArrayMatching(queue->idleQueue, matchFunc, notification);
420 }
421
422 void
423 WMEnqueueCoalesceNotification(WMNotificationQueue * queue,
424                               WMNotification * notification, WMPostingStyle postingStyle, unsigned coalesceMask)
425 {
426         if (coalesceMask != WNCNone)
427                 WMDequeueNotificationMatching(queue, notification, coalesceMask);
428
429         switch (postingStyle) {
430         case WMPostNow:
431                 WMPostNotification(notification);
432                 WMReleaseNotification(notification);
433                 break;
434
435         case WMPostASAP:
436                 WMAddToArray(queue->asapQueue, notification);
437                 break;
438
439         case WMPostWhenIdle:
440                 WMAddToArray(queue->idleQueue, notification);
441                 break;
442         }
443 }
444
445 void W_FlushASAPNotificationQueue()
446 {
447         WMNotificationQueue *queue = notificationQueueList;
448
449         while (queue) {
450                 while (WMGetArrayItemCount(queue->asapQueue)) {
451                         WMPostNotification(WMGetFromArray(queue->asapQueue, 0));
452                         WMDeleteFromArray(queue->asapQueue, 0);
453                 }
454
455                 queue = queue->next;
456         }
457 }
458
459 void W_FlushIdleNotificationQueue()
460 {
461         WMNotificationQueue *queue = notificationQueueList;
462
463         while (queue) {
464                 while (WMGetArrayItemCount(queue->idleQueue)) {
465                         WMPostNotification(WMGetFromArray(queue->idleQueue, 0));
466                         WMDeleteFromArray(queue->idleQueue, 0);
467                 }
468
469                 queue = queue->next;
470         }
471 }