4 * This event handling stuff was inspired on Tk.
10 /* table to map event types to event masks */
11 static unsigned long eventMasks
[] = {
14 KeyPressMask
, /* KeyPress */
15 KeyReleaseMask
, /* KeyRelease */
16 ButtonPressMask
, /* ButtonPress */
17 ButtonReleaseMask
, /* ButtonRelease */
18 PointerMotionMask
|PointerMotionHintMask
|ButtonMotionMask
19 |Button1MotionMask
|Button2MotionMask
|Button3MotionMask
20 |Button4MotionMask
|Button5MotionMask
,
22 EnterWindowMask
, /* EnterNotify */
23 LeaveWindowMask
, /* LeaveNotify */
24 FocusChangeMask
, /* FocusIn */
25 FocusChangeMask
, /* FocusOut */
26 KeymapStateMask
, /* KeymapNotify */
27 ExposureMask
, /* Expose */
28 ExposureMask
, /* GraphicsExpose */
29 ExposureMask
, /* NoExpose */
30 VisibilityChangeMask
, /* VisibilityNotify */
31 SubstructureNotifyMask
, /* CreateNotify */
32 StructureNotifyMask
, /* DestroyNotify */
33 StructureNotifyMask
, /* UnmapNotify */
34 StructureNotifyMask
, /* MapNotify */
35 SubstructureRedirectMask
, /* MapRequest */
36 StructureNotifyMask
, /* ReparentNotify */
37 StructureNotifyMask
, /* ConfigureNotify */
38 SubstructureRedirectMask
, /* ConfigureRequest */
39 StructureNotifyMask
, /* GravityNotify */
40 ResizeRedirectMask
, /* ResizeRequest */
41 StructureNotifyMask
, /* CirculateNotify */
42 SubstructureRedirectMask
, /* CirculateRequest */
43 PropertyChangeMask
, /* PropertyNotify */
44 0, /* SelectionClear */
45 0, /* SelectionRequest */
46 0, /* SelectionNotify */
47 ColormapChangeMask
, /* ColormapNotify */
48 ClientMessageMask
, /* ClientMessage */
49 0, /* Mapping Notify */
54 /* hook for other toolkits or wmaker process their events */
55 static WMEventHook
*extraEventHandler
=NULL
;
62 * WMCreateEventHandler--
63 * Create an event handler and put it in the event handler list for the
64 * view. If the same callback and clientdata are already used in another
65 * handler, the masks are swapped.
69 WMCreateEventHandler(WMView
*view
, unsigned long mask
, WMEventProc
*eventProc
,
72 W_EventHandler
*handler
, *ptr
;
73 unsigned long eventMask
;
80 WM_ITERATE_BAG(view
->eventHandlers
, ptr
, iter
) {
81 if (ptr
->clientData
== clientData
&& ptr
->proc
== eventProc
) {
83 eventMask
|= ptr
->eventMask
;
87 handler
= wmalloc(sizeof(W_EventHandler
));
89 WMPutInBag(view
->eventHandlers
, handler
);
91 /* select events for window */
92 handler
->eventMask
= eventMask
;
93 handler
->proc
= eventProc
;
94 handler
->clientData
= clientData
;
99 * WMDeleteEventHandler--
100 * Delete event handler matching arguments from windows
101 * event handler list.
105 WMDeleteEventHandler(WMView
*view
, unsigned long mask
, WMEventProc
*eventProc
,
108 W_EventHandler
*handler
, *ptr
;
113 WM_ITERATE_BAG(view
->eventHandlers
, ptr
, iter
) {
114 if (ptr
->eventMask
== mask
&& ptr
->proc
== eventProc
115 && ptr
->clientData
== clientData
) {
124 WMRemoveFromBag(view
->eventHandlers
, handler
);
132 W_CleanUpEvents(WMView
*view
)
137 WM_ITERATE_BAG(view
->eventHandlers
, ptr
, iter
) {
145 getEventTime(WMScreen
*screen
, XEvent
*event
)
147 switch (event
->type
) {
150 return event
->xbutton
.time
;
153 return event
->xkey
.time
;
155 return event
->xmotion
.time
;
158 return event
->xcrossing
.time
;
160 return event
->xproperty
.time
;
162 return event
->xselectionclear
.time
;
163 case SelectionRequest
:
164 return event
->xselectionrequest
.time
;
165 case SelectionNotify
:
166 return event
->xselection
.time
;
168 return screen
->lastEventTime
;
174 W_CallDestroyHandlers(W_View
*view
)
178 W_EventHandler
*hPtr
;
180 event
.type
= DestroyNotify
;
181 event
.xdestroywindow
.window
= view
->window
;
182 event
.xdestroywindow
.event
= view
->window
;
184 WM_ITERATE_BAG(view
->eventHandlers
, hPtr
, iter
) {
185 if (hPtr
->eventMask
& StructureNotifyMask
) {
186 (*hPtr
->proc
)(&event
, hPtr
->clientData
);
194 WMSetViewNextResponder(WMView
*view
, WMView
*responder
)
196 /* set the widget to receive keyboard events that aren't handled
199 view
->nextResponder
= responder
;
204 WMRelayToNextResponder(WMView
*view
, XEvent
*event
)
206 unsigned long mask
= eventMasks
[event
->xany
.type
];
208 if (view
->nextResponder
) {
209 WMView
*next
= view
->nextResponder
;
210 W_EventHandler
*hPtr
;
213 WM_ITERATE_BAG(next
->eventHandlers
, hPtr
, iter
) {
214 if ((hPtr
->eventMask
& mask
)) {
215 (*hPtr
->proc
)(event
, hPtr
->clientData
);
223 WMHandleEvent(XEvent
*event
)
225 W_EventHandler
*hPtr
;
226 W_View
*view
, *vPtr
, *toplevel
;
231 if (event
->type
== MappingNotify
) {
232 XRefreshKeyboardMapping(&event
->xmapping
);
236 mask
= eventMasks
[event
->xany
.type
];
238 window
= event
->xany
.window
;
240 /* diferentiate SubstructureNotify with StructureNotify */
241 if (mask
== StructureNotifyMask
) {
242 if (event
->xmap
.event
!= event
->xmap
.window
) {
243 mask
= SubstructureNotifyMask
;
244 window
= event
->xmap
.event
;
247 view
= W_GetViewForXWindow(event
->xany
.display
, window
);
250 if (extraEventHandler
)
251 (extraEventHandler
)(event
);
256 view
->screen
->lastEventTime
= getEventTime(view
->screen
, event
);
258 toplevel
= W_TopLevelOfView(view
);
260 if (event
->type
== SelectionNotify
|| event
->type
== SelectionClear
261 || event
->type
== SelectionRequest
) {
262 /* handle selection related events */
263 W_HandleSelectionEvent(event
);
265 } else if (event
->type
== ClientMessage
) {
267 W_HandleDNDClientMessage(toplevel
, &event
->xclient
);
270 /* if it's a key event, redispatch it to the focused control */
271 if (mask
& (KeyPressMask
|KeyReleaseMask
)) {
272 W_View
*focused
= W_FocusedViewOfToplevel(toplevel
);
279 /* compress Motion events */
280 if (event
->type
== MotionNotify
&& !view
->flags
.dontCompressMotion
) {
281 while (XPending(event
->xmotion
.display
)) {
283 XPeekEvent(event
->xmotion
.display
, &ev
);
284 if (ev
.type
== MotionNotify
285 && event
->xmotion
.window
== ev
.xmotion
.window
286 && event
->xmotion
.subwindow
== ev
.xmotion
.subwindow
) {
288 XNextEvent(event
->xmotion
.display
, event
);
293 /* compress expose events */
294 if (event
->type
== Expose
&& !view
->flags
.dontCompressExpose
) {
295 while (XCheckTypedWindowEvent(event
->xexpose
.display
, view
->window
,
300 if (view
->screen
->modalLoop
&& toplevel
!=view
->screen
->modalView
301 && !toplevel
->flags
.worksWhenModal
) {
302 if (event
->type
== KeyPress
|| event
->type
== KeyRelease
303 || event
->type
== MotionNotify
|| event
->type
== ButtonPress
304 || event
->type
== ButtonRelease
305 || event
->type
== FocusIn
|| event
->type
== FocusOut
) {
310 /* do balloon stuffs */
311 if (event
->type
== EnterNotify
)
312 W_BalloonHandleEnterView(view
);
313 else if (event
->type
== LeaveNotify
)
314 W_BalloonHandleLeaveView(view
);
316 /* This is a hack. It will make the panel be secure while
317 * the event handlers are handled, as some event handler
318 * might destroy the widget. */
319 W_RetainView(toplevel
);
321 WM_ITERATE_BAG(view
->eventHandlers
, hPtr
, iter
) {
322 if ((hPtr
->eventMask
& mask
)) {
323 (*hPtr
->proc
)(event
, hPtr
->clientData
);
327 /* pass the event to the top level window of the widget */
328 /* TODO: change this to a responder chain */
329 if (view
->parent
!= NULL
) {
331 while (vPtr
->parent
!= NULL
)
334 WM_ITERATE_BAG(vPtr
->eventHandlers
, hPtr
, iter
) {
335 if (hPtr
->eventMask
& mask
) {
336 (*hPtr
->proc
)(event
, hPtr
->clientData
);
341 /* save button click info to track double-clicks */
342 if (view
->screen
->ignoreNextDoubleClick
) {
343 view
->screen
->ignoreNextDoubleClick
= 0;
345 if (event
->type
== ButtonPress
) {
346 view
->screen
->lastClickWindow
= event
->xbutton
.window
;
347 view
->screen
->lastClickTime
= event
->xbutton
.time
;
351 W_ReleaseView(toplevel
);
358 WMIsDoubleClick(XEvent
*event
)
362 if (event
->type
!= ButtonPress
)
365 view
= W_GetViewForXWindow(event
->xany
.display
, event
->xbutton
.window
);
370 if (view
->screen
->lastClickWindow
!= event
->xbutton
.window
)
373 if (event
->xbutton
.time
- view
->screen
->lastClickTime
374 < WINGsConfiguration
.doubleClickDelay
) {
375 view
->screen
->lastClickTime
= 0;
376 view
->screen
->lastClickWindow
= None
;
377 view
->screen
->ignoreNextDoubleClick
= 1;
385 * Check for X and input events. If X events are present input events will
388 * Return value: True if a X event is available or any input event was
389 * processed, false otherwise (including return because of
390 * some timer handler expired).
392 * If waitForInput is False, it will just peek for available input and return
393 * without processing. Return vaue will be True if input is available.
395 * If waitForInput is True, it will wait until an input event arrives on the
396 * registered input handlers and ConnectionNumber(dpy), or will return when
397 * a timer handler expires if no input event arrived until then.
400 waitForEvent(Display
*dpy
, unsigned long xeventmask
, Bool waitForInput
)
408 if (XCheckMaskEvent(dpy
, xeventmask
, &ev
)) {
409 XPutBackEvent(dpy
, &ev
);
414 return W_HandleInputEvents(waitForInput
, ConnectionNumber(dpy
));
419 WMNextEvent(Display
*dpy
, XEvent
*event
)
421 /* Check any expired timers */
422 W_CheckTimerHandlers();
424 while (XPending(dpy
) == 0) {
425 /* Do idle and timer stuff while there are no input or X events */
426 while (!waitForEvent(dpy
, 0, False
) && W_CheckIdleHandlers()) {
427 /* dispatch timer events */
428 W_CheckTimerHandlers();
432 * Make sure that new events did not arrive while we were doing
433 * timer/idle stuff. Or we might block forever waiting for
434 * an event that already arrived.
436 /* wait for something to happen or a timer to expire */
437 waitForEvent(dpy
, 0, True
);
439 /* Check any expired timers */
440 W_CheckTimerHandlers();
443 XNextEvent(dpy
, event
);
448 * Is this comment still valid?
449 * waitForEvent seems now to understand masked events. -Dan
451 * Cant use this because XPending() will make waitForEvent
452 * return even if the event in the queue is not what we want,
453 * and if we block until some new event arrives from the
454 * server, other events already in the queue (like Expose)
458 WMMaskEvent(Display
*dpy
, long mask
, XEvent
*event
)
460 /* Check any expired timers */
461 W_CheckTimerHandlers();
463 while (!XCheckMaskEvent(dpy
, mask
, event
)) {
464 /* Do idle and timer stuff while there are no input or X events */
465 while (!waitForEvent(dpy
, mask
, False
) && W_CheckIdleHandlers()) {
466 W_CheckTimerHandlers();
469 if (XCheckMaskEvent(dpy
, mask
, event
))
472 /* Wait for input on the X connection socket or another input handler */
473 waitForEvent(dpy
, mask
, True
);
475 /* Check any expired timers */
476 W_CheckTimerHandlers();
482 WMScreenPending(WMScreen
*scr
)
484 if (XPending(scr
->display
))
492 WMHookEventHandler(WMEventHook
*handler
)
494 WMEventHook
*oldHandler
= extraEventHandler
;
496 extraEventHandler
= handler
;