4 * This event handling stuff was based on Tk.
9 #include "../src/config.h"
11 #include <sys/types.h>
21 #ifdef HAVE_SYS_SELECT_H
22 # include <sys/select.h>
27 #ifndef X_GETTIMEOFDAY
28 #define X_GETTIMEOFDAY(t) gettimeofday(t, (struct timezone*)0)
36 typedef struct TimerHandler
{
37 WMCallback
*callback
; /* procedure to call */
38 struct timeval when
; /* when to call the callback */
40 struct TimerHandler
*next
;
44 typedef struct IdleHandler
{
50 typedef struct InputHandler
{
51 WMInputProc
*callback
;
58 /* table to map event types to event masks */
59 static unsigned long eventMasks
[] = {
62 KeyPressMask
, /* KeyPress */
63 KeyReleaseMask
, /* KeyRelease */
64 ButtonPressMask
, /* ButtonPress */
65 ButtonReleaseMask
, /* ButtonRelease */
66 PointerMotionMask
|PointerMotionHintMask
|ButtonMotionMask
67 |Button1MotionMask
|Button2MotionMask
|Button3MotionMask
68 |Button4MotionMask
|Button5MotionMask
,
70 EnterWindowMask
, /* EnterNotify */
71 LeaveWindowMask
, /* LeaveNotify */
72 FocusChangeMask
, /* FocusIn */
73 FocusChangeMask
, /* FocusOut */
74 KeymapStateMask
, /* KeymapNotify */
75 ExposureMask
, /* Expose */
76 ExposureMask
, /* GraphicsExpose */
77 ExposureMask
, /* NoExpose */
78 VisibilityChangeMask
, /* VisibilityNotify */
79 SubstructureNotifyMask
, /* CreateNotify */
80 StructureNotifyMask
, /* DestroyNotify */
81 StructureNotifyMask
, /* UnmapNotify */
82 StructureNotifyMask
, /* MapNotify */
83 SubstructureRedirectMask
, /* MapRequest */
84 StructureNotifyMask
, /* ReparentNotify */
85 StructureNotifyMask
, /* ConfigureNotify */
86 SubstructureRedirectMask
, /* ConfigureRequest */
87 StructureNotifyMask
, /* GravityNotify */
88 ResizeRedirectMask
, /* ResizeRequest */
89 StructureNotifyMask
, /* CirculateNotify */
90 SubstructureRedirectMask
, /* CirculateRequest */
91 PropertyChangeMask
, /* PropertyNotify */
92 0, /* SelectionClear */
93 0, /* SelectionRequest */
94 0, /* SelectionNotify */
95 ColormapChangeMask
, /* ColormapNotify */
96 ClientMessageMask
, /* ClientMessage */
97 0, /* Mapping Notify */
102 /* queue of timer event handlers */
103 static TimerHandler
*timerHandler
=NULL
;
105 static WMBag
*idleHandler
=NULL
;
107 static WMBag
*inputHandler
=NULL
;
109 /* hook for other toolkits or wmaker process their events */
110 static WMEventHook
*extraEventHandler
=NULL
;
114 #define timerPending() (timerHandler)
119 rightNow(struct timeval
*tv
) {
123 /* is t1 after t2 ? */
124 #define IS_AFTER(t1, t2) (((t1).tv_sec > (t2).tv_sec) || \
125 (((t1).tv_sec == (t2).tv_sec) \
126 && ((t1).tv_usec > (t2).tv_usec)))
130 addmillisecs(struct timeval
*tv
, int milliseconds
)
132 tv
->tv_usec
+= milliseconds
*1000;
134 tv
->tv_sec
+= tv
->tv_usec
/1000000;
135 tv
->tv_usec
= tv
->tv_usec
%1000000;
140 WMAddTimerHandler(int milliseconds
, WMCallback
*callback
, void *cdata
)
142 TimerHandler
*handler
, *tmp
;
144 handler
= malloc(sizeof(TimerHandler
));
148 rightNow(&handler
->when
);
149 addmillisecs(&handler
->when
, milliseconds
);
150 handler
->callback
= callback
;
151 handler
->clientData
= cdata
;
152 /* insert callback in queue, sorted by time left */
153 if (!timerHandler
|| !IS_AFTER(handler
->when
, timerHandler
->when
)) {
154 /* first in the queue */
155 handler
->next
= timerHandler
;
156 timerHandler
= handler
;
159 while (tmp
->next
&& IS_AFTER(handler
->when
, tmp
->next
->when
)) {
162 handler
->next
= tmp
->next
;
171 WMDeleteTimerWithClientData(void *cdata
)
173 TimerHandler
*handler
, *tmp
;
175 if (!cdata
|| !timerHandler
)
179 if (tmp
->clientData
==cdata
) {
180 timerHandler
= tmp
->next
;
184 if (tmp
->next
->clientData
==cdata
) {
186 tmp
->next
= handler
->next
;
198 WMDeleteTimerHandler(WMHandlerID handlerID
)
200 TimerHandler
*tmp
, *handler
=(TimerHandler
*)handlerID
;
202 if (!handler
|| !timerHandler
)
207 timerHandler
= handler
->next
;
211 if (tmp
->next
==handler
) {
212 tmp
->next
=handler
->next
;
224 WMAddIdleHandler(WMCallback
*callback
, void *cdata
)
226 IdleHandler
*handler
;
228 handler
= malloc(sizeof(IdleHandler
));
232 handler
->callback
= callback
;
233 handler
->clientData
= cdata
;
234 /* add handler at end of queue */
236 idleHandler
= WMCreateBag(16);
238 WMPutInBag(idleHandler
, handler
);
246 WMDeleteIdleHandler(WMHandlerID handlerID
)
248 IdleHandler
*handler
= (IdleHandler
*)handlerID
;
251 if (!handler
|| !idleHandler
)
254 pos
= WMGetFirstInBag(idleHandler
, handler
);
255 if (pos
!= WBNotFound
) {
257 WMDeleteFromBag(idleHandler
, pos
);
264 WMAddInputHandler(int fd
, int condition
, WMInputProc
*proc
, void *clientData
)
266 InputHandler
*handler
;
268 handler
= wmalloc(sizeof(InputHandler
));
271 handler
->mask
= condition
;
272 handler
->callback
= proc
;
273 handler
->clientData
= clientData
;
276 inputHandler
= WMCreateBag(16);
277 WMPutInBag(inputHandler
, handler
);
285 WMDeleteInputHandler(WMHandlerID handlerID
)
287 InputHandler
*handler
= (InputHandler
*)handlerID
;
290 if (!handler
|| !inputHandler
)
293 pos
= WMGetFirstInBag(inputHandler
, handler
);
294 if (pos
!= WBNotFound
) {
296 WMDeleteFromBag(inputHandler
, pos
);
305 IdleHandler
*handler
;
309 if (!idleHandler
|| WMGetBagItemCount(idleHandler
)==0) {
310 W_FlushIdleNotificationQueue();
311 /* make sure an observer in queue didn't added an idle handler */
312 return (idleHandler
!=NULL
&& WMGetBagItemCount(idleHandler
)>0);
315 handlerCopy
= WMCreateBag(WMGetBagItemCount(idleHandler
));
316 WMAppendBag(handlerCopy
, idleHandler
);
318 for (handler
= WMBagFirst(handlerCopy
, &iter
);
320 handler
= WMBagNext(handlerCopy
, &iter
)) {
321 /* check if the handler still exist or was removed by a callback */
322 if (WMGetFirstInBag(idleHandler
, handler
) == WBNotFound
)
325 (*handler
->callback
)(handler
->clientData
);
326 WMDeleteIdleHandler(handler
);
329 WMFreeBag(handlerCopy
);
331 W_FlushIdleNotificationQueue();
333 /* this is not necesarrily False, because one handler can re-add itself */
334 return (WMGetBagItemCount(idleHandler
)>0);
342 TimerHandler
*handler
;
346 W_FlushASAPNotificationQueue();
352 while (timerHandler
&& IS_AFTER(now
, timerHandler
->when
)) {
353 handler
= timerHandler
;
354 timerHandler
= timerHandler
->next
;
355 handler
->next
= NULL
;
356 (*handler
->callback
)(handler
->clientData
);
360 W_FlushASAPNotificationQueue();
366 delayUntilNextTimerEvent(struct timeval
*delay
)
371 /* The return value of this function is only valid if there _are_
379 if (IS_AFTER(now
, timerHandler
->when
)) {
383 delay
->tv_sec
= timerHandler
->when
.tv_sec
- now
.tv_sec
;
384 delay
->tv_usec
= timerHandler
->when
.tv_usec
- now
.tv_usec
;
385 if (delay
->tv_usec
< 0) {
386 delay
->tv_usec
+= 1000000;
396 * WMCreateEventHandler--
397 * Create an event handler and put it in the event handler list for the
398 * view. If the same callback and clientdata are already used in another
399 * handler, the masks are swapped.
403 WMCreateEventHandler(WMView
*view
, unsigned long mask
, WMEventProc
*eventProc
,
406 W_EventHandler
*handler
;
407 W_EventHandler
*ptr
= view
->handlerList
;
408 unsigned long eventMask
;
411 handler
= wmalloc(sizeof(W_EventHandler
));
413 handler
->nextHandler
= NULL
;
415 view
->handlerList
= handler
;
421 while (ptr
!= NULL
) {
422 if (ptr
->clientData
== clientData
&& ptr
->proc
== eventProc
) {
425 eventMask
|= ptr
->eventMask
;
427 ptr
= ptr
->nextHandler
;
430 handler
= wmalloc(sizeof(W_EventHandler
));
431 handler
->nextHandler
= view
->handlerList
;
432 view
->handlerList
= handler
;
436 /* select events for window */
437 handler
->eventMask
= mask
;
438 handler
->proc
= eventProc
;
439 handler
->clientData
= clientData
;
444 * WMDeleteEventHandler--
445 * Delete event handler matching arguments from windows
446 * event handler list.
450 WMDeleteEventHandler(WMView
*view
, unsigned long mask
, WMEventProc
*eventProc
,
453 W_EventHandler
*handler
, *ptr
, *pptr
;
455 ptr
= view
->handlerList
;
461 if (ptr
->eventMask
== mask
&& ptr
->proc
== eventProc
462 && ptr
->clientData
== clientData
) {
467 ptr
= ptr
->nextHandler
;
474 view
->handlerList
= handler
->nextHandler
;
476 pptr
->nextHandler
= handler
->nextHandler
;
484 W_CleanUpEvents(WMView
*view
)
486 W_EventHandler
*ptr
, *nptr
;
488 ptr
= view
->handlerList
;
491 nptr
= ptr
->nextHandler
;
500 getEventTime(WMScreen
*screen
, XEvent
*event
)
502 switch (event
->type
) {
505 return event
->xbutton
.time
;
508 return event
->xkey
.time
;
510 return event
->xmotion
.time
;
513 return event
->xcrossing
.time
;
515 return event
->xproperty
.time
;
517 return event
->xselectionclear
.time
;
518 case SelectionRequest
:
519 return event
->xselectionrequest
.time
;
520 case SelectionNotify
:
521 return event
->xselection
.time
;
523 return screen
->lastEventTime
;
529 W_CallDestroyHandlers(W_View
*view
)
532 W_EventHandler
*hPtr
;
534 event
.type
= DestroyNotify
;
535 event
.xdestroywindow
.window
= view
->window
;
536 event
.xdestroywindow
.event
= view
->window
;
537 hPtr
= view
->handlerList
;
539 if (hPtr
->eventMask
& StructureNotifyMask
) {
540 (*hPtr
->proc
)(&event
, hPtr
->clientData
);
543 hPtr
= hPtr
->nextHandler
;
549 WMHandleEvent(XEvent
*event
)
551 W_EventHandler
*hPtr
;
552 W_View
*view
, *vPtr
, *toplevel
;
556 if (event
->type
== MappingNotify
) {
557 XRefreshKeyboardMapping(&event
->xmapping
);
561 mask
= eventMasks
[event
->xany
.type
];
563 window
= event
->xany
.window
;
565 /* diferentiate SubstructureNotify with StructureNotify */
566 if (mask
== StructureNotifyMask
) {
567 if (event
->xmap
.event
!= event
->xmap
.window
) {
568 mask
= SubstructureNotifyMask
;
569 window
= event
->xmap
.event
;
572 view
= W_GetViewForXWindow(event
->xany
.display
, window
);
574 if (extraEventHandler
)
575 (extraEventHandler
)(event
);
580 view
->screen
->lastEventTime
= getEventTime(view
->screen
, event
);
582 toplevel
= W_TopLevelOfView(view
);
584 if (event
->type
== SelectionNotify
|| event
->type
== SelectionClear
585 || event
->type
== SelectionRequest
) {
586 /* handle selection related events */
587 W_HandleSelectionEvent(event
);
589 } else if (event
->type
== ClientMessage
) {
591 W_HandleDNDClientMessage(toplevel
, &event
->xclient
);
594 /* if it's a key event, redispatch it to the focused control */
595 if (mask
& (KeyPressMask
|KeyReleaseMask
)) {
596 W_View
*focused
= W_FocusedViewOfToplevel(toplevel
);
603 /* compress Motion events */
604 if (event
->type
== MotionNotify
&& !view
->flags
.dontCompressMotion
) {
605 while (XPending(event
->xmotion
.display
)) {
607 XPeekEvent(event
->xmotion
.display
, &ev
);
608 if (ev
.type
== MotionNotify
609 && event
->xmotion
.window
== ev
.xmotion
.window
610 && event
->xmotion
.subwindow
== ev
.xmotion
.subwindow
) {
612 XNextEvent(event
->xmotion
.display
, event
);
617 /* compress expose events */
618 if (event
->type
== Expose
&& !view
->flags
.dontCompressExpose
) {
619 while (XCheckTypedWindowEvent(event
->xexpose
.display
, view
->window
,
624 if (view
->screen
->modal
&& toplevel
!=view
->screen
->modalView
625 && !toplevel
->flags
.worksWhenModal
) {
626 if (event
->type
== KeyPress
|| event
->type
== KeyRelease
627 || event
->type
== MotionNotify
|| event
->type
== ButtonPress
628 || event
->type
== ButtonRelease
629 || event
->type
== FocusIn
|| event
->type
== FocusOut
) {
634 /* do balloon stuffs */
635 if (event
->type
== EnterNotify
)
636 W_BalloonHandleEnterView(view
);
637 else if (event
->type
== LeaveNotify
)
638 W_BalloonHandleLeaveView(view
);
640 /* This is a hack. It will make the panel be secure while
641 * the event handlers are handled, as some event handler
642 * might destroy the widget. */
643 W_RetainView(toplevel
);
645 hPtr
= view
->handlerList
;
650 tmp
= hPtr
->nextHandler
;
652 if ((hPtr
->eventMask
& mask
)) {
653 (*hPtr
->proc
)(event
, hPtr
->clientData
);
659 /* pass the event to the top level window of the widget */
660 if (view
->parent
!= NULL
) {
662 while (vPtr
->parent
!= NULL
)
665 hPtr
= vPtr
->handlerList
;
667 while (hPtr
!= NULL
) {
669 if (hPtr
->eventMask
& mask
) {
670 (*hPtr
->proc
)(event
, hPtr
->clientData
);
672 hPtr
= hPtr
->nextHandler
;
676 /* save button click info to track double-clicks */
677 if (view
->screen
->ignoreNextDoubleClick
) {
678 view
->screen
->ignoreNextDoubleClick
= 0;
680 if (event
->type
== ButtonPress
) {
681 view
->screen
->lastClickWindow
= event
->xbutton
.window
;
682 view
->screen
->lastClickTime
= event
->xbutton
.time
;
686 W_ReleaseView(toplevel
);
693 WMIsDoubleClick(XEvent
*event
)
697 if (event
->type
!= ButtonPress
)
700 view
= W_GetViewForXWindow(event
->xany
.display
, event
->xbutton
.window
);
705 if (view
->screen
->lastClickWindow
!= event
->xbutton
.window
)
708 if (event
->xbutton
.time
- view
->screen
->lastClickTime
709 < WINGsConfiguration
.doubleClickDelay
) {
710 view
->screen
->lastClickTime
= 0;
711 view
->screen
->lastClickWindow
= None
;
712 view
->screen
->ignoreNextDoubleClick
= 1;
720 W_WaitForEvent(Display
*dpy
, unsigned long xeventmask
)
722 #if defined(HAVE_POLL) && defined(HAVE_POLL_H) && !defined(HAVE_SELECT)
724 InputHandler
*handler
;
725 int count
, timeout
, nfds
, i
, retval
;
728 nfds
= WMGetBagItemCount(inputHandler
);
732 fds
= wmalloc(nfds
+1 * sizeof(struct pollfd
));
733 /* put this to the end of array to avoid using ranges from 1 to nfds+1 */
734 fds
[nfds
].fd
= ConnectionNumber(dpy
);
735 fds
[nfds
].events
= POLLIN
;
737 for (i
= 0; i
<nfds
; i
++) {
738 handler
= WMGetFromBag(inputHandler
, i
);
739 fds
[i
].fd
= handler
->fd
;
741 if (handler
->mask
& WIReadMask
)
742 fds
[i
].events
|= POLLIN
;
744 if (handler
->mask
& WIWriteMask
)
745 fds
[i
].events
|= POLLOUT
;
748 if (handler
->mask
& WIExceptMask
)
749 FD_SET(handler
->fd
, &eset
);
754 * Setup the select() timeout to the estimated time until the
755 * next timer expires.
757 if (timerPending()) {
759 delayUntilNextTimerEvent(&tv
);
760 timeout
= tv
.tv_sec
* 1000 + tv
.tv_usec
/ 1000;
770 if (XCheckMaskEvent(dpy
, xeventmask
, &ev
)) {
771 XPutBackEvent(dpy
, &ev
);
776 count
= poll(fds
, nfds
, timeout
);
778 if (count
>0 && nfds
>0) {
779 WMBag
*handlerCopy
= WMCreateBag(nfds
);
781 for (i
=0; i
<nfds
; i
++)
782 WMPutInBag(handlerCopy
, WMGetFromBag(inputHandler
, i
));
784 for (i
=0; i
<nfds
; i
++) {
787 handler
= WMGetFromBag(handlerCopy
, i
);
788 /* check if the handler still exist or was removed by a callback */
789 if (WMGetFirstInBag(inputHandler
, handler
) == WBNotFound
)
794 if ((handler
->mask
& WIReadMask
) &&
795 (fds
[i
].revents
& (POLLIN
|POLLRDNORM
|POLLRDBAND
|POLLPRI
)))
798 if ((handler
->mask
& WIWriteMask
) &&
799 (fds
[i
].revents
& (POLLOUT
| POLLWRBAND
)))
802 if ((handler
->mask
& WIExceptMask
) &&
803 (fds
[i
].revents
& (POLLHUP
| POLLNVAL
| POLLERR
)))
804 mask
|= WIExceptMask
;
806 if (mask
!=0 && handler
->callback
) {
807 (*handler
->callback
)(handler
->fd
, mask
,
808 handler
->clientData
);
812 WMFreeBag(handlerCopy
);
815 retval
= fds
[nfds
].revents
& (POLLIN
|POLLRDNORM
|POLLRDBAND
|POLLPRI
);
818 W_FlushASAPNotificationQueue();
821 #else /* not HAVE_POLL */
823 struct timeval timeout
;
824 struct timeval
*timeoutPtr
;
825 fd_set rset
, wset
, eset
;
828 InputHandler
*handler
;
834 FD_SET(ConnectionNumber(dpy
), &rset
);
835 maxfd
= ConnectionNumber(dpy
);
838 nfds
= WMGetBagItemCount(inputHandler
);
842 for (i
=0; i
<nfds
; i
++) {
843 handler
= WMGetFromBag(inputHandler
, i
);
844 if (handler
->mask
& WIReadMask
)
845 FD_SET(handler
->fd
, &rset
);
847 if (handler
->mask
& WIWriteMask
)
848 FD_SET(handler
->fd
, &wset
);
850 if (handler
->mask
& WIExceptMask
)
851 FD_SET(handler
->fd
, &eset
);
853 if (maxfd
< handler
->fd
)
859 * Setup the select() timeout to the estimated time until the
860 * next timer expires.
862 if (timerPending()) {
863 delayUntilNextTimerEvent(&timeout
);
864 timeoutPtr
= &timeout
;
866 timeoutPtr
= (struct timeval
*)0;
875 if (XCheckMaskEvent(dpy
, xeventmask
, &ev
)) {
876 XPutBackEvent(dpy
, &ev
);
881 count
= select(1 + maxfd
, &rset
, &wset
, &eset
, timeoutPtr
);
883 if (count
>0 && nfds
>0) {
884 WMBag
*handlerCopy
= WMCreateBag(nfds
);
886 for (i
=0; i
<nfds
; i
++)
887 WMPutInBag(handlerCopy
, WMGetFromBag(inputHandler
, i
));
889 for (i
=0; i
<nfds
; i
++) {
892 handler
= WMGetFromBag(handlerCopy
, i
);
893 /* check if the handler still exist or was removed by a callback */
894 if (WMGetFirstInBag(inputHandler
, handler
) == WBNotFound
)
899 if ((handler
->mask
& WIReadMask
) && FD_ISSET(handler
->fd
, &rset
))
902 if ((handler
->mask
& WIWriteMask
) && FD_ISSET(handler
->fd
, &wset
))
905 if ((handler
->mask
& WIExceptMask
) && FD_ISSET(handler
->fd
, &eset
))
906 mask
|= WIExceptMask
;
908 if (mask
!=0 && handler
->callback
) {
909 (*handler
->callback
)(handler
->fd
, mask
,
910 handler
->clientData
);
914 WMFreeBag(handlerCopy
);
917 W_FlushASAPNotificationQueue();
919 return FD_ISSET(ConnectionNumber(dpy
), &rset
);
920 #else /* not HAVE_SELECT, not HAVE_POLL */
921 Neither select nor poll
. You lose
.
922 #endif /* HAVE_SELECT */
923 #endif /* HAVE_POLL */
928 WMNextEvent(Display
*dpy
, XEvent
*event
)
930 /* Check any expired timers */
931 checkTimerHandlers();
933 while (XPending(dpy
) == 0) {
935 /* Do idle and timer stuff while there are no timer or X events */
936 while (!XPending(dpy
) && checkIdleHandlers()) {
937 /* dispatch timer events */
938 checkTimerHandlers();
942 * Make sure that new events did not arrive while we were doing
943 * timer/idle stuff. Or we might block forever waiting for
944 * an event that already arrived.
946 /* wait to something happen */
947 W_WaitForEvent(dpy
, 0);
949 /* Check any expired timers */
950 checkTimerHandlers();
953 XNextEvent(dpy
, event
);
958 WMMaskEvent(Display
*dpy
, long mask
, XEvent
*event
)
960 unsigned long milliseconds
;
961 struct timeval timeout
;
962 struct timeval
*timeoutOrInfty
;
965 while (!XCheckMaskEvent(dpy
, mask
, event
)) {
966 /* Do idle stuff while there are no timer or X events */
967 while (checkIdleHandlers()) {
968 if (XCheckMaskEvent(dpy
, mask
, event
))
973 * Setup the select() timeout to the estimated time until the
974 * next timer expires.
976 if (timerPending()) {
977 delayUntilNextTimerEvent(&timeout
);
978 timeoutOrInfty
= &timeout
;
980 timeoutOrInfty
= (struct timeval
*)0;
983 if (XCheckMaskEvent(dpy
, mask
, event
))
986 /* Wait for input on the X connection socket */
988 FD_SET(ConnectionNumber(dpy
), &readset
);
989 select(1 + ConnectionNumber(dpy
), &readset
, (fd_set
*)0, (fd_set
*)0,
992 /* Check any expired timers */
993 checkTimerHandlers();
999 * Cant use this because XPending() will make W_WaitForEvent
1000 * return even if the event in the queue is not what we want,
1001 * and if we block until some new event arrives from the
1002 * server, other events already in the queue (like Expose)
1006 WMMaskEvent(Display
*dpy
, long mask
, XEvent
*event
)
1008 while (!XCheckMaskEvent(dpy
, mask
, event
)) {
1009 /* Do idle stuff while there are no timer or X events */
1010 while (checkIdleHandlers()) {
1011 if (XCheckMaskEvent(dpy
, mask
, event
))
1015 /* Wait for input on the X connection socket */
1016 W_WaitForEvent(dpy
, mask
);
1018 /* Check any expired timers */
1019 checkTimerHandlers();
1025 WMScreenPending(WMScreen
*scr
)
1027 if (XPending(scr
->display
))
1035 WMHookEventHandler(WMEventHook
*handler
)
1037 WMEventHook
*oldHandler
= extraEventHandler
;
1039 extraEventHandler
= handler
;