]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/event.c
don't grab the pointer during interactive events. this allows you to alt-tab during...
[mikachu/openbox.git] / openbox / event.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    event.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003        Ben Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "event.h"
21 #include "debug.h"
22 #include "window.h"
23 #include "openbox.h"
24 #include "dock.h"
25 #include "client.h"
26 #include "xerror.h"
27 #include "prop.h"
28 #include "config.h"
29 #include "screen.h"
30 #include "frame.h"
31 #include "menu.h"
32 #include "menuframe.h"
33 #include "keyboard.h"
34 #include "mouse.h"
35 #include "mainloop.h"
36 #include "framerender.h"
37 #include "focus.h"
38 #include "moveresize.h"
39 #include "group.h"
40 #include "stacking.h"
41 #include "extensions.h"
42
43 #include <X11/Xlib.h>
44 #include <X11/keysym.h>
45 #include <X11/Xatom.h>
46 #include <glib.h>
47
48 #ifdef HAVE_SYS_SELECT_H
49 #  include <sys/select.h>
50 #endif
51 #ifdef HAVE_SIGNAL_H
52 #  include <signal.h>
53 #endif
54 #ifdef XKB
55 #  include <X11/XKBlib.h>
56 #endif
57
58 #ifdef USE_SM
59 #include <X11/ICE/ICElib.h>
60 #endif
61
62 typedef struct
63 {
64     gboolean ignored;
65 } ObEventData;
66
67 typedef struct
68 {
69     ObClient *client;
70     Time time;
71 } ObFocusDelayData;
72
73 static void event_process(const XEvent *e, gpointer data);
74 static void event_client_dest(ObClient *client, gpointer data);
75 static void event_handle_root(XEvent *e);
76 static void event_handle_menu(XEvent *e);
77 static void event_handle_dock(ObDock *s, XEvent *e);
78 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
79 static void event_handle_client(ObClient *c, XEvent *e);
80 static void event_handle_group(ObGroup *g, XEvent *e);
81
82 static void focus_delay_dest(gpointer data);
83 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2);
84 static gboolean focus_delay_func(gpointer data);
85 static void focus_delay_client_dest(ObClient *client, gpointer data);
86
87 static gboolean menu_hide_delay_func(gpointer data);
88
89 /* The time for the current event being processed */
90 Time event_curtime = CurrentTime;
91
92 /*! The value of the mask for the NumLock modifier */
93 guint NumLockMask;
94 /*! The value of the mask for the ScrollLock modifier */
95 guint ScrollLockMask;
96 /*! The key codes for the modifier keys */
97 static XModifierKeymap *modmap;
98 /*! Table of the constant modifier masks */
99 static const gint mask_table[] = {
100     ShiftMask, LockMask, ControlMask, Mod1Mask,
101     Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
102 };
103 static gint mask_table_size;
104
105 static guint ignore_enter_focus = 0;
106
107 static gboolean menu_can_hide;
108
109 #ifdef USE_SM
110 static void ice_handler(gint fd, gpointer conn)
111 {
112     Bool b;
113     IceProcessMessages(conn, NULL, &b);
114 }
115
116 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
117                       IcePointer *watch_data)
118 {
119     static gint fd = -1;
120
121     if (opening) {
122         fd = IceConnectionNumber(conn);
123         ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
124     } else {
125         ob_main_loop_fd_remove(ob_main_loop, fd);
126         fd = -1;
127     }
128 }
129 #endif
130
131 void event_startup(gboolean reconfig)
132 {
133     if (reconfig) return;
134
135     mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
136      
137     /* get lock masks that are defined by the display (not constant) */
138     modmap = XGetModifierMapping(ob_display);
139     g_assert(modmap);
140     if (modmap && modmap->max_keypermod > 0) {
141         size_t cnt;
142         const size_t size = mask_table_size * modmap->max_keypermod;
143         /* get the values of the keyboard lock modifiers
144            Note: Caps lock is not retrieved the same way as Scroll and Num
145            lock since it doesn't need to be. */
146         const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
147         const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
148                                                      XK_Scroll_Lock);
149
150         for (cnt = 0; cnt < size; ++cnt) {
151             if (! modmap->modifiermap[cnt]) continue;
152
153             if (num_lock == modmap->modifiermap[cnt])
154                 NumLockMask = mask_table[cnt / modmap->max_keypermod];
155             if (scroll_lock == modmap->modifiermap[cnt])
156                 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
157         }
158     }
159
160     ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
161
162 #ifdef USE_SM
163     IceAddConnectionWatch(ice_watch, NULL);
164 #endif
165
166     client_add_destructor(focus_delay_client_dest, NULL);
167     client_add_destructor(event_client_dest, NULL);
168 }
169
170 void event_shutdown(gboolean reconfig)
171 {
172     if (reconfig) return;
173
174 #ifdef USE_SM
175     IceRemoveConnectionWatch(ice_watch, NULL);
176 #endif
177
178     client_remove_destructor(focus_delay_client_dest);
179     client_remove_destructor(event_client_dest);
180     XFreeModifiermap(modmap);
181 }
182
183 static Window event_get_window(XEvent *e)
184 {
185     Window window;
186
187     /* pick a window */
188     switch (e->type) {
189     case SelectionClear:
190         window = RootWindow(ob_display, ob_screen);
191         break;
192     case MapRequest:
193         window = e->xmap.window;
194         break;
195     case UnmapNotify:
196         window = e->xunmap.window;
197         break;
198     case DestroyNotify:
199         window = e->xdestroywindow.window;
200         break;
201     case ConfigureRequest:
202         window = e->xconfigurerequest.window;
203         break;
204     case ConfigureNotify:
205         window = e->xconfigure.window;
206         break;
207     default:
208 #ifdef XKB
209         if (extensions_xkb && e->type == extensions_xkb_event_basep) {
210             switch (((XkbAnyEvent*)e)->xkb_type) {
211             case XkbBellNotify:
212                 window = ((XkbBellNotifyEvent*)e)->window;
213             default:
214                 window = None;
215             }
216         } else
217 #endif
218             window = e->xany.window;
219     }
220     return window;
221 }
222
223 static void event_set_curtime(XEvent *e)
224 {
225     Time t = CurrentTime;
226
227     /* grab the lasttime and hack up the state */
228     switch (e->type) {
229     case ButtonPress:
230     case ButtonRelease:
231         t = e->xbutton.time;
232         break;
233     case KeyPress:
234         t = e->xkey.time;
235         break;
236     case KeyRelease:
237         t = e->xkey.time;
238         break;
239     case MotionNotify:
240         t = e->xmotion.time;
241         break;
242     case PropertyNotify:
243         t = e->xproperty.time;
244         break;
245     case EnterNotify:
246     case LeaveNotify:
247         t = e->xcrossing.time;
248         break;
249     default:
250         /* if more event types are anticipated, get their timestamp
251            explicitly */
252         break;
253     }
254
255     event_curtime = t;
256 }
257
258 #define STRIP_MODS(s) \
259         s &= ~(LockMask | NumLockMask | ScrollLockMask), \
260         /* kill off the Button1Mask etc, only want the modifiers */ \
261         s &= (ControlMask | ShiftMask | Mod1Mask | \
262               Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
263
264 static void event_hack_mods(XEvent *e)
265 {
266 #ifdef XKB
267     XkbStateRec xkb_state;
268 #endif
269     KeyCode *kp;
270     gint i, k;
271
272     switch (e->type) {
273     case ButtonPress:
274     case ButtonRelease:
275         STRIP_MODS(e->xbutton.state);
276         break;
277     case KeyPress:
278         STRIP_MODS(e->xkey.state);
279         break;
280     case KeyRelease:
281         STRIP_MODS(e->xkey.state);
282         /* remove from the state the mask of the modifier being released, if
283            it is a modifier key being released (this is a little ugly..) */
284 #ifdef XKB
285         if (XkbGetState(ob_display, XkbUseCoreKbd, &xkb_state) == Success) {
286             e->xkey.state = xkb_state.compat_state;
287             break;
288         }
289 #endif
290         kp = modmap->modifiermap;
291         for (i = 0; i < mask_table_size; ++i) {
292             for (k = 0; k < modmap->max_keypermod; ++k) {
293                 if (*kp == e->xkey.keycode) { /* found the keycode */
294                     /* remove the mask for it */
295                     e->xkey.state &= ~mask_table[i];
296                     /* cause the first loop to break; */
297                     i = mask_table_size;
298                     break; /* get outta here! */
299                 }
300                 ++kp;
301             }
302         }
303         break;
304     case MotionNotify:
305         STRIP_MODS(e->xmotion.state);
306         /* compress events */
307         {
308             XEvent ce;
309             while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
310                                           e->type, &ce)) {
311                 e->xmotion.x_root = ce.xmotion.x_root;
312                 e->xmotion.y_root = ce.xmotion.y_root;
313             }
314         }
315         break;
316     }
317 }
318
319 static gboolean wanted_focusevent(XEvent *e)
320 {
321     gint mode = e->xfocus.mode;
322     gint detail = e->xfocus.detail;
323     Window win = e->xany.window;
324
325     if (e->type == FocusIn) {
326
327         /* These are ones we never want.. */
328
329         /* This means focus was given by a keyboard/mouse grab. */
330         if (mode == NotifyGrab)
331             return FALSE;
332         /* This means focus was given back from a keyboard/mouse grab. */
333         if (mode == NotifyUngrab)
334             return FALSE;
335
336         /* These are the ones we want.. */
337
338         if (win == RootWindow(ob_display, ob_screen)) {
339             /* This means focus reverted off of a client */
340             if (detail == NotifyPointerRoot || detail == NotifyDetailNone ||
341                 detail == NotifyInferior)
342                 return TRUE;
343             else
344                 return FALSE;
345         }
346
347         /* This means focus moved from the root window to a client */
348         if (detail == NotifyVirtual)
349             return TRUE;
350         /* This means focus moved from one client to another */
351         if (detail == NotifyNonlinearVirtual)
352             return TRUE;
353
354         /* This means focus reverted off of a client */
355         if (detail == NotifyInferior)
356             return TRUE;
357
358         /* Otherwise.. */
359         return FALSE;
360     } else {
361         g_assert(e->type == FocusOut);
362
363
364         /* These are ones we never want.. */
365
366         /* This means focus was taken by a keyboard/mouse grab. */
367         if (mode == NotifyGrab)
368             return FALSE;
369
370         /* Focus left the root window revertedto state */
371         if (win == RootWindow(ob_display, ob_screen))
372             return FALSE;
373
374         /* These are the ones we want.. */
375
376         /* This means focus moved from a client to the root window */
377         if (detail == NotifyVirtual)
378             return TRUE;
379         /* This means focus moved from one client to another */
380         if (detail == NotifyNonlinearVirtual)
381             return TRUE;
382
383         /* Otherwise.. */
384         return FALSE;
385     }
386 }
387
388 static Bool look_for_focusin(Display *d, XEvent *e, XPointer arg)
389 {
390     return e->type == FocusIn && wanted_focusevent(e);
391 }
392
393 static gboolean event_ignore(XEvent *e, ObClient *client)
394 {
395     switch(e->type) {
396     case FocusIn:
397     case FocusOut:
398         if (!wanted_focusevent(e)) {
399             ob_debug_type(OB_DEBUG_FOCUS, "focus event ignored\n");
400             return TRUE;
401         }
402         ob_debug_type(OB_DEBUG_FOCUS, "focus event used;\n");
403         break;
404     }
405     return FALSE;
406 }
407
408 static void event_process(const XEvent *ec, gpointer data)
409 {
410     Window window;
411     ObGroup *group = NULL;
412     ObClient *client = NULL;
413     ObDock *dock = NULL;
414     ObDockApp *dockapp = NULL;
415     ObWindow *obwin = NULL;
416     XEvent ee, *e;
417     ObEventData *ed = data;
418
419     /* make a copy we can mangle */
420     ee = *ec;
421     e = &ee;
422
423     window = event_get_window(e);
424     if (!(e->type == PropertyNotify &&
425           (group = g_hash_table_lookup(group_map, &window))))
426         if ((obwin = g_hash_table_lookup(window_map, &window))) {
427             switch (obwin->type) {
428             case Window_Dock:
429                 dock = WINDOW_AS_DOCK(obwin);
430                 break;
431             case Window_DockApp:
432                 dockapp = WINDOW_AS_DOCKAPP(obwin);
433                 break;
434             case Window_Client:
435                 client = WINDOW_AS_CLIENT(obwin);
436                 break;
437             case Window_Menu:
438             case Window_Internal:
439                 /* not to be used for events */
440                 g_assert_not_reached();
441                 break;
442             }
443         }
444
445     if (e->type == FocusIn || e->type == FocusOut) {
446         gint mode = e->xfocus.mode;
447         gint detail = e->xfocus.detail;
448         Window window = e->xfocus.window;
449         if (detail == NotifyVirtual) {
450             ob_debug_type(OB_DEBUG_FOCUS,
451                           "FOCUS %s NOTIFY VIRTUAL window 0x%x\n",
452                           (e->type == FocusIn ? "IN" : "OUT"), window);
453         }
454
455         else if (detail == NotifyNonlinearVirtual) {
456             ob_debug_type(OB_DEBUG_FOCUS,
457                           "FOCUS %s NOTIFY NONLINVIRTUAL window 0x%x\n",
458                           (e->type == FocusIn ? "IN" : "OUT"), window);
459         }
460
461         else
462             ob_debug_type(OB_DEBUG_FOCUS,
463                           "UNKNOWN FOCUS %s (d %d, m %d) window 0x%x\n",
464                           (e->type == FocusIn ? "IN" : "OUT"),
465                           detail, mode, window);
466     }
467
468     event_set_curtime(e);
469     event_hack_mods(e);
470     if (event_ignore(e, client)) {
471         if (ed)
472             ed->ignored = TRUE;
473         return;
474     } else if (ed)
475             ed->ignored = FALSE;
476
477     /* deal with it in the kernel */
478
479     if (menu_frame_visible &&
480         (e->type == EnterNotify || e->type == LeaveNotify))
481     {
482         /* crossing events for menu */
483         event_handle_menu(e);
484     } else if (e->type == FocusIn) {
485         if (e->xfocus.detail == NotifyPointerRoot ||
486                    e->xfocus.detail == NotifyDetailNone) {
487             ob_debug_type(OB_DEBUG_FOCUS, "Focus went to root\n");
488             /* Focus has been reverted to the root window or nothing, so fall
489                back to something other than the window which just had it. */
490             focus_fallback(FALSE);
491         } else if (e->xfocus.detail == NotifyInferior) {
492             ob_debug_type(OB_DEBUG_FOCUS, "Focus went to parent\n");
493             /* Focus has been reverted to parent, which is our frame window,
494                or the root window, so fall back to something other than the
495                window which had it. */
496             focus_fallback(FALSE);
497         } else if (client && client != focus_client) {
498             focus_set_client(client);
499             frame_adjust_focus(client->frame, TRUE);
500             client_calc_layer(client);
501         }
502     } else if (e->type == FocusOut) {
503         gboolean nomove = FALSE;
504         XEvent ce;
505
506         ob_debug_type(OB_DEBUG_FOCUS, "FocusOut Event\n");
507
508         /* Look for the followup FocusIn */
509         if (!XCheckIfEvent(ob_display, &ce, look_for_focusin, NULL)) {
510             /* There is no FocusIn, this means focus went to a window that
511                is not being managed, or a window on another screen. */
512             ob_debug_type(OB_DEBUG_FOCUS, "Focus went to a black hole !\n");
513         } else if (ce.xany.window == e->xany.window) {
514             /* If focus didn't actually move anywhere, there is nothing to do*/
515             nomove = TRUE;
516         } else {
517             /* Focus did move, so process the FocusIn event */
518             ObEventData ed = { .ignored = FALSE };
519             event_process(&ce, &ed);
520             if (ed.ignored) {
521                 /* The FocusIn was ignored, this means it was on a window
522                    that isn't a client. */
523                 ob_debug_type(OB_DEBUG_FOCUS,
524                               "Focus went to an unmanaged window 0x%x !\n",
525                               ce.xfocus.window);
526                 focus_fallback(TRUE);
527             }
528         }
529
530         if (client && !nomove) {
531             /* This client is no longer focused, so show that */
532             focus_hilite = NULL;
533             frame_adjust_focus(client->frame, FALSE);
534             client_calc_layer(client);
535         }
536     } else if (group)
537         event_handle_group(group, e);
538     else if (client)
539         event_handle_client(client, e);
540     else if (dockapp)
541         event_handle_dockapp(dockapp, e);
542     else if (dock)
543         event_handle_dock(dock, e);
544     else if (window == RootWindow(ob_display, ob_screen))
545         event_handle_root(e);
546     else if (e->type == MapRequest)
547         client_manage(window);
548     else if (e->type == ConfigureRequest) {
549         /* unhandled configure requests must be used to configure the
550            window directly */
551         XWindowChanges xwc;
552
553         xwc.x = e->xconfigurerequest.x;
554         xwc.y = e->xconfigurerequest.y;
555         xwc.width = e->xconfigurerequest.width;
556         xwc.height = e->xconfigurerequest.height;
557         xwc.border_width = e->xconfigurerequest.border_width;
558         xwc.sibling = e->xconfigurerequest.above;
559         xwc.stack_mode = e->xconfigurerequest.detail;
560        
561         /* we are not to be held responsible if someone sends us an
562            invalid request! */
563         xerror_set_ignore(TRUE);
564         XConfigureWindow(ob_display, window,
565                          e->xconfigurerequest.value_mask, &xwc);
566         xerror_set_ignore(FALSE);
567     }
568
569     /* user input (action-bound) events */
570     if (e->type == ButtonPress || e->type == ButtonRelease ||
571         e->type == MotionNotify || e->type == KeyPress ||
572         e->type == KeyRelease)
573     {
574         if (menu_frame_visible)
575             event_handle_menu(e);
576         else {
577             if (!keyboard_process_interactive_grab(e, &client)) {
578                 if (moveresize_in_progress) {
579                     moveresize_event(e);
580
581                     /* make further actions work on the client being
582                        moved/resized */
583                     client = moveresize_client;
584                 }
585
586                 menu_can_hide = FALSE;
587                 ob_main_loop_timeout_add(ob_main_loop,
588                                          config_menu_hide_delay * 1000,
589                                          menu_hide_delay_func,
590                                          NULL, g_direct_equal, NULL);
591
592                 if (e->type == ButtonPress || e->type == ButtonRelease ||
593                     e->type == MotionNotify) {
594                     if (!keyboard_interactively_grabbed())
595                         mouse_event(client, e);
596                 } else if (e->type == KeyPress) {
597                     keyboard_event((focus_cycle_target ? focus_cycle_target :
598                                     (focus_hilite ? focus_hilite : client)),
599                                    e);
600                 }
601             }
602         }
603     }
604     /* if something happens and it's not from an XEvent, then we don't know
605        the time */
606     event_curtime = CurrentTime;
607 }
608
609 static void event_handle_root(XEvent *e)
610 {
611     Atom msgtype;
612      
613     switch(e->type) {
614     case SelectionClear:
615         ob_debug("Another WM has requested to replace us. Exiting.\n");
616         ob_exit_replace();
617         break;
618
619     case ClientMessage:
620         if (e->xclient.format != 32) break;
621
622         msgtype = e->xclient.message_type;
623         if (msgtype == prop_atoms.net_current_desktop) {
624             guint d = e->xclient.data.l[0];
625             if (d < screen_num_desktops) {
626                 event_curtime = e->xclient.data.l[1];
627                 ob_debug("SWITCH DESKTOP TIME: %d\n", event_curtime);
628                 screen_set_desktop(d);
629             }
630         } else if (msgtype == prop_atoms.net_number_of_desktops) {
631             guint d = e->xclient.data.l[0];
632             if (d > 0)
633                 screen_set_num_desktops(d);
634         } else if (msgtype == prop_atoms.net_showing_desktop) {
635             screen_show_desktop(e->xclient.data.l[0] != 0);
636         } else if (msgtype == prop_atoms.ob_control) {
637             if (e->xclient.data.l[0] == 1)
638                 ob_reconfigure();
639             else if (e->xclient.data.l[0] == 2)
640                 ob_restart();
641         }
642         break;
643     case PropertyNotify:
644         if (e->xproperty.atom == prop_atoms.net_desktop_names)
645             screen_update_desktop_names();
646         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
647             screen_update_layout();
648         break;
649     case ConfigureNotify:
650 #ifdef XRANDR
651         XRRUpdateConfiguration(e);
652 #endif
653         screen_resize();
654         break;
655     default:
656         ;
657     }
658 }
659
660 static void event_handle_group(ObGroup *group, XEvent *e)
661 {
662     GSList *it;
663
664     g_assert(e->type == PropertyNotify);
665
666     for (it = group->members; it; it = g_slist_next(it))
667         event_handle_client(it->data, e);
668 }
669
670 void event_enter_client(ObClient *client)
671 {
672     g_assert(config_focus_follow);
673
674     if (client_normal(client) && client_can_focus(client)) {
675         if (config_focus_delay) {
676             ObFocusDelayData *data;
677
678             ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
679
680             data = g_new(ObFocusDelayData, 1);
681             data->client = client;
682             data->time = event_curtime;
683
684             ob_main_loop_timeout_add(ob_main_loop,
685                                      config_focus_delay,
686                                      focus_delay_func,
687                                      data, focus_delay_cmp, focus_delay_dest);
688         } else {
689             ObFocusDelayData data;
690             data.client = client;
691             data.time = event_curtime;
692             focus_delay_func(&data);
693         }
694     }
695 }
696
697 static void event_handle_client(ObClient *client, XEvent *e)
698 {
699     XEvent ce;
700     Atom msgtype;
701     gint i=0;
702     ObFrameContext con;
703      
704     switch (e->type) {
705     case VisibilityNotify:
706         client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
707         break;
708     case ButtonPress:
709     case ButtonRelease:
710         /* Wheel buttons don't draw because they are an instant click, so it
711            is a waste of resources to go drawing it. */
712         if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
713             con = frame_context(client, e->xbutton.window);
714             con = mouse_button_frame_context(con, e->xbutton.button);
715             switch (con) {
716             case OB_FRAME_CONTEXT_MAXIMIZE:
717                 client->frame->max_press = (e->type == ButtonPress);
718                 framerender_frame(client->frame);
719                 break;
720             case OB_FRAME_CONTEXT_CLOSE:
721                 client->frame->close_press = (e->type == ButtonPress);
722                 framerender_frame(client->frame);
723                 break;
724             case OB_FRAME_CONTEXT_ICONIFY:
725                 client->frame->iconify_press = (e->type == ButtonPress);
726                 framerender_frame(client->frame);
727                 break;
728             case OB_FRAME_CONTEXT_ALLDESKTOPS:
729                 client->frame->desk_press = (e->type == ButtonPress);
730                 framerender_frame(client->frame);
731                 break; 
732             case OB_FRAME_CONTEXT_SHADE:
733                 client->frame->shade_press = (e->type == ButtonPress);
734                 framerender_frame(client->frame);
735                 break;
736             default:
737                 /* nothing changes with clicks for any other contexts */
738                 break;
739             }
740         }
741         break;
742     case LeaveNotify:
743         con = frame_context(client, e->xcrossing.window);
744         switch (con) {
745         case OB_FRAME_CONTEXT_MAXIMIZE:
746             client->frame->max_hover = FALSE;
747             frame_adjust_state(client->frame);
748             break;
749         case OB_FRAME_CONTEXT_ALLDESKTOPS:
750             client->frame->desk_hover = FALSE;
751             frame_adjust_state(client->frame);
752             break;
753         case OB_FRAME_CONTEXT_SHADE:
754             client->frame->shade_hover = FALSE;
755             frame_adjust_state(client->frame);
756             break;
757         case OB_FRAME_CONTEXT_ICONIFY:
758             client->frame->iconify_hover = FALSE;
759             frame_adjust_state(client->frame);
760             break;
761         case OB_FRAME_CONTEXT_CLOSE:
762             client->frame->close_hover = FALSE;
763             frame_adjust_state(client->frame);
764             break;
765         case OB_FRAME_CONTEXT_FRAME:
766             if (keyboard_interactively_grabbed())
767                 break;
768             if (config_focus_follow && config_focus_delay)
769                 ob_main_loop_timeout_remove_data(ob_main_loop,
770                                                  focus_delay_func,
771                                                  client, FALSE);
772             break;
773         default:
774             break;
775         }
776         break;
777     case EnterNotify:
778     {
779         gboolean nofocus = FALSE;
780
781         if (ignore_enter_focus) {
782             ignore_enter_focus--;
783             nofocus = TRUE;
784         }
785
786         con = frame_context(client, e->xcrossing.window);
787         switch (con) {
788         case OB_FRAME_CONTEXT_MAXIMIZE:
789             client->frame->max_hover = TRUE;
790             frame_adjust_state(client->frame);
791             break;
792         case OB_FRAME_CONTEXT_ALLDESKTOPS:
793             client->frame->desk_hover = TRUE;
794             frame_adjust_state(client->frame);
795             break;
796         case OB_FRAME_CONTEXT_SHADE:
797             client->frame->shade_hover = TRUE;
798             frame_adjust_state(client->frame);
799             break;
800         case OB_FRAME_CONTEXT_ICONIFY:
801             client->frame->iconify_hover = TRUE;
802             frame_adjust_state(client->frame);
803             break;
804         case OB_FRAME_CONTEXT_CLOSE:
805             client->frame->close_hover = TRUE;
806             frame_adjust_state(client->frame);
807             break;
808         case OB_FRAME_CONTEXT_FRAME:
809             if (keyboard_interactively_grabbed())
810                 break;
811             if (e->xcrossing.mode == NotifyGrab ||
812                 e->xcrossing.mode == NotifyUngrab)
813             {
814                 ob_debug_type(OB_DEBUG_FOCUS,
815                               "%sNotify mode %d detail %d on %lx IGNORED\n",
816                               (e->type == EnterNotify ? "Enter" : "Leave"),
817                               e->xcrossing.mode,
818                               e->xcrossing.detail, client?client->window:0);
819             } else {
820                 ob_debug_type(OB_DEBUG_FOCUS,
821                               "%sNotify mode %d detail %d on %lx, "
822                               "focusing window: %d\n",
823                               (e->type == EnterNotify ? "Enter" : "Leave"),
824                               e->xcrossing.mode,
825                               e->xcrossing.detail, (client?client->window:0),
826                               !nofocus);
827                 if (!nofocus && config_focus_follow)
828                     event_enter_client(client);
829             }
830             break;
831         default:
832             break;
833         }
834         break;
835     }
836     case ConfigureRequest:
837         /* compress these */
838         while (XCheckTypedWindowEvent(ob_display, client->window,
839                                       ConfigureRequest, &ce)) {
840             ++i;
841             /* XXX if this causes bad things.. we can compress config req's
842                with the same mask. */
843             e->xconfigurerequest.value_mask |=
844                 ce.xconfigurerequest.value_mask;
845             if (ce.xconfigurerequest.value_mask & CWX)
846                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
847             if (ce.xconfigurerequest.value_mask & CWY)
848                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
849             if (ce.xconfigurerequest.value_mask & CWWidth)
850                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
851             if (ce.xconfigurerequest.value_mask & CWHeight)
852                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
853             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
854                 e->xconfigurerequest.border_width =
855                     ce.xconfigurerequest.border_width;
856             if (ce.xconfigurerequest.value_mask & CWStackMode)
857                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
858         }
859
860         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
861         if (client->iconic || client->shaded) return;
862
863         /* resize, then move, as specified in the EWMH section 7.7 */
864         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
865                                                CWX | CWY |
866                                                CWBorderWidth)) {
867             gint x, y, w, h;
868             ObCorner corner;
869
870             if (e->xconfigurerequest.value_mask & CWBorderWidth)
871                 client->border_width = e->xconfigurerequest.border_width;
872
873             x = (e->xconfigurerequest.value_mask & CWX) ?
874                 e->xconfigurerequest.x : client->area.x;
875             y = (e->xconfigurerequest.value_mask & CWY) ?
876                 e->xconfigurerequest.y : client->area.y;
877             w = (e->xconfigurerequest.value_mask & CWWidth) ?
878                 e->xconfigurerequest.width : client->area.width;
879             h = (e->xconfigurerequest.value_mask & CWHeight) ?
880                 e->xconfigurerequest.height : client->area.height;
881
882             {
883                 gint newx = x;
884                 gint newy = y;
885                 gint fw = w +
886                      client->frame->size.left + client->frame->size.right;
887                 gint fh = h +
888                      client->frame->size.top + client->frame->size.bottom;
889                 /* make this rude for size-only changes but not for position
890                    changes.. */
891                 gboolean moving = ((e->xconfigurerequest.value_mask & CWX) ||
892                                    (e->xconfigurerequest.value_mask & CWY));
893
894                 client_find_onscreen(client, &newx, &newy, fw, fh,
895                                      !moving);
896                 if (e->xconfigurerequest.value_mask & CWX)
897                     x = newx;
898                 if (e->xconfigurerequest.value_mask & CWY)
899                     y = newy;
900             }
901
902             switch (client->gravity) {
903             case NorthEastGravity:
904             case EastGravity:
905                 corner = OB_CORNER_TOPRIGHT;
906                 break;
907             case SouthWestGravity:
908             case SouthGravity:
909                 corner = OB_CORNER_BOTTOMLEFT;
910                 break;
911             case SouthEastGravity:
912                 corner = OB_CORNER_BOTTOMRIGHT;
913                 break;
914             default:     /* NorthWest, Static, etc */
915                 corner = OB_CORNER_TOPLEFT;
916             }
917
918             client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
919                                   TRUE);
920         }
921
922         if (e->xconfigurerequest.value_mask & CWStackMode) {
923             switch (e->xconfigurerequest.detail) {
924             case Below:
925             case BottomIf:
926                 /* Apps are so rude. And this is totally disconnected from
927                    activation/focus. Bleh. */
928                 /*client_lower(client);*/
929                 break;
930
931             case Above:
932             case TopIf:
933             default:
934                 /* Apps are so rude. And this is totally disconnected from
935                    activation/focus. Bleh. */
936                 /*client_raise(client);*/
937                 break;
938             }
939         }
940         break;
941     case UnmapNotify:
942         if (client->ignore_unmaps) {
943             client->ignore_unmaps--;
944             break;
945         }
946         ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
947                  "ignores left %d\n",
948                  client->window, e->xunmap.event, e->xunmap.from_configure,
949                  client->ignore_unmaps);
950         client_unmanage(client);
951         break;
952     case DestroyNotify:
953         ob_debug("DestroyNotify for window 0x%x\n", client->window);
954         client_unmanage(client);
955         break;
956     case ReparentNotify:
957         /* this is when the client is first taken captive in the frame */
958         if (e->xreparent.parent == client->frame->plate) break;
959
960         /*
961           This event is quite rare and is usually handled in unmapHandler.
962           However, if the window is unmapped when the reparent event occurs,
963           the window manager never sees it because an unmap event is not sent
964           to an already unmapped window.
965         */
966
967         /* we don't want the reparent event, put it back on the stack for the
968            X server to deal with after we unmanage the window */
969         XPutBackEvent(ob_display, e);
970      
971         ob_debug("ReparentNotify for window 0x%x\n", client->window);
972         client_unmanage(client);
973         break;
974     case MapRequest:
975         ob_debug("MapRequest for 0x%lx\n", client->window);
976         if (!client->iconic) break; /* this normally doesn't happen, but if it
977                                        does, we don't want it!
978                                        it can happen now when the window is on
979                                        another desktop, but we still don't
980                                        want it! */
981         client_activate(client, FALSE, TRUE);
982         break;
983     case ClientMessage:
984         /* validate cuz we query stuff off the client here */
985         if (!client_validate(client)) break;
986
987         if (e->xclient.format != 32) return;
988
989         msgtype = e->xclient.message_type;
990         if (msgtype == prop_atoms.wm_change_state) {
991             /* compress changes into a single change */
992             while (XCheckTypedWindowEvent(ob_display, client->window,
993                                           e->type, &ce)) {
994                 /* XXX: it would be nice to compress ALL messages of a
995                    type, not just messages in a row without other
996                    message types between. */
997                 if (ce.xclient.message_type != msgtype) {
998                     XPutBackEvent(ob_display, &ce);
999                     break;
1000                 }
1001                 e->xclient = ce.xclient;
1002             }
1003             client_set_wm_state(client, e->xclient.data.l[0]);
1004         } else if (msgtype == prop_atoms.net_wm_desktop) {
1005             /* compress changes into a single change */
1006             while (XCheckTypedWindowEvent(ob_display, client->window,
1007                                           e->type, &ce)) {
1008                 /* XXX: it would be nice to compress ALL messages of a
1009                    type, not just messages in a row without other
1010                    message types between. */
1011                 if (ce.xclient.message_type != msgtype) {
1012                     XPutBackEvent(ob_display, &ce);
1013                     break;
1014                 }
1015                 e->xclient = ce.xclient;
1016             }
1017             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
1018                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
1019                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
1020                                    FALSE);
1021         } else if (msgtype == prop_atoms.net_wm_state) {
1022             /* can't compress these */
1023             ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1024                      (e->xclient.data.l[0] == 0 ? "Remove" :
1025                       e->xclient.data.l[0] == 1 ? "Add" :
1026                       e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
1027                      e->xclient.data.l[1], e->xclient.data.l[2],
1028                      client->window);
1029             client_set_state(client, e->xclient.data.l[0],
1030                              e->xclient.data.l[1], e->xclient.data.l[2]);
1031         } else if (msgtype == prop_atoms.net_close_window) {
1032             ob_debug("net_close_window for 0x%lx\n", client->window);
1033             client_close(client);
1034         } else if (msgtype == prop_atoms.net_active_window) {
1035             ob_debug("net_active_window for 0x%lx source=%s\n",
1036                      client->window,
1037                      (e->xclient.data.l[0] == 0 ? "unknown" :
1038                       (e->xclient.data.l[0] == 1 ? "application" :
1039                        (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
1040             /* XXX make use of data.l[2] ! */
1041             event_curtime = e->xclient.data.l[1];
1042             client_activate(client, FALSE,
1043                             (e->xclient.data.l[0] == 0 ||
1044                              e->xclient.data.l[0] == 2));
1045         } else if (msgtype == prop_atoms.net_wm_moveresize) {
1046             ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1047                      client->window, e->xclient.data.l[2]);
1048             if ((Atom)e->xclient.data.l[2] ==
1049                 prop_atoms.net_wm_moveresize_size_topleft ||
1050                 (Atom)e->xclient.data.l[2] ==
1051                 prop_atoms.net_wm_moveresize_size_top ||
1052                 (Atom)e->xclient.data.l[2] ==
1053                 prop_atoms.net_wm_moveresize_size_topright ||
1054                 (Atom)e->xclient.data.l[2] ==
1055                 prop_atoms.net_wm_moveresize_size_right ||
1056                 (Atom)e->xclient.data.l[2] ==
1057                 prop_atoms.net_wm_moveresize_size_right ||
1058                 (Atom)e->xclient.data.l[2] ==
1059                 prop_atoms.net_wm_moveresize_size_bottomright ||
1060                 (Atom)e->xclient.data.l[2] ==
1061                 prop_atoms.net_wm_moveresize_size_bottom ||
1062                 (Atom)e->xclient.data.l[2] ==
1063                 prop_atoms.net_wm_moveresize_size_bottomleft ||
1064                 (Atom)e->xclient.data.l[2] ==
1065                 prop_atoms.net_wm_moveresize_size_left ||
1066                 (Atom)e->xclient.data.l[2] ==
1067                 prop_atoms.net_wm_moveresize_move ||
1068                 (Atom)e->xclient.data.l[2] ==
1069                 prop_atoms.net_wm_moveresize_size_keyboard ||
1070                 (Atom)e->xclient.data.l[2] ==
1071                 prop_atoms.net_wm_moveresize_move_keyboard) {
1072
1073                 moveresize_start(client, e->xclient.data.l[0],
1074                                  e->xclient.data.l[1], e->xclient.data.l[3],
1075                                  e->xclient.data.l[2]);
1076             }
1077             else if ((Atom)e->xclient.data.l[2] ==
1078                      prop_atoms.net_wm_moveresize_cancel)
1079                 moveresize_end(TRUE);
1080         } else if (msgtype == prop_atoms.net_moveresize_window) {
1081             gint oldg = client->gravity;
1082             gint tmpg, x, y, w, h;
1083
1084             if (e->xclient.data.l[0] & 0xff)
1085                 tmpg = e->xclient.data.l[0] & 0xff;
1086             else
1087                 tmpg = oldg;
1088
1089             if (e->xclient.data.l[0] & 1 << 8)
1090                 x = e->xclient.data.l[1];
1091             else
1092                 x = client->area.x;
1093             if (e->xclient.data.l[0] & 1 << 9)
1094                 y = e->xclient.data.l[2];
1095             else
1096                 y = client->area.y;
1097             if (e->xclient.data.l[0] & 1 << 10)
1098                 w = e->xclient.data.l[3];
1099             else
1100                 w = client->area.width;
1101             if (e->xclient.data.l[0] & 1 << 11)
1102                 h = e->xclient.data.l[4];
1103             else
1104                 h = client->area.height;
1105             client->gravity = tmpg;
1106
1107             {
1108                 gint newx = x;
1109                 gint newy = y;
1110                 gint fw = w +
1111                      client->frame->size.left + client->frame->size.right;
1112                 gint fh = h +
1113                      client->frame->size.top + client->frame->size.bottom;
1114                 client_find_onscreen(client, &newx, &newy, fw, fh,
1115                                      client_normal(client));
1116                 if (e->xclient.data.l[0] & 1 << 8)
1117                     x = newx;
1118                 if (e->xclient.data.l[0] & 1 << 9)
1119                     y = newy;
1120             }
1121
1122             client_configure(client, OB_CORNER_TOPLEFT,
1123                              x, y, w, h, FALSE, TRUE);
1124
1125             client->gravity = oldg;
1126         }
1127         break;
1128     case PropertyNotify:
1129         /* validate cuz we query stuff off the client here */
1130         if (!client_validate(client)) break;
1131   
1132         /* compress changes to a single property into a single change */
1133         while (XCheckTypedWindowEvent(ob_display, client->window,
1134                                       e->type, &ce)) {
1135             Atom a, b;
1136
1137             /* XXX: it would be nice to compress ALL changes to a property,
1138                not just changes in a row without other props between. */
1139
1140             a = ce.xproperty.atom;
1141             b = e->xproperty.atom;
1142
1143             if (a == b)
1144                 continue;
1145             if ((a == prop_atoms.net_wm_name ||
1146                  a == prop_atoms.wm_name ||
1147                  a == prop_atoms.net_wm_icon_name ||
1148                  a == prop_atoms.wm_icon_name)
1149                 &&
1150                 (b == prop_atoms.net_wm_name ||
1151                  b == prop_atoms.wm_name ||
1152                  b == prop_atoms.net_wm_icon_name ||
1153                  b == prop_atoms.wm_icon_name)) {
1154                 continue;
1155             }
1156             if (a == prop_atoms.net_wm_icon &&
1157                 b == prop_atoms.net_wm_icon)
1158                 continue;
1159
1160             XPutBackEvent(ob_display, &ce);
1161             break;
1162         }
1163
1164         msgtype = e->xproperty.atom;
1165         if (msgtype == XA_WM_NORMAL_HINTS) {
1166             client_update_normal_hints(client);
1167             /* normal hints can make a window non-resizable */
1168             client_setup_decor_and_functions(client);
1169         } else if (msgtype == XA_WM_HINTS) {
1170             client_update_wmhints(client);
1171         } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1172             client_update_transient_for(client);
1173             client_get_type(client);
1174             /* type may have changed, so update the layer */
1175             client_calc_layer(client);
1176             client_setup_decor_and_functions(client);
1177         } else if (msgtype == prop_atoms.net_wm_name ||
1178                    msgtype == prop_atoms.wm_name ||
1179                    msgtype == prop_atoms.net_wm_icon_name ||
1180                    msgtype == prop_atoms.wm_icon_name) {
1181             client_update_title(client);
1182         } else if (msgtype == prop_atoms.wm_class) {
1183             client_update_class(client);
1184         } else if (msgtype == prop_atoms.wm_protocols) {
1185             client_update_protocols(client);
1186             client_setup_decor_and_functions(client);
1187         }
1188         else if (msgtype == prop_atoms.net_wm_strut) {
1189             client_update_strut(client);
1190         }
1191         else if (msgtype == prop_atoms.net_wm_icon) {
1192             client_update_icons(client);
1193         }
1194         else if (msgtype == prop_atoms.net_wm_user_time) {
1195             client_update_user_time(client);
1196         }
1197         else if (msgtype == prop_atoms.sm_client_id) {
1198             client_update_sm_client_id(client);
1199         }
1200     default:
1201         ;
1202 #ifdef SHAPE
1203         if (extensions_shape && e->type == extensions_shape_event_basep) {
1204             client->shaped = ((XShapeEvent*)e)->shaped;
1205             frame_adjust_shape(client->frame);
1206         }
1207 #endif
1208     }
1209 }
1210
1211 static void event_handle_dock(ObDock *s, XEvent *e)
1212 {
1213     switch (e->type) {
1214     case ButtonPress:
1215         if (e->xbutton.button == 1)
1216             stacking_raise(DOCK_AS_WINDOW(s));
1217         else if (e->xbutton.button == 2)
1218             stacking_lower(DOCK_AS_WINDOW(s));
1219         break;
1220     case EnterNotify:
1221         dock_hide(FALSE);
1222         break;
1223     case LeaveNotify:
1224         dock_hide(TRUE);
1225         break;
1226     }
1227 }
1228
1229 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1230 {
1231     switch (e->type) {
1232     case MotionNotify:
1233         dock_app_drag(app, &e->xmotion);
1234         break;
1235     case UnmapNotify:
1236         if (app->ignore_unmaps) {
1237             app->ignore_unmaps--;
1238             break;
1239         }
1240         dock_remove(app, TRUE);
1241         break;
1242     case DestroyNotify:
1243         dock_remove(app, FALSE);
1244         break;
1245     case ReparentNotify:
1246         dock_remove(app, FALSE);
1247         break;
1248     case ConfigureNotify:
1249         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1250         break;
1251     }
1252 }
1253
1254 ObMenuFrame* find_active_menu()
1255 {
1256     GList *it;
1257     ObMenuFrame *ret = NULL;
1258
1259     for (it = menu_frame_visible; it; it = g_list_next(it)) {
1260         ret = it->data;
1261         if (ret->selected)
1262             break;
1263         ret = NULL;
1264     }
1265     return ret;
1266 }
1267
1268 ObMenuFrame* find_active_or_last_menu()
1269 {
1270     ObMenuFrame *ret = NULL;
1271
1272     ret = find_active_menu();
1273     if (!ret && menu_frame_visible)
1274         ret = menu_frame_visible->data;
1275     return ret;
1276 }
1277
1278 static void event_handle_menu(XEvent *ev)
1279 {
1280     ObMenuFrame *f;
1281     ObMenuEntryFrame *e;
1282
1283     switch (ev->type) {
1284     case ButtonRelease:
1285         if (menu_can_hide) {
1286             if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1287                                             ev->xbutton.y_root)))
1288                 menu_entry_frame_execute(e, ev->xbutton.state,
1289                                          ev->xbutton.time);
1290             else
1291                 menu_frame_hide_all();
1292         }
1293         break;
1294     case EnterNotify:
1295         if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1296             if (e->ignore_enters)
1297                 --e->ignore_enters;
1298             else
1299                 menu_frame_select(e->frame, e);
1300         }
1301         break;
1302     case LeaveNotify:
1303         if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
1304             (f = find_active_menu()) && f->selected == e &&
1305             e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1306         {
1307             menu_frame_select(e->frame, NULL);
1308         }
1309     case MotionNotify:   
1310         if ((e = menu_entry_frame_under(ev->xmotion.x_root,   
1311                                         ev->xmotion.y_root)))
1312             menu_frame_select(e->frame, e);   
1313         break;
1314     case KeyPress:
1315         if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1316             menu_frame_hide_all();
1317         else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1318             ObMenuFrame *f;
1319             if ((f = find_active_menu()))
1320                 menu_entry_frame_execute(f->selected, ev->xkey.state,
1321                                          ev->xkey.time);
1322         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1323             ObMenuFrame *f;
1324             if ((f = find_active_or_last_menu()) && f->parent)
1325                 menu_frame_select(f, NULL);
1326         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1327             ObMenuFrame *f;
1328             if ((f = find_active_or_last_menu()) && f->child)
1329                 menu_frame_select_next(f->child);
1330         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1331             ObMenuFrame *f;
1332             if ((f = find_active_or_last_menu()))
1333                 menu_frame_select_previous(f);
1334         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1335             ObMenuFrame *f;
1336             if ((f = find_active_or_last_menu()))
1337                 menu_frame_select_next(f);
1338         }
1339         break;
1340     }
1341 }
1342
1343 static gboolean menu_hide_delay_func(gpointer data)
1344 {
1345     menu_can_hide = TRUE;
1346     return FALSE; /* no repeat */
1347 }
1348
1349 static void focus_delay_dest(gpointer data)
1350 {
1351     g_free(data);
1352 }
1353
1354 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
1355 {
1356     const ObFocusDelayData *f1 = d1, *f2 = d2;
1357     return f1->client == f2->client;
1358 }
1359
1360 static gboolean focus_delay_func(gpointer data)
1361 {
1362     ObFocusDelayData *d = data;
1363     Time old = event_curtime;
1364
1365     event_curtime = d->time;
1366     if (focus_client != d->client) {
1367         if (client_focus(d->client) && config_focus_raise)
1368             client_raise(d->client);
1369     }
1370     event_curtime = old;
1371     return FALSE; /* no repeat */
1372 }
1373
1374 static void focus_delay_client_dest(ObClient *client, gpointer data)
1375 {
1376     ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1377                                      client, FALSE);
1378 }
1379
1380 static void event_client_dest(ObClient *client, gpointer data)
1381 {
1382     if (client == focus_hilite)
1383         focus_hilite = NULL;
1384 }
1385
1386 void event_halt_focus_delay()
1387 {
1388     ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1389 }
1390
1391 void event_ignore_queued_enters()
1392 {
1393     GSList *saved = NULL, *it;
1394     XEvent *e;
1395                 
1396     XSync(ob_display, FALSE);
1397
1398     /* count the events */
1399     while (TRUE) {
1400         e = g_new(XEvent, 1);
1401         if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1402             ObWindow *win;
1403             
1404             win = g_hash_table_lookup(window_map, &e->xany.window);
1405             if (win && WINDOW_IS_CLIENT(win))
1406                 ++ignore_enter_focus;
1407             
1408             saved = g_slist_append(saved, e);
1409         } else {
1410             g_free(e);
1411             break;
1412         }
1413     }
1414     /* put the events back */
1415     for (it = saved; it; it = g_slist_next(it)) {
1416         XPutBackEvent(ob_display, it->data);
1417         g_free(it->data);
1418     }
1419     g_slist_free(saved);
1420 }
1421
1422 gboolean event_time_after(Time t1, Time t2)
1423 {
1424     g_assert(t1 != CurrentTime);
1425     g_assert(t2 != CurrentTime);
1426
1427     /*
1428       Timestamp values wrap around (after about 49.7 days). The server, given
1429       its current time is represented by timestamp T, always interprets
1430       timestamps from clients by treating half of the timestamp space as being
1431       later in time than T.
1432       - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1433     */
1434
1435     /* TIME_HALF is half of the number space of a Time type variable */
1436 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1437
1438     if (t2 >= TIME_HALF)
1439         /* t2 is in the second half so t1 might wrap around and be smaller than
1440            t2 */
1441         return t1 >= t2 || t1 < (t2 + TIME_HALF);
1442     else
1443         /* t2 is in the first half so t1 has to come after it */
1444         return t1 >= t2 && t1 < (t2 + TIME_HALF);
1445 }