3 * WINGs internal handlers: timer, idle and input handlers
14 #ifdef HAVE_SYS_SELECT_H
15 # include <sys/select.h>
20 #ifndef X_GETTIMEOFDAY
21 #define X_GETTIMEOFDAY(t) gettimeofday(t, (struct timezone*)0)
24 typedef struct TimerHandler
{
25 WMCallback
*callback
; /* procedure to call */
26 struct timeval when
; /* when to call the callback */
28 struct TimerHandler
*next
;
29 int nextDelay
; /* 0 if it's one-shot */
32 typedef struct IdleHandler
{
37 typedef struct InputHandler
{
38 WMInputProc
*callback
;
44 /* queue of timer event handlers */
45 static TimerHandler
*timerHandler
= NULL
;
47 static WMArray
*idleHandler
= NULL
;
49 static WMArray
*inputHandler
= NULL
;
51 #define timerPending() (timerHandler)
53 static void rightNow(struct timeval
*tv
)
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)))
63 #define IS_ZERO(tv) (tv.tv_sec == 0 && tv.tv_usec == 0)
65 #define SET_ZERO(tv) tv.tv_sec = 0, tv.tv_usec = 0
67 static void addmillisecs(struct timeval
*tv
, int milliseconds
)
69 tv
->tv_usec
+= milliseconds
* 1000;
71 tv
->tv_sec
+= tv
->tv_usec
/ 1000000;
72 tv
->tv_usec
= tv
->tv_usec
% 1000000;
75 static void enqueueTimerHandler(TimerHandler
* handler
)
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
;
86 while (tmp
->next
&& IS_AFTER(handler
->when
, tmp
->next
->when
)) {
89 handler
->next
= tmp
->next
;
94 static void delayUntilNextTimerEvent(struct timeval
*delay
)
97 TimerHandler
*handler
;
99 handler
= timerHandler
;
100 while (handler
&& IS_ZERO(handler
->when
))
101 handler
= handler
->next
;
104 /* The return value of this function is only valid if there _are_
112 if (IS_AFTER(now
, handler
->when
)) {
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;
125 WMHandlerID
WMAddTimerHandler(int milliseconds
, WMCallback
* callback
, void *cdata
)
127 TimerHandler
*handler
;
129 handler
= malloc(sizeof(TimerHandler
));
133 rightNow(&handler
->when
);
134 addmillisecs(&handler
->when
, milliseconds
);
135 handler
->callback
= callback
;
136 handler
->clientData
= cdata
;
137 handler
->nextDelay
= 0;
139 enqueueTimerHandler(handler
);
144 WMHandlerID
WMAddPersistentTimerHandler(int milliseconds
, WMCallback
* callback
, void *cdata
)
146 TimerHandler
*handler
= WMAddTimerHandler(milliseconds
, callback
, cdata
);
149 handler
->nextDelay
= milliseconds
;
154 void WMDeleteTimerWithClientData(void *cdata
)
156 TimerHandler
*handler
, *tmp
;
158 if (!cdata
|| !timerHandler
)
162 if (tmp
->clientData
== cdata
) {
164 if (!IS_ZERO(tmp
->when
)) {
165 timerHandler
= tmp
->next
;
170 if (tmp
->next
->clientData
== cdata
) {
172 handler
->nextDelay
= 0;
173 if (IS_ZERO(handler
->when
))
175 tmp
->next
= handler
->next
;
184 void WMDeleteTimerHandler(WMHandlerID handlerID
)
186 TimerHandler
*tmp
, *handler
= (TimerHandler
*) handlerID
;
188 if (!handler
|| !timerHandler
)
193 handler
->nextDelay
= 0;
195 if (IS_ZERO(handler
->when
))
198 if (tmp
== handler
) {
199 timerHandler
= handler
->next
;
203 if (tmp
->next
== handler
) {
204 tmp
->next
= handler
->next
;
213 WMHandlerID
WMAddIdleHandler(WMCallback
* callback
, void *cdata
)
215 IdleHandler
*handler
;
217 handler
= malloc(sizeof(IdleHandler
));
221 handler
->callback
= callback
;
222 handler
->clientData
= cdata
;
223 /* add handler at end of queue */
225 idleHandler
= WMCreateArrayWithDestructor(16, wfree
);
227 WMAddToArray(idleHandler
, handler
);
232 void WMDeleteIdleHandler(WMHandlerID handlerID
)
234 IdleHandler
*handler
= (IdleHandler
*) handlerID
;
236 if (!handler
|| !idleHandler
)
239 WMRemoveFromArray(idleHandler
, handler
);
242 WMHandlerID
WMAddInputHandler(int fd
, int condition
, WMInputProc
* proc
, void *clientData
)
244 InputHandler
*handler
;
246 handler
= wmalloc(sizeof(InputHandler
));
249 handler
->mask
= condition
;
250 handler
->callback
= proc
;
251 handler
->clientData
= clientData
;
254 inputHandler
= WMCreateArrayWithDestructor(16, wfree
);
255 WMAddToArray(inputHandler
, handler
);
260 void WMDeleteInputHandler(WMHandlerID handlerID
)
262 InputHandler
*handler
= (InputHandler
*) handlerID
;
264 if (!handler
|| !inputHandler
)
267 WMRemoveFromArray(inputHandler
, handler
);
270 Bool
W_CheckIdleHandlers(void)
272 IdleHandler
*handler
;
273 WMArray
*handlerCopy
;
274 WMArrayIterator iter
;
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);
282 handlerCopy
= WMDuplicateArray(idleHandler
);
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
)
289 (*handler
->callback
) (handler
->clientData
);
290 WMDeleteIdleHandler(handler
);
293 WMFreeArray(handlerCopy
);
295 W_FlushIdleNotificationQueue();
297 /* this is not necesarrily False, because one handler can re-add itself */
298 return (WMGetArrayItemCount(idleHandler
) > 0);
301 void W_CheckTimerHandlers(void)
303 TimerHandler
*handler
;
307 W_FlushASAPNotificationQueue();
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
);
319 handler
= handler
->next
;
322 while (timerHandler
&& IS_ZERO(timerHandler
->when
)) {
323 handler
= timerHandler
;
324 timerHandler
= timerHandler
->next
;
326 if (handler
->nextDelay
> 0) {
328 addmillisecs(&handler
->when
, handler
->nextDelay
);
329 enqueueTimerHandler(handler
);
335 W_FlushASAPNotificationQueue();
339 * This functions will handle input events on all registered file descriptors.
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.
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
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.
360 * If the retured value is True, the input handlers for the corresponding file
361 * descriptors are also called.
363 * Parametersshould be passed like this:
365 * waitForInput - apropriate value passed by the function who called us
366 * inputfd = ConnectionNumber(dpy)
368 * waitForInput - apropriate value passed by the function who called us
372 Bool
W_HandleInputEvents(Bool waitForInput
, int inputfd
)
374 #if defined(HAVE_POLL) && defined(HAVE_POLL_H) && !defined(HAVE_SELECT)
376 InputHandler
*handler
;
377 int count
, timeout
, nfds
, i
, extrafd
;
379 extrafd
= (inputfd
< 0) ? 0 : 1;
382 nfds
= WMGetArrayItemCount(inputHandler
);
386 if (!extrafd
&& nfds
== 0) {
387 W_FlushASAPNotificationQueue();
391 fds
= wmalloc((nfds
+ extrafd
) * sizeof(struct pollfd
));
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
;
398 /* use WM_ITERATE_ARRAY() here */
399 for (i
= 0; i
< nfds
; i
++) {
400 handler
= WMGetFromArray(inputHandler
, i
);
401 fds
[i
].fd
= handler
->fd
;
403 if (handler
->mask
& WIReadMask
)
404 fds
[i
].events
|= POLLIN
;
406 if (handler
->mask
& WIWriteMask
)
407 fds
[i
].events
|= POLLOUT
;
410 if (handler
->mask
& WIExceptMask
)
411 FD_SET(handler
->fd
, &eset
);
416 * Setup the timeout to the estimated time until the
417 * next timer expires.
421 } else if (timerPending()) {
423 delayUntilNextTimerEvent(&tv
);
424 timeout
= tv
.tv_sec
* 1000 + tv
.tv_usec
/ 1000;
429 count
= poll(fds
, nfds
+ extrafd
, timeout
);
431 if (count
> 0 && nfds
> 0) {
432 WMArray
*handlerCopy
= WMDuplicateArray(inputHandler
);
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
)
444 if ((handler
->mask
& WIReadMask
) &&
445 (fds
[i
].revents
& (POLLIN
| POLLRDNORM
| POLLRDBAND
| POLLPRI
)))
448 if ((handler
->mask
& WIWriteMask
) && (fds
[i
].revents
& (POLLOUT
| POLLWRBAND
)))
451 if ((handler
->mask
& WIExceptMask
) && (fds
[i
].revents
& (POLLHUP
| POLLNVAL
| POLLERR
)))
452 mask
|= WIExceptMask
;
454 if (mask
!= 0 && handler
->callback
) {
455 (*handler
->callback
) (handler
->fd
, mask
, handler
->clientData
);
459 WMFreeArray(handlerCopy
);
464 W_FlushASAPNotificationQueue();
469 struct timeval timeout
;
470 struct timeval
*timeoutPtr
;
471 fd_set rset
, wset
, eset
;
474 InputHandler
*handler
;
477 nfds
= WMGetArrayItemCount(inputHandler
);
481 if (inputfd
< 0 && nfds
== 0) {
482 W_FlushASAPNotificationQueue();
493 FD_SET(inputfd
, &rset
);
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
);
503 if (handler
->mask
& WIWriteMask
)
504 FD_SET(handler
->fd
, &wset
);
506 if (handler
->mask
& WIExceptMask
)
507 FD_SET(handler
->fd
, &eset
);
509 if (maxfd
< handler
->fd
)
514 * Setup the timeout to the estimated time until the
515 * next timer expires.
519 timeoutPtr
= &timeout
;
520 } else if (timerPending()) {
521 delayUntilNextTimerEvent(&timeout
);
522 timeoutPtr
= &timeout
;
524 timeoutPtr
= (struct timeval
*)0;
527 count
= select(1 + maxfd
, &rset
, &wset
, &eset
, timeoutPtr
);
529 if (count
> 0 && nfds
> 0) {
530 WMArray
*handlerCopy
= WMDuplicateArray(inputHandler
);
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
)
542 if ((handler
->mask
& WIReadMask
) && FD_ISSET(handler
->fd
, &rset
))
545 if ((handler
->mask
& WIWriteMask
) && FD_ISSET(handler
->fd
, &wset
))
548 if ((handler
->mask
& WIExceptMask
) && FD_ISSET(handler
->fd
, &eset
))
549 mask
|= WIExceptMask
;
551 if (mask
!= 0 && handler
->callback
) {
552 (*handler
->callback
) (handler
->fd
, mask
, handler
->clientData
);
556 WMFreeArray(handlerCopy
);
559 W_FlushASAPNotificationQueue();
562 #else /* not HAVE_SELECT, not HAVE_POLL */
563 # error Neither select nor poll. You lose.
564 #endif /* HAVE_SELECT */
565 #endif /* HAVE_POLL */