4 * This event handling stuff was based on Tk.
5 * adapted from wevent.c
10 #include "../src/config.h"
12 #include <sys/types.h>
22 #ifdef HAVE_SYS_SELECT_H
23 # include <sys/select.h>
28 #ifndef X_GETTIMEOFDAY
29 #define X_GETTIMEOFDAY(t) gettimeofday(t, (struct timezone*)0)
35 typedef struct TimerHandler
{
36 WMCallback
*callback
; /* procedure to call */
37 struct timeval when
; /* when to call the callback */
39 struct TimerHandler
*next
;
43 typedef struct IdleHandler
{
49 typedef struct InputHandler
{
50 WMInputProc
*callback
;
57 /* queue of timer event handlers */
58 static TimerHandler
*timerHandler
=NULL
;
60 static WMBag
*idleHandler
=NULL
;
62 static WMBag
*inputHandler
=NULL
;
66 #define timerPending() (timerHandler)
70 rightNow(struct timeval
*tv
) {
74 /* is t1 after t2 ? */
75 #define IS_AFTER(t1, t2) (((t1).tv_sec > (t2).tv_sec) || \
76 (((t1).tv_sec == (t2).tv_sec) \
77 && ((t1).tv_usec > (t2).tv_usec)))
81 addmillisecs(struct timeval
*tv
, int milliseconds
)
83 tv
->tv_usec
+= milliseconds
*1000;
85 tv
->tv_sec
+= tv
->tv_usec
/1000000;
86 tv
->tv_usec
= tv
->tv_usec
%1000000;
91 WMAddTimerHandler(int milliseconds
, WMCallback
*callback
, void *cdata
)
93 TimerHandler
*handler
, *tmp
;
95 handler
= malloc(sizeof(TimerHandler
));
99 rightNow(&handler
->when
);
100 addmillisecs(&handler
->when
, milliseconds
);
101 handler
->callback
= callback
;
102 handler
->clientData
= cdata
;
103 /* insert callback in queue, sorted by time left */
104 if (!timerHandler
|| !IS_AFTER(handler
->when
, timerHandler
->when
)) {
105 /* first in the queue */
106 handler
->next
= timerHandler
;
107 timerHandler
= handler
;
110 while (tmp
->next
&& IS_AFTER(handler
->when
, tmp
->next
->when
)) {
113 handler
->next
= tmp
->next
;
122 WMDeleteTimerWithClientData(void *cdata
)
124 TimerHandler
*handler
, *tmp
;
126 if (!cdata
|| !timerHandler
)
130 if (tmp
->clientData
==cdata
) {
131 timerHandler
= tmp
->next
;
135 if (tmp
->next
->clientData
==cdata
) {
137 tmp
->next
= handler
->next
;
149 WMDeleteTimerHandler(WMHandlerID handlerID
)
151 TimerHandler
*tmp
, *handler
=(TimerHandler
*)handlerID
;
153 if (!handler
|| !timerHandler
)
158 timerHandler
= handler
->next
;
162 if (tmp
->next
==handler
) {
163 tmp
->next
=handler
->next
;
175 WMAddIdleHandler(WMCallback
*callback
, void *cdata
)
177 IdleHandler
*handler
;
179 handler
= malloc(sizeof(IdleHandler
));
183 handler
->callback
= callback
;
184 handler
->clientData
= cdata
;
185 /* add handler at end of queue */
187 idleHandler
= WMCreateBag(16);
189 WMPutInBag(idleHandler
, handler
);
197 WMDeleteIdleHandler(WMHandlerID handlerID
)
199 IdleHandler
*handler
= (IdleHandler
*)handlerID
;
202 if (!handler
|| !idleHandler
)
205 pos
= WMGetFirstInBag(idleHandler
, handler
);
208 WMDeleteFromBag(idleHandler
, pos
);
215 WMAddInputHandler(int fd
, int condition
, WMInputProc
*proc
, void *clientData
)
217 InputHandler
*handler
;
219 handler
= wmalloc(sizeof(InputHandler
));
222 handler
->mask
= condition
;
223 handler
->callback
= proc
;
224 handler
->clientData
= clientData
;
227 inputHandler
= WMCreateBag(16);
228 WMPutInBag(inputHandler
, handler
);
236 WMDeleteInputHandler(WMHandlerID handlerID
)
238 InputHandler
*handler
= (InputHandler
*)handlerID
;
241 if (!handler
|| !inputHandler
)
244 pos
= WMGetFirstInBag(inputHandler
, handler
);
247 WMDeleteFromBag(inputHandler
, pos
);
256 IdleHandler
*handler
;
260 if (!idleHandler
|| WMGetBagItemCount(idleHandler
)==0) {
261 W_FlushIdleNotificationQueue();
262 /* make sure an observer in queue didn't added an idle handler */
263 return (idleHandler
!=NULL
&& WMGetBagItemCount(idleHandler
)>0);
266 n
= WMGetBagItemCount(idleHandler
);
267 handlerCopy
= WMCreateBag(n
);
269 WMPutInBag(handlerCopy
, WMGetFromBag(idleHandler
, i
));
271 for (i
=0; i
<n
; i
++) {
272 handler
= WMGetFromBag(handlerCopy
, i
);
273 /* check if the handler still exist or was removed by a callback */
274 if (WMGetFirstInBag(idleHandler
, handler
)<0)
277 (*handler
->callback
)(handler
->clientData
);
278 WMDeleteIdleHandler(handler
);
282 WMFreeBag(handlerCopy
);
284 W_FlushIdleNotificationQueue();
286 /* this is not necesarrily False, because one handler can re-add itself */
287 return (WMGetBagItemCount(idleHandler
)>0);
295 TimerHandler
*handler
;
299 W_FlushASAPNotificationQueue();
305 while (timerHandler
&& IS_AFTER(now
, timerHandler
->when
)) {
306 handler
= timerHandler
;
307 timerHandler
= timerHandler
->next
;
308 handler
->next
= NULL
;
309 (*handler
->callback
)(handler
->clientData
);
313 W_FlushASAPNotificationQueue();
319 delayUntilNextTimerEvent(struct timeval
*delay
)
324 /* The return value of this function is only valid if there _are_
332 if (IS_AFTER(now
, timerHandler
->when
)) {
336 delay
->tv_sec
= timerHandler
->when
.tv_sec
- now
.tv_sec
;
337 delay
->tv_usec
= timerHandler
->when
.tv_usec
- now
.tv_usec
;
338 if (delay
->tv_usec
< 0) {
339 delay
->tv_usec
+= 1000000;
347 * This functions will handle input events on all registered file descriptors.
349 * - waitForInput - True if we want the function to wait until an event
350 * appears on a file descriptor we watch, False if we
351 * want the function to immediately return if there is
352 * no data available on the file descriptors we watch.
354 * if waitForInput is False, the function will return False if there are no
355 * input handlers registered, or if there is no data
356 * available on the registered ones, and will return True
357 * if there is at least one input handler that has data
359 * if waitForInput is True, the function will return False if there are no
360 * input handlers registered, else it will block until an
361 * event appears on one of the file descriptors it watches
362 * and then it will return True.
364 * If the retured value is True, the input handlers for the corresponding file
365 * descriptors are also called.
369 handleInputEvents(Bool waitForInput
)
371 #if defined(HAVE_POLL) && defined(HAVE_POLL_H) && !defined(HAVE_SELECT)
373 InputHandler
*handler
;
374 int count
, timeout
, nfds
, i
;
376 if (!inputHandler
|| (nfds
=WMGetBagItemCount(inputHandler
))==0) {
377 W_FlushASAPNotificationQueue();
381 fds
= wmalloc(nfds
* sizeof(struct pollfd
));
383 for (i
= 0; i
<nfds
; i
++) {
384 handler
= WMGetFromBag(inputHandler
, i
);
385 fds
[i
].fd
= handler
->fd
;
387 if (handler
->mask
& WIReadMask
)
388 fds
[i
].events
|= POLLIN
;
390 if (handler
->mask
& WIWriteMask
)
391 fds
[i
].events
|= POLLOUT
;
394 if (handler
->mask
& WIExceptMask
)
395 FD_SET(handler
->fd
, &eset
);
400 * If we don't wait for input, set timeout to return immediately,
401 * else setup the timeout to the estimated time until the
402 * next timer expires or if no timer is pending to infinite.
406 } else if (timerPending()) {
408 delayUntilNextTimerEvent(&tv
);
409 timeout
= tv
.tv_sec
* 1000 + tv
.tv_usec
/ 1000;
414 count
= poll(fds
, nfds
, timeout
);
417 WMBag
*handlerCopy
= WMCreateBag(nfds
);
419 for (i
=0; i
<nfds
, i
++)
420 WMPutInBag(handlerCopy
, WMGetFromBag(inputHandler
, i
));
422 for (i
=0; i
<nfds
; i
++) {
425 handler
= WMGetFromBag(handlerCopy
, i
);
426 /* check if the handler still exist or was removed by a callback */
427 if (WMGetFirstInBag(inputHandler
, handler
)<0)
432 if ((handler
->mask
& WIReadMask
) &&
433 (fds
[i
].revents
& (POLLIN
|POLLRDNORM
|POLLRDBAND
|POLLPRI
)))
436 if ((handler
->mask
& WIWriteMask
) &&
437 (fds
[i
].revents
& (POLLOUT
| POLLWRBAND
)))
440 if ((handler
->mask
& WIExceptMask
) &&
441 (fds
[i
].revents
& (POLLHUP
| POLLNVAL
| POLLERR
)))
442 mask
|= WIExceptMask
;
444 if (mask
!=0 && handler
->callback
) {
445 (*handler
->callback
)(handler
->fd
, mask
,
446 handler
->clientData
);
450 WMFreeBag(handlerCopy
);
455 W_FlushASAPNotificationQueue();
458 #else /* not HAVE_POLL */
460 struct timeval timeout
;
461 struct timeval
*timeoutPtr
;
462 fd_set rset
, wset
, eset
;
465 InputHandler
*handler
;
467 if (!inputHandler
|| (nfds
=WMGetBagItemCount(inputHandler
))==0) {
468 W_FlushASAPNotificationQueue();
478 for (i
=0; i
<nfds
; i
++) {
479 handler
= WMGetFromBag(inputHandler
, i
);
480 if (handler
->mask
& WIReadMask
)
481 FD_SET(handler
->fd
, &rset
);
483 if (handler
->mask
& WIWriteMask
)
484 FD_SET(handler
->fd
, &wset
);
486 if (handler
->mask
& WIExceptMask
)
487 FD_SET(handler
->fd
, &eset
);
489 if (maxfd
< handler
->fd
)
494 * If we don't wait for input, set timeout to return immediately,
495 * else setup the timeout to the estimated time until the
496 * next timer expires or if no timer is pending to infinite.
501 timeoutPtr
= &timeout
;
502 } else if (timerPending()) {
503 delayUntilNextTimerEvent(&timeout
);
504 timeoutPtr
= &timeout
;
506 timeoutPtr
= (struct timeval
*)0;
509 count
= select(1 + maxfd
, &rset
, &wset
, &eset
, timeoutPtr
);
512 WMBag
*handlerCopy
= WMCreateBag(nfds
);
514 for (i
=0; i
<nfds
; i
++)
515 WMPutInBag(handlerCopy
, WMGetFromBag(inputHandler
, i
));
517 for (i
=0; i
<nfds
; i
++) {
520 handler
= WMGetFromBag(handlerCopy
, i
);
521 /* check if the handler still exist or was removed by a callback */
522 if (WMGetFirstInBag(inputHandler
, handler
)<0)
527 if ((handler
->mask
& WIReadMask
) && FD_ISSET(handler
->fd
, &rset
))
530 if ((handler
->mask
& WIWriteMask
) && FD_ISSET(handler
->fd
, &wset
))
533 if ((handler
->mask
& WIExceptMask
) && FD_ISSET(handler
->fd
, &eset
))
534 mask
|= WIExceptMask
;
536 if (mask
!=0 && handler
->callback
) {
537 (*handler
->callback
)(handler
->fd
, mask
,
538 handler
->clientData
);
542 WMFreeBag(handlerCopy
);
545 W_FlushASAPNotificationQueue();
548 #else /* not HAVE_SELECT, not HAVE_POLL */
549 Neither select nor poll
. You lose
.
550 #endif /* HAVE_SELECT */
551 #endif /* HAVE_POLL */
558 /* Check any expired timers */
559 checkTimerHandlers();
561 /* We need to make sure that we have some input handler before calling
562 * checkIdleHandlers() in a while loop, because else the while loop
563 * can run forever (if some idle handler reinitiates itself).
565 if (inputHandler
&& WMGetBagItemCount(inputHandler
)>0) {
566 /* Do idle and timer stuff while there are no input events */
567 /* Check again if there are still input handlers, because some idle
568 * handler could have removed them */
569 while (checkIdleHandlers() && WMGetBagItemCount(inputHandler
)>0 &&
570 !handleInputEvents(False
)) {
571 /* dispatch timer events */
572 checkTimerHandlers();
576 /* dispatch timer events */
577 checkTimerHandlers();
580 handleInputEvents(True
);
582 /* Check any expired timers */
583 checkTimerHandlers();