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 }