- Now when Window Maker calls wmsetbg to set the background, it will pass the
[wmaker-crm.git] / WINGs / wevent.c
blob91528e1e3dc9105a6270ccd1386e6737e664ac14
3 /*
4 * This event handling stuff was inspired on Tk.
5 */
7 #include "WINGsP.h"
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,
21 /* MotionNotify */
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.
68 void
69 WMCreateEventHandler(WMView *view, unsigned long mask, WMEventProc *eventProc,
70 void *clientData)
72 W_EventHandler *handler, *ptr;
73 unsigned long eventMask;
74 WMBagIterator iter;
77 handler = NULL;
78 eventMask = mask;
80 WM_ITERATE_BAG(view->eventHandlers, ptr, iter) {
81 if (ptr->clientData == clientData && ptr->proc == eventProc) {
82 handler = ptr;
83 eventMask |= ptr->eventMask;
86 if (!handler) {
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.
104 void
105 WMDeleteEventHandler(WMView *view, unsigned long mask, WMEventProc *eventProc,
106 void *clientData)
108 W_EventHandler *handler, *ptr;
109 WMBagIterator iter;
111 handler = NULL;
113 WM_ITERATE_BAG(view->eventHandlers, ptr, iter) {
114 if (ptr->eventMask == mask && ptr->proc == eventProc
115 && ptr->clientData == clientData) {
116 handler = ptr;
117 break;
121 if (!handler)
122 return;
124 WMRemoveFromBag(view->eventHandlers, handler);
126 wfree(handler);
131 void
132 W_CleanUpEvents(WMView *view)
134 W_EventHandler *ptr;
135 WMBagIterator iter;
137 WM_ITERATE_BAG(view->eventHandlers, ptr, iter) {
138 wfree(ptr);
144 static Time
145 getEventTime(WMScreen *screen, XEvent *event)
147 switch (event->type) {
148 case ButtonPress:
149 case ButtonRelease:
150 return event->xbutton.time;
151 case KeyPress:
152 case KeyRelease:
153 return event->xkey.time;
154 case MotionNotify:
155 return event->xmotion.time;
156 case EnterNotify:
157 case LeaveNotify:
158 return event->xcrossing.time;
159 case PropertyNotify:
160 return event->xproperty.time;
161 case SelectionClear:
162 return event->xselectionclear.time;
163 case SelectionRequest:
164 return event->xselectionrequest.time;
165 case SelectionNotify:
166 return event->xselection.time;
167 default:
168 return screen->lastEventTime;
173 void
174 W_CallDestroyHandlers(W_View *view)
176 XEvent event;
177 WMBagIterator iter;
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);
193 void
194 WMSetViewNextResponder(WMView *view, WMView *responder)
196 /* set the widget to receive keyboard events that aren't handled
197 * by this widget */
199 view->nextResponder = responder;
203 void
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;
211 WMBagIterator iter;
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;
227 unsigned long mask;
228 Window window;
229 WMBagIterator iter;
231 if (event->type == MappingNotify) {
232 XRefreshKeyboardMapping(&event->xmapping);
233 return True;
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);
249 if (!view) {
250 if (extraEventHandler)
251 (extraEventHandler)(event);
253 return False;
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);
274 if (focused) {
275 view = focused;
279 /* compress Motion events */
280 if (event->type == MotionNotify && !view->flags.dontCompressMotion) {
281 while (XPending(event->xmotion.display)) {
282 XEvent ev;
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) {
287 /* replace events */
288 XNextEvent(event->xmotion.display, event);
289 } else break;
293 /* compress expose events */
294 if (event->type == Expose && !view->flags.dontCompressExpose) {
295 while (XCheckTypedWindowEvent(event->xexpose.display, view->window,
296 Expose, event));
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) {
306 return True;
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);
326 #if 0
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) {
330 vPtr = view;
331 while (vPtr->parent != NULL)
332 vPtr = vPtr->parent;
334 WM_ITERATE_BAG(vPtr->eventHandlers, hPtr, iter) {
335 if (hPtr->eventMask & mask) {
336 (*hPtr->proc)(event, hPtr->clientData);
340 #endif
341 /* save button click info to track double-clicks */
342 if (view->screen->ignoreNextDoubleClick) {
343 view->screen->ignoreNextDoubleClick = 0;
344 } else {
345 if (event->type == ButtonPress) {
346 view->screen->lastClickWindow = event->xbutton.window;
347 view->screen->lastClickTime = event->xbutton.time;
351 W_ReleaseView(toplevel);
353 return True;
358 WMIsDoubleClick(XEvent *event)
360 W_View *view;
362 if (event->type != ButtonPress)
363 return False;
365 view = W_GetViewForXWindow(event->xany.display, event->xbutton.window);
367 if (!view)
368 return False;
370 if (view->screen->lastClickWindow != event->xbutton.window)
371 return False;
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;
378 return True;
379 } else
380 return False;
385 * Check for X and input events. If X events are present input events will
386 * not be checked.
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.
399 static Bool
400 waitForEvent(Display *dpy, unsigned long xeventmask, Bool waitForInput)
402 XSync(dpy, False);
403 if (xeventmask==0) {
404 if (XPending(dpy))
405 return True;
406 } else {
407 XEvent ev;
408 if (XCheckMaskEvent(dpy, xeventmask, &ev)) {
409 XPutBackEvent(dpy, &ev);
410 return True;
414 return W_HandleInputEvents(waitForInput, ConnectionNumber(dpy));
418 void
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);
447 void
448 WMMaskEvent(Display *dpy, long mask, XEvent *event)
450 /* Check any expired timers */
451 W_CheckTimerHandlers();
453 while (!XCheckMaskEvent(dpy, mask, event)) {
454 /* Do idle and timer stuff while there are no input or X events */
455 while (!waitForEvent(dpy, mask, False) && W_CheckIdleHandlers()) {
456 W_CheckTimerHandlers();
459 if (XCheckMaskEvent(dpy, mask, event))
460 return;
462 /* Wait for input on the X connection socket or another input handler */
463 waitForEvent(dpy, mask, True);
465 /* Check any expired timers */
466 W_CheckTimerHandlers();
471 Bool
472 WMScreenPending(WMScreen *scr)
474 if (XPending(scr->display))
475 return True;
476 else
477 return False;
481 WMEventHook*
482 WMHookEventHandler(WMEventHook *handler)
484 WMEventHook *oldHandler = extraEventHandler;
486 extraEventHandler = handler;
488 return oldHandler;