Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / handlers.c
1
2 /*
3  * WINGs internal handlers: timer, idle and input handlers
4  */
5
6 #include "wconfig.h"
7 #include "WINGsP.h"
8
9 #include <sys/types.h>
10 #include <unistd.h>
11
12 #include <X11/Xos.h>
13
14 #ifdef HAVE_SYS_SELECT_H
15 # include <sys/select.h>
16 #endif
17
18 #include <time.h>
19
20 #ifndef X_GETTIMEOFDAY
21 #define X_GETTIMEOFDAY(t) gettimeofday(t, (struct timezone*)0)
22 #endif
23
24 typedef struct TimerHandler {
25         WMCallback *callback;   /* procedure to call */
26         struct timeval when;    /* when to call the callback */
27         void *clientData;
28         struct TimerHandler *next;
29         int nextDelay;          /* 0 if it's one-shot */
30 } TimerHandler;
31
32 typedef struct IdleHandler {
33         WMCallback *callback;
34         void *clientData;
35 } IdleHandler;
36
37 typedef struct InputHandler {
38         WMInputProc *callback;
39         void *clientData;
40         int fd;
41         int mask;
42 } InputHandler;
43
44 /* queue of timer event handlers */
45 static TimerHandler *timerHandler = NULL;
46
47 static WMArray *idleHandler = NULL;
48
49 static WMArray *inputHandler = NULL;
50
51 #define timerPending()  (timerHandler)
52
53 static void rightNow(struct timeval *tv)
54 {
55         X_GETTIMEOFDAY(tv);
56 }
57
58 /* is t1 after t2 ? */
59 #define IS_AFTER(t1, t2)        (((t1).tv_sec > (t2).tv_sec) || \
60     (((t1).tv_sec == (t2).tv_sec) \
61     && ((t1).tv_usec > (t2).tv_usec)))
62
63 #define IS_ZERO(tv) (tv.tv_sec == 0 && tv.tv_usec == 0)
64
65 #define SET_ZERO(tv) tv.tv_sec = 0, tv.tv_usec = 0
66
67 static void addmillisecs(struct timeval *tv, int milliseconds)
68 {
69         tv->tv_usec += milliseconds * 1000;
70
71         tv->tv_sec += tv->tv_usec / 1000000;
72         tv->tv_usec = tv->tv_usec % 1000000;
73 }
74
75 static void enqueueTimerHandler(TimerHandler * handler)
76 {
77         TimerHandler *tmp;
78
79         /* insert callback in queue, sorted by time left */
80         if (!timerHandler || !IS_AFTER(handler->when, timerHandler->when)) {
81                 /* first in the queue */
82                 handler->next = timerHandler;
83                 timerHandler = handler;
84         } else {
85                 tmp = timerHandler;
86                 while (tmp->next && IS_AFTER(handler->when, tmp->next->when)) {
87                         tmp = tmp->next;
88                 }
89                 handler->next = tmp->next;
90                 tmp->next = handler;
91         }
92 }
93
94 static void delayUntilNextTimerEvent(struct timeval *delay)
95 {
96         struct timeval now;
97         TimerHandler *handler;
98
99         handler = timerHandler;
100         while (handler && IS_ZERO(handler->when))
101                 handler = handler->next;
102
103         if (!handler) {
104                 /* The return value of this function is only valid if there _are_
105                    timers active. */
106                 delay->tv_sec = 0;
107                 delay->tv_usec = 0;
108                 return;
109         }
110
111         rightNow(&now);
112         if (IS_AFTER(now, handler->when)) {
113                 delay->tv_sec = 0;
114                 delay->tv_usec = 0;
115         } else {
116                 delay->tv_sec = handler->when.tv_sec - now.tv_sec;
117                 delay->tv_usec = handler->when.tv_usec - now.tv_usec;
118                 if (delay->tv_usec < 0) {
119                         delay->tv_usec += 1000000;
120                         delay->tv_sec--;
121                 }
122         }
123 }
124
125 WMHandlerID WMAddTimerHandler(int milliseconds, WMCallback * callback, void *cdata)
126 {
127         TimerHandler *handler;
128
129         handler = malloc(sizeof(TimerHandler));
130         if (!handler)
131                 return NULL;
132
133         rightNow(&handler->when);
134         addmillisecs(&handler->when, milliseconds);
135         handler->callback = callback;
136         handler->clientData = cdata;
137         handler->nextDelay = 0;
138
139         enqueueTimerHandler(handler);
140
141         return handler;
142 }
143
144 WMHandlerID WMAddPersistentTimerHandler(int milliseconds, WMCallback * callback, void *cdata)
145 {
146         TimerHandler *handler = WMAddTimerHandler(milliseconds, callback, cdata);
147
148         if (handler != NULL)
149                 handler->nextDelay = milliseconds;
150
151         return handler;
152 }
153
154 void WMDeleteTimerWithClientData(void *cdata)
155 {
156         TimerHandler *handler, *tmp;
157
158         if (!cdata || !timerHandler)
159                 return;
160
161         tmp = timerHandler;
162         if (tmp->clientData == cdata) {
163                 tmp->nextDelay = 0;
164                 if (!IS_ZERO(tmp->when)) {
165                         timerHandler = tmp->next;
166                         wfree(tmp);
167                 }
168         } else {
169                 while (tmp->next) {
170                         if (tmp->next->clientData == cdata) {
171                                 handler = tmp->next;
172                                 handler->nextDelay = 0;
173                                 if (IS_ZERO(handler->when))
174                                         break;
175                                 tmp->next = handler->next;
176                                 wfree(handler);
177                                 break;
178                         }
179                         tmp = tmp->next;
180                 }
181         }
182 }
183
184 void WMDeleteTimerHandler(WMHandlerID handlerID)
185 {
186         TimerHandler *tmp, *handler = (TimerHandler *) handlerID;
187
188         if (!handler || !timerHandler)
189                 return;
190
191         tmp = timerHandler;
192
193         handler->nextDelay = 0;
194
195         if (IS_ZERO(handler->when))
196                 return;
197
198         if (tmp == handler) {
199                 timerHandler = handler->next;
200                 wfree(handler);
201         } else {
202                 while (tmp->next) {
203                         if (tmp->next == handler) {
204                                 tmp->next = handler->next;
205                                 wfree(handler);
206                                 break;
207                         }
208                         tmp = tmp->next;
209                 }
210         }
211 }
212
213 WMHandlerID WMAddIdleHandler(WMCallback * callback, void *cdata)
214 {
215         IdleHandler *handler;
216
217         handler = malloc(sizeof(IdleHandler));
218         if (!handler)
219                 return NULL;
220
221         handler->callback = callback;
222         handler->clientData = cdata;
223         /* add handler at end of queue */
224         if (!idleHandler) {
225                 idleHandler = WMCreateArrayWithDestructor(16, wfree);
226         }
227         WMAddToArray(idleHandler, handler);
228
229         return handler;
230 }
231
232 void WMDeleteIdleHandler(WMHandlerID handlerID)
233 {
234         IdleHandler *handler = (IdleHandler *) handlerID;
235
236         if (!handler || !idleHandler)
237                 return;
238
239         WMRemoveFromArray(idleHandler, handler);
240 }
241
242 WMHandlerID WMAddInputHandler(int fd, int condition, WMInputProc * proc, void *clientData)
243 {
244         InputHandler *handler;
245
246         handler = wmalloc(sizeof(InputHandler));
247
248         handler->fd = fd;
249         handler->mask = condition;
250         handler->callback = proc;
251         handler->clientData = clientData;
252
253         if (!inputHandler)
254                 inputHandler = WMCreateArrayWithDestructor(16, wfree);
255         WMAddToArray(inputHandler, handler);
256
257         return handler;
258 }
259
260 void WMDeleteInputHandler(WMHandlerID handlerID)
261 {
262         InputHandler *handler = (InputHandler *) handlerID;
263
264         if (!handler || !inputHandler)
265                 return;
266
267         WMRemoveFromArray(inputHandler, handler);
268 }
269
270 Bool W_CheckIdleHandlers(void)
271 {
272         IdleHandler *handler;
273         WMArray *handlerCopy;
274         WMArrayIterator iter;
275
276         if (!idleHandler || WMGetArrayItemCount(idleHandler) == 0) {
277                 W_FlushIdleNotificationQueue();
278                 /* make sure an observer in queue didn't added an idle handler */
279                 return (idleHandler != NULL && WMGetArrayItemCount(idleHandler) > 0);
280         }
281
282         handlerCopy = WMDuplicateArray(idleHandler);
283
284         WM_ITERATE_ARRAY(handlerCopy, handler, iter) {
285                 /* check if the handler still exist or was removed by a callback */
286                 if (WMGetFirstInArray(idleHandler, handler) == WANotFound)
287                         continue;
288
289                 (*handler->callback) (handler->clientData);
290                 WMDeleteIdleHandler(handler);
291         }
292
293         WMFreeArray(handlerCopy);
294
295         W_FlushIdleNotificationQueue();
296
297         /* this is not necesarrily False, because one handler can re-add itself */
298         return (WMGetArrayItemCount(idleHandler) > 0);
299 }
300
301 void W_CheckTimerHandlers(void)
302 {
303         TimerHandler *handler;
304         struct timeval now;
305
306         if (!timerHandler) {
307                 W_FlushASAPNotificationQueue();
308                 return;
309         }
310
311         rightNow(&now);
312
313         handler = timerHandler;
314         while (handler && IS_AFTER(now, handler->when)) {
315                 if (!IS_ZERO(handler->when)) {
316                         SET_ZERO(handler->when);
317                         (*handler->callback) (handler->clientData);
318                 }
319                 handler = handler->next;
320         }
321
322         while (timerHandler && IS_ZERO(timerHandler->when)) {
323                 handler = timerHandler;
324                 timerHandler = timerHandler->next;
325
326                 if (handler->nextDelay > 0) {
327                         handler->when = now;
328                         addmillisecs(&handler->when, handler->nextDelay);
329                         enqueueTimerHandler(handler);
330                 } else {
331                         wfree(handler);
332                 }
333         }
334
335         W_FlushASAPNotificationQueue();
336 }
337
338 /*
339  * This functions will handle input events on all registered file descriptors.
340  * Input:
341  *    - waitForInput - True if we want the function to wait until an event
342  *                     appears on a file descriptor we watch, False if we
343  *                     want the function to immediately return if there is
344  *                     no data available on the file descriptors we watch.
345  *    - inputfd      - Extra input file descriptor to watch for input.
346  *                     This is only used when called from wevent.c to watch
347  *                     on ConnectionNumber(dpy) to avoid blocking of X events
348  *                     if we wait for input from other file handlers.
349  * Output:
350  *    if waitForInput is False, the function will return False if there are no
351  *                     input handlers registered, or if there is no data
352  *                     available on the registered ones, and will return True
353  *                     if there is at least one input handler that has data
354  *                     available.
355  *    if waitForInput is True, the function will return False if there are no
356  *                     input handlers registered, else it will block until an
357  *                     event appears on one of the file descriptors it watches
358  *                     and then it will return True.
359  *
360  * If the retured value is True, the input handlers for the corresponding file
361  * descriptors are also called.
362  *
363  * Parametersshould be passed like this:
364  * - from wevent.c:
365  *   waitForInput - apropriate value passed by the function who called us
366  *   inputfd = ConnectionNumber(dpy)
367  * - from wutil.c:
368  *   waitForInput - apropriate value passed by the function who called us
369  *   inputfd = -1
370  *
371  */
372 Bool W_HandleInputEvents(Bool waitForInput, int inputfd)
373 {
374 #if defined(HAVE_POLL) && defined(HAVE_POLL_H) && !defined(HAVE_SELECT)
375         struct poll fd *fds;
376         InputHandler *handler;
377         int count, timeout, nfds, i, extrafd;
378
379         extrafd = (inputfd < 0) ? 0 : 1;
380
381         if (inputHandler)
382                 nfds = WMGetArrayItemCount(inputHandler);
383         else
384                 nfds = 0;
385
386         if (!extrafd && nfds == 0) {
387                 W_FlushASAPNotificationQueue();
388                 return False;
389         }
390
391         fds = wmalloc((nfds + extrafd) * sizeof(struct pollfd));
392         if (extrafd) {
393                 /* put this to the end of array to avoid using ranges from 1 to nfds+1 */
394                 fds[nfds].fd = inputfd;
395                 fds[nfds].events = POLLIN;
396         }
397
398         /* use WM_ITERATE_ARRAY() here */
399         for (i = 0; i < nfds; i++) {
400                 handler = WMGetFromArray(inputHandler, i);
401                 fds[i].fd = handler->fd;
402                 fds[i].events = 0;
403                 if (handler->mask & WIReadMask)
404                         fds[i].events |= POLLIN;
405
406                 if (handler->mask & WIWriteMask)
407                         fds[i].events |= POLLOUT;
408
409 #if 0                           /* FIXME */
410                 if (handler->mask & WIExceptMask)
411                         FD_SET(handler->fd, &eset);
412 #endif
413         }
414
415         /*
416          * Setup the timeout to the estimated time until the
417          * next timer expires.
418          */
419         if (!waitForInput) {
420                 timeout = 0;
421         } else if (timerPending()) {
422                 struct timeval tv;
423                 delayUntilNextTimerEvent(&tv);
424                 timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
425         } else {
426                 timeout = -1;
427         }
428
429         count = poll(fds, nfds + extrafd, timeout);
430
431         if (count > 0 && nfds > 0) {
432                 WMArray *handlerCopy = WMDuplicateArray(inputHandler);
433                 int mask;
434
435                 /* use WM_ITERATE_ARRAY() here */
436                 for (i = 0; i < nfds; i++) {
437                         handler = WMGetFromArray(handlerCopy, i);
438                         /* check if the handler still exist or was removed by a callback */
439                         if (WMGetFirstInArray(inputHandler, handler) == WANotFound)
440                                 continue;
441
442                         mask = 0;
443
444                         if ((handler->mask & WIReadMask) &&
445                             (fds[i].revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)))
446                                 mask |= WIReadMask;
447
448                         if ((handler->mask & WIWriteMask) && (fds[i].revents & (POLLOUT | POLLWRBAND)))
449                                 mask |= WIWriteMask;
450
451                         if ((handler->mask & WIExceptMask) && (fds[i].revents & (POLLHUP | POLLNVAL | POLLERR)))
452                                 mask |= WIExceptMask;
453
454                         if (mask != 0 && handler->callback) {
455                                 (*handler->callback) (handler->fd, mask, handler->clientData);
456                         }
457                 }
458
459                 WMFreeArray(handlerCopy);
460         }
461
462         wfree(fds);
463
464         W_FlushASAPNotificationQueue();
465
466         return (count > 0);
467 #else
468 #ifdef HAVE_SELECT
469         struct timeval timeout;
470         struct timeval *timeoutPtr;
471         fd_set rset, wset, eset;
472         int maxfd, nfds, i;
473         int count;
474         InputHandler *handler;
475
476         if (inputHandler)
477                 nfds = WMGetArrayItemCount(inputHandler);
478         else
479                 nfds = 0;
480
481         if (inputfd < 0 && nfds == 0) {
482                 W_FlushASAPNotificationQueue();
483                 return False;
484         }
485
486         FD_ZERO(&rset);
487         FD_ZERO(&wset);
488         FD_ZERO(&eset);
489
490         if (inputfd < 0) {
491                 maxfd = 0;
492         } else {
493                 FD_SET(inputfd, &rset);
494                 maxfd = inputfd;
495         }
496
497         /* use WM_ITERATE_ARRAY() here */
498         for (i = 0; i < nfds; i++) {
499                 handler = WMGetFromArray(inputHandler, i);
500                 if (handler->mask & WIReadMask)
501                         FD_SET(handler->fd, &rset);
502
503                 if (handler->mask & WIWriteMask)
504                         FD_SET(handler->fd, &wset);
505
506                 if (handler->mask & WIExceptMask)
507                         FD_SET(handler->fd, &eset);
508
509                 if (maxfd < handler->fd)
510                         maxfd = handler->fd;
511         }
512
513         /*
514          * Setup the timeout to the estimated time until the
515          * next timer expires.
516          */
517         if (!waitForInput) {
518                 SET_ZERO(timeout);
519                 timeoutPtr = &timeout;
520         } else if (timerPending()) {
521                 delayUntilNextTimerEvent(&timeout);
522                 timeoutPtr = &timeout;
523         } else {
524                 timeoutPtr = (struct timeval *)0;
525         }
526
527         count = select(1 + maxfd, &rset, &wset, &eset, timeoutPtr);
528
529         if (count > 0 && nfds > 0) {
530                 WMArray *handlerCopy = WMDuplicateArray(inputHandler);
531                 int mask;
532
533                 /* use WM_ITERATE_ARRAY() here */
534                 for (i = 0; i < nfds; i++) {
535                         handler = WMGetFromArray(handlerCopy, i);
536                         /* check if the handler still exist or was removed by a callback */
537                         if (WMGetFirstInArray(inputHandler, handler) == WANotFound)
538                                 continue;
539
540                         mask = 0;
541
542                         if ((handler->mask & WIReadMask) && FD_ISSET(handler->fd, &rset))
543                                 mask |= WIReadMask;
544
545                         if ((handler->mask & WIWriteMask) && FD_ISSET(handler->fd, &wset))
546                                 mask |= WIWriteMask;
547
548                         if ((handler->mask & WIExceptMask) && FD_ISSET(handler->fd, &eset))
549                                 mask |= WIExceptMask;
550
551                         if (mask != 0 && handler->callback) {
552                                 (*handler->callback) (handler->fd, mask, handler->clientData);
553                         }
554                 }
555
556                 WMFreeArray(handlerCopy);
557         }
558
559         W_FlushASAPNotificationQueue();
560
561         return (count > 0);
562 #else                           /* not HAVE_SELECT, not HAVE_POLL */
563 # error   Neither select nor poll. You lose.
564 #endif                          /* HAVE_SELECT */
565 #endif                          /* HAVE_POLL */
566 }