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
{
47 struct IdleHandler
*next
;
51 typedef struct InputHandler
{
52 WMInputProc
*callback
;
56 struct InputHandler
*next
;
60 /* table to map event types to event masks */
61 static unsigned long eventMasks
[] = {
64 KeyPressMask
, /* KeyPress */
65 KeyReleaseMask
, /* KeyRelease */
66 ButtonPressMask
, /* ButtonPress */
67 ButtonReleaseMask
, /* ButtonRelease */
68 PointerMotionMask
|PointerMotionHintMask
|ButtonMotionMask
69 |Button1MotionMask
|Button2MotionMask
|Button3MotionMask
70 |Button4MotionMask
|Button5MotionMask
,
72 EnterWindowMask
, /* EnterNotify */
73 LeaveWindowMask
, /* LeaveNotify */
74 FocusChangeMask
, /* FocusIn */
75 FocusChangeMask
, /* FocusOut */
76 KeymapStateMask
, /* KeymapNotify */
77 ExposureMask
, /* Expose */
78 ExposureMask
, /* GraphicsExpose */
79 ExposureMask
, /* NoExpose */
80 VisibilityChangeMask
, /* VisibilityNotify */
81 SubstructureNotifyMask
, /* CreateNotify */
82 StructureNotifyMask
, /* DestroyNotify */
83 StructureNotifyMask
, /* UnmapNotify */
84 StructureNotifyMask
, /* MapNotify */
85 SubstructureRedirectMask
, /* MapRequest */
86 StructureNotifyMask
, /* ReparentNotify */
87 StructureNotifyMask
, /* ConfigureNotify */
88 SubstructureRedirectMask
, /* ConfigureRequest */
89 StructureNotifyMask
, /* GravityNotify */
90 ResizeRedirectMask
, /* ResizeRequest */
91 StructureNotifyMask
, /* CirculateNotify */
92 SubstructureRedirectMask
, /* CirculateRequest */
93 PropertyChangeMask
, /* PropertyNotify */
94 0, /* SelectionClear */
95 0, /* SelectionRequest */
96 0, /* SelectionNotify */
97 ColormapChangeMask
, /* ColormapNotify */
98 ClientMessageMask
, /* ClientMessage */
99 0, /* Mapping Notify */
104 /* queue of timer event handlers */
105 static TimerHandler
*timerHandler
=NULL
;
107 static IdleHandler
*idleHandler
=NULL
;
109 static InputHandler
*inputHandler
=NULL
;
111 /* hook for other toolkits or wmaker process their events */
112 static WMEventHook
*extraEventHandler
=NULL
;
116 #define timerPending() (timerHandler)
121 rightNow(struct timeval
*tv
) {
125 /* is t1 after t2 ? */
126 #define IS_AFTER(t1, t2) (((t1).tv_sec > (t2).tv_sec) || \
127 (((t1).tv_sec == (t2).tv_sec) \
128 && ((t1).tv_usec > (t2).tv_usec)))
132 addmillisecs(struct timeval
*tv
, int milliseconds
)
134 tv
->tv_usec
+= milliseconds
*1000;
136 tv
->tv_sec
+= tv
->tv_usec
/1000000;
137 tv
->tv_usec
= tv
->tv_usec
%1000000;
142 WMAddTimerHandler(int milliseconds
, WMCallback
*callback
, void *cdata
)
144 TimerHandler
*handler
, *tmp
;
146 handler
= malloc(sizeof(TimerHandler
));
150 rightNow(&handler
->when
);
151 addmillisecs(&handler
->when
, milliseconds
);
152 handler
->callback
= callback
;
153 handler
->clientData
= cdata
;
154 /* insert callback in queue, sorted by time left */
155 if (!timerHandler
|| !IS_AFTER(handler
->when
, timerHandler
->when
)) {
156 /* first in the queue */
157 handler
->next
= timerHandler
;
158 timerHandler
= handler
;
161 while (tmp
->next
&& IS_AFTER(handler
->when
, tmp
->next
->when
)) {
164 handler
->next
= tmp
->next
;
173 WMDeleteTimerWithClientData(void *cdata
)
175 TimerHandler
*handler
, *tmp
;
177 if (!cdata
|| !timerHandler
)
181 if (tmp
->clientData
==cdata
) {
182 timerHandler
= tmp
->next
;
186 if (tmp
->next
->clientData
==cdata
) {
188 tmp
->next
= handler
->next
;
200 WMDeleteTimerHandler(WMHandlerID handlerID
)
202 TimerHandler
*tmp
, *handler
=(TimerHandler
*)handlerID
;
204 if (!handler
|| !timerHandler
)
209 timerHandler
= handler
->next
;
213 if (tmp
->next
==handler
) {
214 tmp
->next
=handler
->next
;
226 WMAddIdleHandler(WMCallback
*callback
, void *cdata
)
228 IdleHandler
*handler
, *tmp
;
230 handler
= malloc(sizeof(IdleHandler
));
234 handler
->callback
= callback
;
235 handler
->clientData
= cdata
;
236 handler
->next
= NULL
;
237 /* add callback at end of queue */
239 idleHandler
= handler
;
254 WMDeleteIdleHandler(WMHandlerID handlerID
)
256 IdleHandler
*tmp
, *handler
= (IdleHandler
*)handlerID
;
258 if (!handler
|| !idleHandler
)
262 if (tmp
== handler
) {
263 idleHandler
= handler
->next
;
267 if (tmp
->next
== handler
) {
268 tmp
->next
= handler
->next
;
280 WMAddInputHandler(int fd
, int condition
, WMInputProc
*proc
, void *clientData
)
282 InputHandler
*handler
;
284 handler
= wmalloc(sizeof(InputHandler
));
287 handler
->mask
= condition
;
288 handler
->callback
= proc
;
289 handler
->clientData
= clientData
;
291 handler
->next
= inputHandler
;
293 inputHandler
= handler
;
300 WMDeleteInputHandler(WMHandlerID handlerID
)
302 InputHandler
*tmp
, *handler
= (InputHandler
*)handlerID
;
304 if (!handler
|| !inputHandler
)
308 if (tmp
== handler
) {
309 inputHandler
= handler
->next
;
313 if (tmp
->next
== handler
) {
314 tmp
->next
= handler
->next
;
327 IdleHandler
*handler
, *tmp
;
330 W_FlushIdleNotificationQueue();
331 /* make sure an observer in queue didn't added an idle handler */
332 return (idleHandler
!=NULL
);
335 handler
= idleHandler
;
337 /* we will process all idleHandlers so, empty the handler list */
342 (*handler
->callback
)(handler
->clientData
);
343 /* remove the handler */
349 W_FlushIdleNotificationQueue();
351 /* this is not necesarrily False, because one handler can re-add itself */
352 return (idleHandler
!=NULL
);
360 TimerHandler
*handler
;
364 W_FlushASAPNotificationQueue();
370 while (timerHandler
&& IS_AFTER(now
, timerHandler
->when
)) {
371 handler
= timerHandler
;
372 timerHandler
= timerHandler
->next
;
373 handler
->next
= NULL
;
374 (*handler
->callback
)(handler
->clientData
);
378 W_FlushASAPNotificationQueue();
384 delayUntilNextTimerEvent(struct timeval
*delay
)
389 /* The return value of this function is only valid if there _are_
397 if (IS_AFTER(now
, timerHandler
->when
)) {
401 delay
->tv_sec
= timerHandler
->when
.tv_sec
- now
.tv_sec
;
402 delay
->tv_usec
= timerHandler
->when
.tv_usec
- now
.tv_usec
;
403 if (delay
->tv_usec
< 0) {
404 delay
->tv_usec
+= 1000000;
414 * WMCreateEventHandler--
415 * Create an event handler and put it in the event handler list for the
416 * view. If the same callback and clientdata are already used in another
417 * handler, the masks are swapped.
421 WMCreateEventHandler(WMView
*view
, unsigned long mask
, WMEventProc
*eventProc
,
424 W_EventHandler
*handler
;
425 W_EventHandler
*ptr
= view
->handlerList
;
426 unsigned long eventMask
;
429 handler
= wmalloc(sizeof(W_EventHandler
));
431 handler
->nextHandler
= NULL
;
433 view
->handlerList
= handler
;
439 while (ptr
!= NULL
) {
440 if (ptr
->clientData
== clientData
&& ptr
->proc
== eventProc
) {
443 eventMask
|= ptr
->eventMask
;
445 ptr
= ptr
->nextHandler
;
448 handler
= wmalloc(sizeof(W_EventHandler
));
449 handler
->nextHandler
= view
->handlerList
;
450 view
->handlerList
= handler
;
454 /* select events for window */
455 handler
->eventMask
= mask
;
456 handler
->proc
= eventProc
;
457 handler
->clientData
= clientData
;
462 * WMDeleteEventHandler--
463 * Delete event handler matching arguments from windows
464 * event handler list.
468 WMDeleteEventHandler(WMView
*view
, unsigned long mask
, WMEventProc
*eventProc
,
471 W_EventHandler
*handler
, *ptr
, *pptr
;
473 ptr
= view
->handlerList
;
479 if (ptr
->eventMask
== mask
&& ptr
->proc
== eventProc
480 && ptr
->clientData
== clientData
) {
485 ptr
= ptr
->nextHandler
;
492 view
->handlerList
= handler
->nextHandler
;
494 pptr
->nextHandler
= handler
->nextHandler
;
502 W_CleanUpEvents(WMView
*view
)
504 W_EventHandler
*ptr
, *nptr
;
506 ptr
= view
->handlerList
;
509 nptr
= ptr
->nextHandler
;
518 getEventTime(WMScreen
*screen
, XEvent
*event
)
520 switch (event
->type
) {
523 return event
->xbutton
.time
;
526 return event
->xkey
.time
;
528 return event
->xmotion
.time
;
531 return event
->xcrossing
.time
;
533 return event
->xproperty
.time
;
535 return event
->xselectionclear
.time
;
536 case SelectionRequest
:
537 return event
->xselectionrequest
.time
;
538 case SelectionNotify
:
539 return event
->xselection
.time
;
541 return screen
->lastEventTime
;
547 W_CallDestroyHandlers(W_View
*view
)
550 W_EventHandler
*hPtr
;
552 event
.type
= DestroyNotify
;
553 event
.xdestroywindow
.window
= view
->window
;
554 event
.xdestroywindow
.event
= view
->window
;
555 hPtr
= view
->handlerList
;
557 if (hPtr
->eventMask
& StructureNotifyMask
) {
558 (*hPtr
->proc
)(&event
, hPtr
->clientData
);
561 hPtr
= hPtr
->nextHandler
;
567 WMHandleEvent(XEvent
*event
)
569 W_EventHandler
*hPtr
;
570 W_View
*view
, *vPtr
, *toplevel
;
574 if (event
->type
== MappingNotify
) {
575 XRefreshKeyboardMapping(&event
->xmapping
);
579 mask
= eventMasks
[event
->xany
.type
];
581 window
= event
->xany
.window
;
583 /* diferentiate SubstructureNotify with StructureNotify */
584 if (mask
== StructureNotifyMask
) {
585 if (event
->xmap
.event
!= event
->xmap
.window
) {
586 mask
= SubstructureNotifyMask
;
587 window
= event
->xmap
.event
;
590 view
= W_GetViewForXWindow(event
->xany
.display
, window
);
592 if (extraEventHandler
)
593 (extraEventHandler
)(event
);
598 view
->screen
->lastEventTime
= getEventTime(view
->screen
, event
);
600 toplevel
= W_TopLevelOfView(view
);
602 if (event
->type
== SelectionNotify
|| event
->type
== SelectionClear
603 || event
->type
== SelectionRequest
) {
604 /* handle selection related events */
605 W_HandleSelectionEvent(event
);
608 /* if it's a key event, redispatch it to the focused control */
609 if (mask
& (KeyPressMask
|KeyReleaseMask
)) {
610 W_View
*focused
= W_FocusedViewOfToplevel(toplevel
);
617 /* compress Motion events */
618 if (event
->type
== MotionNotify
&& !view
->flags
.dontCompressMotion
) {
619 while (XPending(event
->xmotion
.display
)) {
621 XPeekEvent(event
->xmotion
.display
, &ev
);
622 if (ev
.type
== MotionNotify
623 && event
->xmotion
.window
== ev
.xmotion
.window
624 && event
->xmotion
.subwindow
== ev
.xmotion
.subwindow
) {
626 XNextEvent(event
->xmotion
.display
, event
);
631 /* compress expose events */
632 if (event
->type
== Expose
&& !view
->flags
.dontCompressExpose
) {
633 while (XCheckTypedWindowEvent(event
->xexpose
.display
, view
->window
,
638 if (view
->screen
->modal
&& toplevel
!=view
->screen
->modalView
639 && !toplevel
->flags
.worksWhenModal
) {
640 if (event
->type
== KeyPress
|| event
->type
== KeyRelease
641 || event
->type
== MotionNotify
|| event
->type
== ButtonPress
642 || event
->type
== ButtonRelease
643 || event
->type
== FocusIn
|| event
->type
== FocusOut
) {
648 /* do balloon stuffs */
649 if (event
->type
== EnterNotify
)
650 W_BalloonHandleEnterView(view
);
651 else if (event
->type
== LeaveNotify
)
652 W_BalloonHandleLeaveView(view
);
654 /* This is a hack. It will make the panel be secure while
655 * the event handlers are handled, as some event handler
656 * might destroy the widget. */
657 W_RetainView(toplevel
);
659 hPtr
= view
->handlerList
;
664 tmp
= hPtr
->nextHandler
;
666 if ((hPtr
->eventMask
& mask
)) {
667 (*hPtr
->proc
)(event
, hPtr
->clientData
);
673 /* pass the event to the top level window of the widget */
674 if (view
->parent
!=NULL
) {
676 while (vPtr
->parent
!=NULL
)
679 hPtr
= vPtr
->handlerList
;
683 if (hPtr
->eventMask
& mask
) {
684 (*hPtr
->proc
)(event
, hPtr
->clientData
);
686 hPtr
= hPtr
->nextHandler
;
690 /* save button click info to track double-clicks */
691 if (view
->screen
->ignoreNextDoubleClick
) {
692 view
->screen
->ignoreNextDoubleClick
= 0;
694 if (event
->type
== ButtonPress
) {
695 view
->screen
->lastClickWindow
= event
->xbutton
.window
;
696 view
->screen
->lastClickTime
= event
->xbutton
.time
;
700 W_ReleaseView(toplevel
);
707 WMIsDoubleClick(XEvent
*event
)
711 if (event
->type
!= ButtonPress
)
714 view
= W_GetViewForXWindow(event
->xany
.display
, event
->xbutton
.window
);
719 if (view
->screen
->lastClickWindow
!= event
->xbutton
.window
)
722 if (event
->xbutton
.time
- view
->screen
->lastClickTime
723 < WINGsConfiguration
.doubleClickDelay
) {
724 view
->screen
->lastClickTime
= 0;
725 view
->screen
->lastClickWindow
= None
;
726 view
->screen
->ignoreNextDoubleClick
= 1;
734 W_WaitForEvent(Display
*dpy
, unsigned long xeventmask
)
736 #if defined(HAVE_POLL) && defined(HAVE_POLL_H) && !defined(HAVE_SELECT)
738 InputHandler
*handler
;
739 int count
, timeout
, nfds
, k
, retval
;
741 for (nfds
= 1, handler
= inputHandler
;
742 handler
!= 0; handler
= handler
->next
) nfds
++;
744 fds
= wmalloc(nfds
* sizeof(struct pollfd
));
745 fds
[0].fd
= ConnectionNumber(dpy
);
746 fds
[0].events
= POLLIN
;
748 for (k
= 1, handler
= inputHandler
;
750 handler
= handler
->next
, k
++) {
751 fds
[k
].fd
= handler
->fd
;
753 if (handler
->mask
& WIReadMask
)
754 fds
[k
].events
|= POLLIN
;
756 if (handler
->mask
& WIWriteMask
)
757 fds
[k
].events
|= POLLOUT
;
760 if (handler
->mask
& WIExceptMask
)
761 FD_SET(handler
->fd
, &eset
);
766 * Setup the select() timeout to the estimated time until the
767 * next timer expires.
769 if (timerPending()) {
771 delayUntilNextTimerEvent(&tv
);
772 timeout
= tv
.tv_sec
* 1000 + tv
.tv_usec
/ 1000;
782 if (XCheckMaskEvent(dpy
, xeventmask
, &ev
)) {
783 XPutBackEvent(dpy
, &ev
);
788 count
= poll(fds
, nfds
, timeout
);
791 handler
= inputHandler
;
799 if ((handler
->mask
& WIReadMask
) &&
800 fds
[k
].revents
& (POLLIN
|POLLRDNORM
|POLLRDBAND
|POLLPRI
))
803 if ((handler
->mask
& WIWriteMask
) &&
804 fds
[k
].revents
& (POLLOUT
| POLLWRBAND
))
807 if ((handler
->mask
& WIExceptMask
) &&
808 fds
[k
].revents
& (POLLHUP
| POLLNVAL
| POLLERR
))
809 mask
|= WIExceptMask
;
811 next
= handler
->next
;
813 if (mask
!=0 && handler
->callback
) {
814 (*handler
->callback
)(handler
->fd
, mask
,
815 handler
->clientData
);
823 retval
= fds
[0].revents
& (POLLIN
|POLLRDNORM
|POLLRDBAND
|POLLPRI
);
826 W_FlushASAPNotificationQueue();
829 #else /* not HAVE_POLL */
831 struct timeval timeout
;
832 struct timeval
*timeoutPtr
;
833 fd_set rset
, wset
, eset
;
836 InputHandler
*handler
= inputHandler
;
842 FD_SET(ConnectionNumber(dpy
), &rset
);
843 maxfd
= ConnectionNumber(dpy
);
846 if (handler
->mask
& WIReadMask
)
847 FD_SET(handler
->fd
, &rset
);
849 if (handler
->mask
& WIWriteMask
)
850 FD_SET(handler
->fd
, &wset
);
852 if (handler
->mask
& WIExceptMask
)
853 FD_SET(handler
->fd
, &eset
);
855 if (maxfd
< handler
->fd
)
858 handler
= handler
->next
;
863 * Setup the select() timeout to the estimated time until the
864 * next timer expires.
866 if (timerPending()) {
867 delayUntilNextTimerEvent(&timeout
);
868 timeoutPtr
= &timeout
;
870 timeoutPtr
= (struct timeval
*)0;
879 if (XCheckMaskEvent(dpy
, xeventmask
, &ev
)) {
880 XPutBackEvent(dpy
, &ev
);
885 count
= select(1 + maxfd
, &rset
, &wset
, &eset
, timeoutPtr
);
888 handler
= inputHandler
;
896 if ((handler
->mask
& WIReadMask
) && FD_ISSET(handler
->fd
, &rset
))
899 if ((handler
->mask
& WIWriteMask
) && FD_ISSET(handler
->fd
, &wset
))
902 if ((handler
->mask
& WIExceptMask
) && FD_ISSET(handler
->fd
, &eset
))
903 mask
|= WIExceptMask
;
905 next
= handler
->next
;
907 if (mask
!=0 && handler
->callback
) {
908 (*handler
->callback
)(handler
->fd
, mask
,
909 handler
->clientData
);
916 W_FlushASAPNotificationQueue();
918 return FD_ISSET(ConnectionNumber(dpy
), &rset
);
919 #else /* not HAVE_SELECT, not HAVE_POLL */
920 Neither select nor poll
. You lose
.
921 #endif /* HAVE_SELECT */
922 #endif /* HAVE_POLL */
927 WMNextEvent(Display
*dpy
, XEvent
*event
)
929 /* Check any expired timers */
930 checkTimerHandlers();
932 while (XPending(dpy
) == 0) {
934 /* Do idle and timer stuff while there are no timer or X events */
935 while (!XPending(dpy
) && checkIdleHandlers()) {
936 /* dispatch timer events */
937 checkTimerHandlers();
941 * Make sure that new events did not arrive while we were doing
942 * timer/idle stuff. Or we might block forever waiting for
943 * an event that already arrived.
945 /* wait to something happen */
946 W_WaitForEvent(dpy
, 0);
948 /* Check any expired timers */
949 checkTimerHandlers();
952 XNextEvent(dpy
, event
);
957 WMMaskEvent(Display
*dpy
, long mask
, XEvent
*event
)
959 unsigned long milliseconds
;
960 struct timeval timeout
;
961 struct timeval
*timeoutOrInfty
;
964 while (!XCheckMaskEvent(dpy
, mask
, event
)) {
965 /* Do idle stuff while there are no timer or X events */
966 while (checkIdleHandlers()) {
967 if (XCheckMaskEvent(dpy
, mask
, event
))
972 * Setup the select() timeout to the estimated time until the
973 * next timer expires.
975 if (timerPending()) {
976 delayUntilNextTimerEvent(&timeout
);
977 timeoutOrInfty
= &timeout
;
979 timeoutOrInfty
= (struct timeval
*)0;
982 if (XCheckMaskEvent(dpy
, mask
, event
))
985 /* Wait for input on the X connection socket */
987 FD_SET(ConnectionNumber(dpy
), &readset
);
988 select(1 + ConnectionNumber(dpy
), &readset
, (fd_set
*)0, (fd_set
*)0,
991 /* Check any expired timers */
992 checkTimerHandlers();
998 * Cant use this because XPending() will make W_WaitForEvent
999 * return even if the event in the queue is not what we want,
1000 * and if we block until some new event arrives from the
1001 * server, other events already in the queue (like Expose)
1005 WMMaskEvent(Display
*dpy
, long mask
, XEvent
*event
)
1007 while (!XCheckMaskEvent(dpy
, mask
, event
)) {
1008 /* Do idle stuff while there are no timer or X events */
1009 while (checkIdleHandlers()) {
1010 if (XCheckMaskEvent(dpy
, mask
, event
))
1014 /* Wait for input on the X connection socket */
1015 W_WaitForEvent(dpy
, mask
);
1017 /* Check any expired timers */
1018 checkTimerHandlers();
1024 WMScreenPending(WMScreen
*scr
)
1026 if (XPending(scr
->display
))
1034 WMHookEventHandler(WMEventHook
*handler
)
1036 WMEventHook
*oldHandler
= extraEventHandler
;
1038 extraEventHandler
= handler
;