src/winmenu.c: updated prepare_menu_position
[wmaker-crm.git] / WINGs / wevent.c
blob71be3d77c4aa35c02f07bb54e9c1e7a35ca5d23c
2 /*
3 * This event handling stuff was inspired on Tk.
4 */
6 #include "WINGsP.h"
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,
18 /* MotionNotify */
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)
61 W_EventHandler *hPtr;
62 WMArrayIterator iter;
64 WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
65 if (hPtr->clientData == clientData && hPtr->proc == eventProc) {
66 hPtr->eventMask |= mask;
67 return;
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
94 * event handler list.
97 void WMDeleteEventHandler(WMView * view, unsigned long mask, WMEventProc * eventProc, void *clientData)
99 W_EventHandler tmp;
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) {
110 case ButtonPress:
111 case ButtonRelease:
112 return event->xbutton.time;
113 case KeyPress:
114 case KeyRelease:
115 return event->xkey.time;
116 case MotionNotify:
117 return event->xmotion.time;
118 case EnterNotify:
119 case LeaveNotify:
120 return event->xcrossing.time;
121 case PropertyNotify:
122 return event->xproperty.time;
123 case SelectionClear:
124 return event->xselectionclear.time;
125 case SelectionRequest:
126 return event->xselectionrequest.time;
127 case SelectionNotify:
128 return event->xselection.time;
129 default:
130 return screen->lastEventTime;
134 void W_CallDestroyHandlers(W_View * view)
136 XEvent event;
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
154 * by this widget */
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;
180 unsigned long mask;
181 Window window;
182 WMArrayIterator iter;
184 if (event->type == MappingNotify) {
185 XRefreshKeyboardMapping(&event->xmapping);
186 return True;
189 if (XFilterEvent(event, None) == True) {
190 return False;
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);
206 if (!view) {
207 if (extraEventHandler)
208 (extraEventHandler) (event);
210 return False;
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);
227 if (focused) {
228 view = focused;
232 /* compress Motion events */
233 if (event->type == MotionNotify && !view->flags.dontCompressMotion) {
234 while (XPending(event->xmotion.display)) {
235 XEvent ev;
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) {
240 /* replace events */
241 XNextEvent(event->xmotion.display, event);
242 } else
243 break;
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) {
256 return True;
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);
276 #if 0
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) {
280 vPtr = view;
281 while (vPtr->parent != NULL)
282 vPtr = vPtr->parent;
284 WM_ITERATE_ARRAY(vPtr->eventHandlers, hPtr, iter) {
285 if (hPtr->eventMask & mask) {
286 (*hPtr->proc) (event, hPtr->clientData);
290 #endif
291 /* save button click info to track double-clicks */
292 if (view->screen->ignoreNextDoubleClick) {
293 view->screen->ignoreNextDoubleClick = 0;
294 } else {
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);
308 return True;
311 int WMIsDoubleClick(XEvent * event)
313 W_View *view;
315 if (event->type != ButtonPress)
316 return False;
318 view = W_GetViewForXWindow(event->xany.display, event->xbutton.window);
320 if (!view)
321 return False;
323 if (view->screen->lastClickWindow != event->xbutton.window)
324 return False;
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;
330 return True;
331 } else
332 return False;
336 * Check for X and input events. If X events are present input events will
337 * not be checked.
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)
352 XSync(dpy, False);
353 if (xeventmask == 0) {
354 if (XPending(dpy))
355 return True;
356 } else {
357 XEvent ev;
358 if (XCheckMaskEvent(dpy, xeventmask, &ev)) {
359 XPutBackEvent(dpy, &ev);
360 return True;
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))
406 return;
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))
419 return True;
420 else
421 return False;
424 WMEventHook *WMHookEventHandler(WMEventHook * handler)
426 WMEventHook *oldHandler = extraEventHandler;
428 extraEventHandler = handler;
430 return oldHandler;