fixed crash when saving empty menu items in wprefs
[wmaker-crm.git] / WINGs / wevent.c
blob511be0f2a09012c18ed70fa7351bdb03d4a38d63
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 WMArrayIterator iter;
77 handler = NULL;
78 eventMask = mask;
80 WM_ITERATE_ARRAY(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 WMAddToArray(view->eventHandlers, handler);
91 /* select events for window */
92 handler->eventMask = eventMask;
93 handler->proc = eventProc;
94 handler->clientData = clientData;
98 static int
99 matchHandler(void *item, void *cdata)
101 #define H1 ((W_EventHandler*)item)
102 #define H2 ((W_EventHandler*)cdata)
104 return (H1->eventMask==H2->eventMask && H1->proc==H2->proc &&
105 H1->clientData==H2->clientData);
110 * WMDeleteEventHandler--
111 * Delete event handler matching arguments from windows
112 * event handler list.
115 void
116 WMDeleteEventHandler(WMView *view, unsigned long mask, WMEventProc *eventProc,
117 void *clientData)
119 W_EventHandler tmp;
121 tmp.eventMask = mask;
122 tmp.proc = eventProc;
123 tmp.clientData = clientData;
124 WMRemoveFromArrayMatching(view->eventHandlers, matchHandler, (void*)&tmp);
128 static Time
129 getEventTime(WMScreen *screen, XEvent *event)
131 switch (event->type) {
132 case ButtonPress:
133 case ButtonRelease:
134 return event->xbutton.time;
135 case KeyPress:
136 case KeyRelease:
137 return event->xkey.time;
138 case MotionNotify:
139 return event->xmotion.time;
140 case EnterNotify:
141 case LeaveNotify:
142 return event->xcrossing.time;
143 case PropertyNotify:
144 return event->xproperty.time;
145 case SelectionClear:
146 return event->xselectionclear.time;
147 case SelectionRequest:
148 return event->xselectionrequest.time;
149 case SelectionNotify:
150 return event->xselection.time;
151 default:
152 return screen->lastEventTime;
157 void
158 W_CallDestroyHandlers(W_View *view)
160 XEvent event;
161 WMArrayIterator iter;
162 W_EventHandler *hPtr;
165 event.type = DestroyNotify;
166 event.xdestroywindow.window = view->window;
167 event.xdestroywindow.event = view->window;
169 WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
170 if (hPtr->eventMask & StructureNotifyMask) {
171 (*hPtr->proc)(&event, hPtr->clientData);
178 void
179 WMSetViewNextResponder(WMView *view, WMView *responder)
181 /* set the widget to receive keyboard events that aren't handled
182 * by this widget */
184 view->nextResponder = responder;
188 void
189 WMRelayToNextResponder(WMView *view, XEvent *event)
191 unsigned long mask = eventMasks[event->xany.type];
193 if (view->nextResponder) {
194 WMView *next = view->nextResponder;
195 W_EventHandler *hPtr;
196 WMArrayIterator iter;
198 WM_ITERATE_ARRAY(next->eventHandlers, hPtr, iter) {
199 if ((hPtr->eventMask & mask)) {
200 (*hPtr->proc)(event, hPtr->clientData);
208 WMHandleEvent(XEvent *event)
210 W_EventHandler *hPtr;
211 W_View *view, *toplevel;
212 unsigned long mask;
213 Window window;
214 WMArrayIterator iter;
216 if (event->type == MappingNotify) {
217 XRefreshKeyboardMapping(&event->xmapping);
218 return True;
221 mask = eventMasks[event->xany.type];
223 window = event->xany.window;
225 /* diferentiate SubstructureNotify with StructureNotify */
226 if (mask == StructureNotifyMask) {
227 if (event->xmap.event != event->xmap.window) {
228 mask = SubstructureNotifyMask;
229 window = event->xmap.event;
232 view = W_GetViewForXWindow(event->xany.display, window);
234 if (!view) {
235 if (extraEventHandler)
236 (extraEventHandler)(event);
238 return False;
241 view->screen->lastEventTime = getEventTime(view->screen, event);
243 toplevel = W_TopLevelOfView(view);
245 if (event->type == SelectionNotify || event->type == SelectionClear
246 || event->type == SelectionRequest) {
247 /* handle selection related events */
248 W_HandleSelectionEvent(event);
250 } else if (event->type == ClientMessage) {
252 W_HandleDNDClientMessage(toplevel, &event->xclient);
255 /* if it's a key event, redispatch it to the focused control */
256 if (mask & (KeyPressMask|KeyReleaseMask)) {
257 W_View *focused = W_FocusedViewOfToplevel(toplevel);
259 if (focused) {
260 view = focused;
264 /* compress Motion events */
265 if (event->type == MotionNotify && !view->flags.dontCompressMotion) {
266 while (XPending(event->xmotion.display)) {
267 XEvent ev;
268 XPeekEvent(event->xmotion.display, &ev);
269 if (ev.type == MotionNotify
270 && event->xmotion.window == ev.xmotion.window
271 && event->xmotion.subwindow == ev.xmotion.subwindow) {
272 /* replace events */
273 XNextEvent(event->xmotion.display, event);
274 } else break;
278 /* compress expose events */
279 if (event->type == Expose && !view->flags.dontCompressExpose) {
280 while (XCheckTypedWindowEvent(event->xexpose.display, view->window,
281 Expose, event));
285 if (view->screen->modalLoop && toplevel!=view->screen->modalView
286 && !toplevel->flags.worksWhenModal) {
287 if (event->type == KeyPress || event->type == KeyRelease
288 || event->type == MotionNotify || event->type == ButtonPress
289 || event->type == ButtonRelease
290 || event->type == FocusIn || event->type == FocusOut) {
291 return True;
295 /* do balloon stuffs */
296 if (event->type == EnterNotify)
297 W_BalloonHandleEnterView(view);
298 else if (event->type == LeaveNotify)
299 W_BalloonHandleLeaveView(view);
301 /* This is a hack. It will make the panel be secure while
302 * the event handlers are handled, as some event handler
303 * might destroy the widget. */
304 W_RetainView(toplevel);
306 WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
307 if ((hPtr->eventMask & mask)) {
308 (*hPtr->proc)(event, hPtr->clientData);
311 #if 0
312 /* pass the event to the top level window of the widget */
313 /* TODO: change this to a responder chain */
314 if (view->parent != NULL) {
315 vPtr = view;
316 while (vPtr->parent != NULL)
317 vPtr = vPtr->parent;
319 WM_ITERATE_ARRAY(vPtr->eventHandlers, hPtr, iter) {
320 if (hPtr->eventMask & mask) {
321 (*hPtr->proc)(event, hPtr->clientData);
325 #endif
326 /* save button click info to track double-clicks */
327 if (view->screen->ignoreNextDoubleClick) {
328 view->screen->ignoreNextDoubleClick = 0;
329 } else {
330 if (event->type == ButtonPress) {
331 view->screen->lastClickWindow = event->xbutton.window;
332 view->screen->lastClickTime = event->xbutton.time;
336 W_ReleaseView(toplevel);
338 return True;
343 WMIsDoubleClick(XEvent *event)
345 W_View *view;
347 if (event->type != ButtonPress)
348 return False;
350 view = W_GetViewForXWindow(event->xany.display, event->xbutton.window);
352 if (!view)
353 return False;
355 if (view->screen->lastClickWindow != event->xbutton.window)
356 return False;
358 if (event->xbutton.time - view->screen->lastClickTime
359 < WINGsConfiguration.doubleClickDelay) {
360 view->screen->lastClickTime = 0;
361 view->screen->lastClickWindow = None;
362 view->screen->ignoreNextDoubleClick = 1;
363 return True;
364 } else
365 return False;
370 * Check for X and input events. If X events are present input events will
371 * not be checked.
373 * Return value: True if a X event is available or any input event was
374 * processed, false otherwise (including return because of
375 * some timer handler expired).
377 * If waitForInput is False, it will just peek for available input and return
378 * without processing. Return vaue will be True if input is available.
380 * If waitForInput is True, it will wait until an input event arrives on the
381 * registered input handlers and ConnectionNumber(dpy), or will return when
382 * a timer handler expires if no input event arrived until then.
384 static Bool
385 waitForEvent(Display *dpy, unsigned long xeventmask, Bool waitForInput)
387 XSync(dpy, False);
388 if (xeventmask==0) {
389 if (XPending(dpy))
390 return True;
391 } else {
392 XEvent ev;
393 if (XCheckMaskEvent(dpy, xeventmask, &ev)) {
394 XPutBackEvent(dpy, &ev);
395 return True;
399 return W_HandleInputEvents(waitForInput, ConnectionNumber(dpy));
403 void
404 WMNextEvent(Display *dpy, XEvent *event)
406 /* Check any expired timers */
407 W_CheckTimerHandlers();
409 while (XPending(dpy) == 0) {
410 /* Do idle and timer stuff while there are no input or X events */
411 while (!waitForEvent(dpy, 0, False) && W_CheckIdleHandlers()) {
412 /* dispatch timer events */
413 W_CheckTimerHandlers();
417 * Make sure that new events did not arrive while we were doing
418 * timer/idle stuff. Or we might block forever waiting for
419 * an event that already arrived.
421 /* wait for something to happen or a timer to expire */
422 waitForEvent(dpy, 0, True);
424 /* Check any expired timers */
425 W_CheckTimerHandlers();
428 XNextEvent(dpy, event);
432 void
433 WMMaskEvent(Display *dpy, long mask, XEvent *event)
435 /* Check any expired timers */
436 W_CheckTimerHandlers();
438 while (!XCheckMaskEvent(dpy, mask, event)) {
439 /* Do idle and timer stuff while there are no input or X events */
440 while (!waitForEvent(dpy, mask, False) && W_CheckIdleHandlers()) {
441 W_CheckTimerHandlers();
444 if (XCheckMaskEvent(dpy, mask, event))
445 return;
447 /* Wait for input on the X connection socket or another input handler */
448 waitForEvent(dpy, mask, True);
450 /* Check any expired timers */
451 W_CheckTimerHandlers();
456 Bool
457 WMScreenPending(WMScreen *scr)
459 if (XPending(scr->display))
460 return True;
461 else
462 return False;
466 WMEventHook*
467 WMHookEventHandler(WMEventHook *handler)
469 WMEventHook *oldHandler = extraEventHandler;
471 extraEventHandler = handler;
473 return oldHandler;