reverted the last test commit (duh)
[wmaker-crm.git] / WINGs / wevent.c
blob28cd4e0e7bb06835ebf290b822602cf6b4aeffdb
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 OR'ed.
68 void
69 WMCreateEventHandler(WMView *view, unsigned long mask, WMEventProc *eventProc,
70 void *clientData)
72 W_EventHandler *hPtr;
73 WMArrayIterator iter;
75 WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
76 if (hPtr->clientData==clientData && hPtr->proc==eventProc) {
77 hPtr->eventMask |= mask;
78 return;
82 hPtr = wmalloc(sizeof(W_EventHandler));
84 /* select events for window */
85 hPtr->eventMask = mask;
86 hPtr->proc = eventProc;
87 hPtr->clientData = clientData;
89 WMAddToArray(view->eventHandlers, hPtr);
93 static int
94 matchHandler(void *item, void *cdata)
96 #define H1 ((W_EventHandler*)item)
97 #define H2 ((W_EventHandler*)cdata)
99 return (H1->eventMask==H2->eventMask && H1->proc==H2->proc &&
100 H1->clientData==H2->clientData);
105 * WMDeleteEventHandler--
106 * Delete event handler matching arguments from windows
107 * event handler list.
110 void
111 WMDeleteEventHandler(WMView *view, unsigned long mask, WMEventProc *eventProc,
112 void *clientData)
114 W_EventHandler tmp;
116 tmp.eventMask = mask;
117 tmp.proc = eventProc;
118 tmp.clientData = clientData;
119 WMRemoveFromArrayMatching(view->eventHandlers, matchHandler, (void*)&tmp);
123 static Time
124 getEventTime(WMScreen *screen, XEvent *event)
126 switch (event->type) {
127 case ButtonPress:
128 case ButtonRelease:
129 return event->xbutton.time;
130 case KeyPress:
131 case KeyRelease:
132 return event->xkey.time;
133 case MotionNotify:
134 return event->xmotion.time;
135 case EnterNotify:
136 case LeaveNotify:
137 return event->xcrossing.time;
138 case PropertyNotify:
139 return event->xproperty.time;
140 case SelectionClear:
141 return event->xselectionclear.time;
142 case SelectionRequest:
143 return event->xselectionrequest.time;
144 case SelectionNotify:
145 return event->xselection.time;
146 default:
147 return screen->lastEventTime;
152 void
153 W_CallDestroyHandlers(W_View *view)
155 XEvent event;
156 WMArrayIterator iter;
157 W_EventHandler *hPtr;
160 event.type = DestroyNotify;
161 event.xdestroywindow.window = view->window;
162 event.xdestroywindow.event = view->window;
164 WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
165 if (hPtr->eventMask & StructureNotifyMask) {
166 (*hPtr->proc)(&event, hPtr->clientData);
173 void
174 WMSetViewNextResponder(WMView *view, WMView *responder)
176 /* set the widget to receive keyboard events that aren't handled
177 * by this widget */
179 view->nextResponder = responder;
183 void
184 WMRelayToNextResponder(WMView *view, XEvent *event)
186 unsigned long mask = eventMasks[event->xany.type];
188 if (view->nextResponder) {
189 WMView *next = view->nextResponder;
190 W_EventHandler *hPtr;
191 WMArrayIterator iter;
193 WM_ITERATE_ARRAY(next->eventHandlers, hPtr, iter) {
194 if ((hPtr->eventMask & mask)) {
195 (*hPtr->proc)(event, hPtr->clientData);
203 WMHandleEvent(XEvent *event)
205 W_EventHandler *hPtr;
206 W_View *view, *toplevel;
207 unsigned long mask;
208 Window window;
209 WMArrayIterator iter;
211 if (event->type == MappingNotify) {
212 XRefreshKeyboardMapping(&event->xmapping);
213 return True;
216 mask = eventMasks[event->xany.type];
218 window = event->xany.window;
220 /* diferentiate SubstructureNotify with StructureNotify */
221 if (mask == StructureNotifyMask) {
222 if (event->xmap.event != event->xmap.window) {
223 mask = SubstructureNotifyMask;
224 window = event->xmap.event;
227 view = W_GetViewForXWindow(event->xany.display, window);
229 if (!view) {
230 if (extraEventHandler)
231 (extraEventHandler)(event);
233 return False;
236 view->screen->lastEventTime = getEventTime(view->screen, event);
238 toplevel = W_TopLevelOfView(view);
240 if (event->type == SelectionNotify || event->type == SelectionClear
241 || event->type == SelectionRequest) {
242 /* handle selection related events */
243 W_HandleSelectionEvent(event);
247 /* if it's a key event, redispatch it to the focused control */
248 if (mask & (KeyPressMask|KeyReleaseMask)) {
249 W_View *focused = W_FocusedViewOfToplevel(toplevel);
251 if (focused) {
252 view = focused;
256 /* compress Motion events */
257 if (event->type == MotionNotify && !view->flags.dontCompressMotion) {
258 while (XPending(event->xmotion.display)) {
259 XEvent ev;
260 XPeekEvent(event->xmotion.display, &ev);
261 if (ev.type == MotionNotify
262 && event->xmotion.window == ev.xmotion.window
263 && event->xmotion.subwindow == ev.xmotion.subwindow) {
264 /* replace events */
265 XNextEvent(event->xmotion.display, event);
266 } else break;
270 /* compress expose events */
271 if (event->type == Expose && !view->flags.dontCompressExpose) {
272 while (XCheckTypedWindowEvent(event->xexpose.display, view->window,
273 Expose, event));
277 if (view->screen->modalLoop && toplevel!=view->screen->modalView
278 && !toplevel->flags.worksWhenModal) {
279 if (event->type == KeyPress || event->type == KeyRelease
280 || event->type == MotionNotify || event->type == ButtonPress
281 || event->type == ButtonRelease
282 || event->type == FocusIn || event->type == FocusOut) {
283 return True;
287 /* do balloon stuffs */
288 if (event->type == EnterNotify)
289 W_BalloonHandleEnterView(view);
290 else if (event->type == LeaveNotify)
291 W_BalloonHandleLeaveView(view);
293 /* This is a hack. It will make the panel be secure while
294 * the event handlers are handled, as some event handler
295 * might destroy the widget. */
296 W_RetainView(toplevel);
298 WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
299 if ((hPtr->eventMask & mask)) {
300 (*hPtr->proc)(event, hPtr->clientData);
303 #if 0
304 /* pass the event to the top level window of the widget */
305 /* TODO: change this to a responder chain */
306 if (view->parent != NULL) {
307 vPtr = view;
308 while (vPtr->parent != NULL)
309 vPtr = vPtr->parent;
311 WM_ITERATE_ARRAY(vPtr->eventHandlers, hPtr, iter) {
312 if (hPtr->eventMask & mask) {
313 (*hPtr->proc)(event, hPtr->clientData);
317 #endif
318 /* save button click info to track double-clicks */
319 if (view->screen->ignoreNextDoubleClick) {
320 view->screen->ignoreNextDoubleClick = 0;
321 } else {
322 if (event->type == ButtonPress) {
323 view->screen->lastClickWindow = event->xbutton.window;
324 view->screen->lastClickTime = event->xbutton.time;
328 if (event->type == ClientMessage) {
329 /* must be handled at the end, for such message can destroy the view */
330 W_HandleDNDClientMessage(toplevel, &event->xclient);
333 W_ReleaseView(toplevel);
335 return True;
340 WMIsDoubleClick(XEvent *event)
342 W_View *view;
344 if (event->type != ButtonPress)
345 return False;
347 view = W_GetViewForXWindow(event->xany.display, event->xbutton.window);
349 if (!view)
350 return False;
352 if (view->screen->lastClickWindow != event->xbutton.window)
353 return False;
355 if (event->xbutton.time - view->screen->lastClickTime
356 < WINGsConfiguration.doubleClickDelay) {
357 view->screen->lastClickTime = 0;
358 view->screen->lastClickWindow = None;
359 view->screen->ignoreNextDoubleClick = 1;
360 return True;
361 } else
362 return False;
367 * Check for X and input events. If X events are present input events will
368 * not be checked.
370 * Return value: True if a X event is available or any input event was
371 * processed, false otherwise (including return because of
372 * some timer handler expired).
374 * If waitForInput is False, it will just peek for available input and return
375 * without processing. Return vaue will be True if input is available.
377 * If waitForInput is True, it will wait until an input event arrives on the
378 * registered input handlers and ConnectionNumber(dpy), or will return when
379 * a timer handler expires if no input event arrived until then.
381 static Bool
382 waitForEvent(Display *dpy, unsigned long xeventmask, Bool waitForInput)
384 XSync(dpy, False);
385 if (xeventmask==0) {
386 if (XPending(dpy))
387 return True;
388 } else {
389 XEvent ev;
390 if (XCheckMaskEvent(dpy, xeventmask, &ev)) {
391 XPutBackEvent(dpy, &ev);
392 return True;
396 return W_HandleInputEvents(waitForInput, ConnectionNumber(dpy));
400 void
401 WMNextEvent(Display *dpy, XEvent *event)
403 /* Check any expired timers */
404 W_CheckTimerHandlers();
406 while (XPending(dpy) == 0) {
407 /* Do idle and timer stuff while there are no input or X events */
408 while (!waitForEvent(dpy, 0, False) && W_CheckIdleHandlers()) {
409 /* dispatch timer events */
410 W_CheckTimerHandlers();
414 * Make sure that new events did not arrive while we were doing
415 * timer/idle stuff. Or we might block forever waiting for
416 * an event that already arrived.
418 /* wait for something to happen or a timer to expire */
419 waitForEvent(dpy, 0, True);
421 /* Check any expired timers */
422 W_CheckTimerHandlers();
425 XNextEvent(dpy, event);
429 void
430 WMMaskEvent(Display *dpy, long mask, XEvent *event)
432 /* Check any expired timers */
433 W_CheckTimerHandlers();
435 while (!XCheckMaskEvent(dpy, mask, event)) {
436 /* Do idle and timer stuff while there are no input or X events */
437 while (!waitForEvent(dpy, mask, False) && W_CheckIdleHandlers()) {
438 W_CheckTimerHandlers();
441 if (XCheckMaskEvent(dpy, mask, event))
442 return;
444 /* Wait for input on the X connection socket or another input handler */
445 waitForEvent(dpy, mask, True);
447 /* Check any expired timers */
448 W_CheckTimerHandlers();
453 Bool
454 WMScreenPending(WMScreen *scr)
456 if (XPending(scr->display))
457 return True;
458 else
459 return False;
463 WMEventHook*
464 WMHookEventHandler(WMEventHook *handler)
466 WMEventHook *oldHandler = extraEventHandler;
468 extraEventHandler = handler;
470 return oldHandler;