3 * This event handling stuff was inspired on Tk.
8 /* table to map event types to event masks */
9 static const unsigned long eventMasks
[] = {
12 KeyPressMask
, /* KeyPress */
13 KeyReleaseMask
, /* KeyRelease */
14 ButtonPressMask
, /* ButtonPress */
15 ButtonReleaseMask
, /* ButtonRelease */
16 PointerMotionMask
| PointerMotionHintMask
| ButtonMotionMask
17 | Button1MotionMask
| Button2MotionMask
| Button3MotionMask
| Button4MotionMask
| Button5MotionMask
,
19 EnterWindowMask
, /* EnterNotify */
20 LeaveWindowMask
, /* LeaveNotify */
21 FocusChangeMask
, /* FocusIn */
22 FocusChangeMask
, /* FocusOut */
23 KeymapStateMask
, /* KeymapNotify */
24 ExposureMask
, /* Expose */
25 ExposureMask
, /* GraphicsExpose */
26 ExposureMask
, /* NoExpose */
27 VisibilityChangeMask
, /* VisibilityNotify */
28 SubstructureNotifyMask
, /* CreateNotify */
29 StructureNotifyMask
, /* DestroyNotify */
30 StructureNotifyMask
, /* UnmapNotify */
31 StructureNotifyMask
, /* MapNotify */
32 SubstructureRedirectMask
, /* MapRequest */
33 StructureNotifyMask
, /* ReparentNotify */
34 StructureNotifyMask
, /* ConfigureNotify */
35 SubstructureRedirectMask
, /* ConfigureRequest */
36 StructureNotifyMask
, /* GravityNotify */
37 ResizeRedirectMask
, /* ResizeRequest */
38 StructureNotifyMask
, /* CirculateNotify */
39 SubstructureRedirectMask
, /* CirculateRequest */
40 PropertyChangeMask
, /* PropertyNotify */
41 0, /* SelectionClear */
42 0, /* SelectionRequest */
43 0, /* SelectionNotify */
44 ColormapChangeMask
, /* ColormapNotify */
45 ClientMessageMask
, /* ClientMessage */
46 0, /* Mapping Notify */
49 /* hook for other toolkits or wmaker process their events */
50 static WMEventHook
*extraEventHandler
= NULL
;
53 * WMCreateEventHandler--
54 * Create an event handler and put it in the event handler list for the
55 * view. If the same callback and clientdata are already used in another
56 * handler, the masks are OR'ed.
59 void WMCreateEventHandler(WMView
* view
, unsigned long mask
, WMEventProc
* eventProc
, void *clientData
)
64 WM_ITERATE_ARRAY(view
->eventHandlers
, hPtr
, iter
) {
65 if (hPtr
->clientData
== clientData
&& hPtr
->proc
== eventProc
) {
66 hPtr
->eventMask
|= mask
;
71 hPtr
= wmalloc(sizeof(W_EventHandler
));
73 /* select events for window */
74 hPtr
->eventMask
= mask
;
75 hPtr
->proc
= eventProc
;
76 hPtr
->clientData
= clientData
;
78 WMAddToArray(view
->eventHandlers
, hPtr
);
81 static int matchHandler(const void *item
, const void *cdata
)
83 const W_EventHandler
*h1
= item
;
84 const W_EventHandler
*h2
= cdata
;
86 return ((h1
->eventMask
== h2
->eventMask
) &&
87 (h1
->proc
== h2
->proc
) &&
88 (h1
->clientData
== h2
->clientData
));
92 * WMDeleteEventHandler--
93 * Delete event handler matching arguments from windows
97 void WMDeleteEventHandler(WMView
* view
, unsigned long mask
, WMEventProc
* eventProc
, void *clientData
)
101 tmp
.eventMask
= mask
;
102 tmp
.proc
= eventProc
;
103 tmp
.clientData
= clientData
;
104 WMRemoveFromArrayMatching(view
->eventHandlers
, matchHandler
, (void *)&tmp
);
107 static Time
getEventTime(WMScreen
* screen
, XEvent
* event
)
109 switch (event
->type
) {
112 return event
->xbutton
.time
;
115 return event
->xkey
.time
;
117 return event
->xmotion
.time
;
120 return event
->xcrossing
.time
;
122 return event
->xproperty
.time
;
124 return event
->xselectionclear
.time
;
125 case SelectionRequest
:
126 return event
->xselectionrequest
.time
;
127 case SelectionNotify
:
128 return event
->xselection
.time
;
130 return screen
->lastEventTime
;
134 void W_CallDestroyHandlers(W_View
* view
)
137 WMArrayIterator iter
;
138 W_EventHandler
*hPtr
;
140 event
.type
= DestroyNotify
;
141 event
.xdestroywindow
.window
= view
->window
;
142 event
.xdestroywindow
.event
= view
->window
;
144 WM_ITERATE_ARRAY(view
->eventHandlers
, hPtr
, iter
) {
145 if (hPtr
->eventMask
& StructureNotifyMask
) {
146 (*hPtr
->proc
) (&event
, hPtr
->clientData
);
151 void WMSetViewNextResponder(WMView
* view
, WMView
* responder
)
153 /* set the widget to receive keyboard events that aren't handled
156 view
->nextResponder
= responder
;
159 void WMRelayToNextResponder(WMView
* view
, XEvent
* event
)
161 unsigned long mask
= eventMasks
[event
->xany
.type
];
163 if (view
->nextResponder
) {
164 WMView
*next
= view
->nextResponder
;
165 W_EventHandler
*hPtr
;
166 WMArrayIterator iter
;
168 WM_ITERATE_ARRAY(next
->eventHandlers
, hPtr
, iter
) {
169 if ((hPtr
->eventMask
& mask
)) {
170 (*hPtr
->proc
) (event
, hPtr
->clientData
);
176 int WMHandleEvent(XEvent
* event
)
178 W_EventHandler
*hPtr
;
179 W_View
*view
, *toplevel
;
182 WMArrayIterator iter
;
184 if (event
->type
== MappingNotify
) {
185 XRefreshKeyboardMapping(&event
->xmapping
);
189 if (XFilterEvent(event
, None
) == True
) {
193 mask
= eventMasks
[event
->xany
.type
];
195 window
= event
->xany
.window
;
197 /* diferentiate SubstructureNotify with StructureNotify */
198 if (mask
== StructureNotifyMask
) {
199 if (event
->xmap
.event
!= event
->xmap
.window
) {
200 mask
= SubstructureNotifyMask
;
201 window
= event
->xmap
.event
;
204 view
= W_GetViewForXWindow(event
->xany
.display
, window
);
207 if (extraEventHandler
)
208 (extraEventHandler
) (event
);
213 view
->screen
->lastEventTime
= getEventTime(view
->screen
, event
);
215 toplevel
= W_TopLevelOfView(view
);
217 if (event
->type
== SelectionNotify
|| event
->type
== SelectionClear
|| event
->type
== SelectionRequest
) {
218 /* handle selection related events */
219 W_HandleSelectionEvent(event
);
223 /* if it's a key event, redispatch it to the focused control */
224 if (mask
& (KeyPressMask
| KeyReleaseMask
)) {
225 W_View
*focused
= W_FocusedViewOfToplevel(toplevel
);
232 /* compress Motion events */
233 if (event
->type
== MotionNotify
&& !view
->flags
.dontCompressMotion
) {
234 while (XPending(event
->xmotion
.display
)) {
236 XPeekEvent(event
->xmotion
.display
, &ev
);
237 if (ev
.type
== MotionNotify
238 && event
->xmotion
.window
== ev
.xmotion
.window
239 && event
->xmotion
.subwindow
== ev
.xmotion
.subwindow
) {
241 XNextEvent(event
->xmotion
.display
, event
);
247 /* compress expose events */
248 if (event
->type
== Expose
&& !view
->flags
.dontCompressExpose
) {
249 while (XCheckTypedWindowEvent(event
->xexpose
.display
, view
->window
, Expose
, event
)) ;
252 if (view
->screen
->modalLoop
&& toplevel
!= view
->screen
->modalView
&& !toplevel
->flags
.worksWhenModal
) {
253 if (event
->type
== KeyPress
|| event
->type
== KeyRelease
254 || event
->type
== MotionNotify
|| event
->type
== ButtonPress
255 || event
->type
== ButtonRelease
|| event
->type
== FocusIn
|| event
->type
== FocusOut
) {
260 /* do balloon stuffs */
261 if (event
->type
== EnterNotify
)
262 W_BalloonHandleEnterView(view
);
263 else if (event
->type
== LeaveNotify
)
264 W_BalloonHandleLeaveView(view
);
266 /* This is a hack. It will make the panel be secure while
267 * the event handlers are handled, as some event handler
268 * might destroy the widget. */
269 W_RetainView(toplevel
);
271 WM_ITERATE_ARRAY(view
->eventHandlers
, hPtr
, iter
) {
272 if ((hPtr
->eventMask
& mask
)) {
273 (*hPtr
->proc
) (event
, hPtr
->clientData
);
277 /* pass the event to the top level window of the widget */
278 /* TODO: change this to a responder chain */
279 if (view
->parent
!= NULL
) {
281 while (vPtr
->parent
!= NULL
)
284 WM_ITERATE_ARRAY(vPtr
->eventHandlers
, hPtr
, iter
) {
285 if (hPtr
->eventMask
& mask
) {
286 (*hPtr
->proc
) (event
, hPtr
->clientData
);
291 /* save button click info to track double-clicks */
292 if (view
->screen
->ignoreNextDoubleClick
) {
293 view
->screen
->ignoreNextDoubleClick
= 0;
295 if (event
->type
== ButtonPress
) {
296 view
->screen
->lastClickWindow
= event
->xbutton
.window
;
297 view
->screen
->lastClickTime
= event
->xbutton
.time
;
301 if (event
->type
== ClientMessage
) {
302 /* must be handled at the end, for such message can destroy the view */
303 W_HandleDNDClientMessage(toplevel
, &event
->xclient
);
306 W_ReleaseView(toplevel
);
311 int WMIsDoubleClick(XEvent
* event
)
315 if (event
->type
!= ButtonPress
)
318 view
= W_GetViewForXWindow(event
->xany
.display
, event
->xbutton
.window
);
323 if (view
->screen
->lastClickWindow
!= event
->xbutton
.window
)
326 if (event
->xbutton
.time
- view
->screen
->lastClickTime
< WINGsConfiguration
.doubleClickDelay
) {
327 view
->screen
->lastClickTime
= 0;
328 view
->screen
->lastClickWindow
= None
;
329 view
->screen
->ignoreNextDoubleClick
= 1;
336 * Check for X and input events. If X events are present input events will
339 * Return value: True if a X event is available or any input event was
340 * processed, false otherwise (including return because of
341 * some timer handler expired).
343 * If waitForInput is False, it will just peek for available input and return
344 * without processing. Return vaue will be True if input is available.
346 * If waitForInput is True, it will wait until an input event arrives on the
347 * registered input handlers and ConnectionNumber(dpy), or will return when
348 * a timer handler expires if no input event arrived until then.
350 static Bool
waitForEvent(Display
* dpy
, unsigned long xeventmask
, Bool waitForInput
)
353 if (xeventmask
== 0) {
358 if (XCheckMaskEvent(dpy
, xeventmask
, &ev
)) {
359 XPutBackEvent(dpy
, &ev
);
364 return W_HandleInputEvents(waitForInput
, ConnectionNumber(dpy
));
367 void WMNextEvent(Display
* dpy
, XEvent
* event
)
369 /* Check any expired timers */
370 W_CheckTimerHandlers();
372 while (XPending(dpy
) == 0) {
373 /* Do idle and timer stuff while there are no input or X events */
374 while (!waitForEvent(dpy
, 0, False
) && W_CheckIdleHandlers()) {
375 /* dispatch timer events */
376 W_CheckTimerHandlers();
380 * Make sure that new events did not arrive while we were doing
381 * timer/idle stuff. Or we might block forever waiting for
382 * an event that already arrived.
384 /* wait for something to happen or a timer to expire */
385 waitForEvent(dpy
, 0, True
);
387 /* Check any expired timers */
388 W_CheckTimerHandlers();
391 XNextEvent(dpy
, event
);
394 void WMMaskEvent(Display
* dpy
, long mask
, XEvent
* event
)
396 /* Check any expired timers */
397 W_CheckTimerHandlers();
399 while (!XCheckMaskEvent(dpy
, mask
, event
)) {
400 /* Do idle and timer stuff while there are no input or X events */
401 while (!waitForEvent(dpy
, mask
, False
) && W_CheckIdleHandlers()) {
402 W_CheckTimerHandlers();
405 if (XCheckMaskEvent(dpy
, mask
, event
))
408 /* Wait for input on the X connection socket or another input handler */
409 waitForEvent(dpy
, mask
, True
);
411 /* Check any expired timers */
412 W_CheckTimerHandlers();
416 Bool
WMScreenPending(WMScreen
* scr
)
418 if (XPending(scr
->display
))
424 WMEventHook
*WMHookEventHandler(WMEventHook
* handler
)
426 WMEventHook
*oldHandler
= extraEventHandler
;
428 extraEventHandler
= handler
;