]> icculus.org git repositories - dana/openbox.git/blob - openbox/event.c
start on the current desktop when a wm was already running
[dana/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                     mouse_event(client, e);
595                 else if (e->type == KeyPress) {
596                     keyboard_event((focus_cycle_target ? focus_cycle_target :
597                                     (focus_hilite ? focus_hilite : client)),
598                                    e);
599                 }
600             }
601         }
602     }
603     /* if something happens and it's not from an XEvent, then we don't know
604        the time */
605     event_curtime = CurrentTime;
606 }
607
608 static void event_handle_root(XEvent *e)
609 {
610     Atom msgtype;
611      
612     switch(e->type) {
613     case SelectionClear:
614         ob_debug("Another WM has requested to replace us. Exiting.\n");
615         ob_exit_replace();
616         break;
617
618     case ClientMessage:
619         if (e->xclient.format != 32) break;
620
621         msgtype = e->xclient.message_type;
622         if (msgtype == prop_atoms.net_current_desktop) {
623             guint d = e->xclient.data.l[0];
624             if (d < screen_num_desktops) {
625                 event_curtime = e->xclient.data.l[1];
626                 ob_debug("SWITCH DESKTOP TIME: %d\n", event_curtime);
627                 screen_set_desktop(d);
628             }
629         } else if (msgtype == prop_atoms.net_number_of_desktops) {
630             guint d = e->xclient.data.l[0];
631             if (d > 0)
632                 screen_set_num_desktops(d);
633         } else if (msgtype == prop_atoms.net_showing_desktop) {
634             screen_show_desktop(e->xclient.data.l[0] != 0);
635         } else if (msgtype == prop_atoms.ob_control) {
636             if (e->xclient.data.l[0] == 1)
637                 ob_reconfigure();
638             else if (e->xclient.data.l[0] == 2)
639                 ob_restart();
640         }
641         break;
642     case PropertyNotify:
643         if (e->xproperty.atom == prop_atoms.net_desktop_names)
644             screen_update_desktop_names();
645         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
646             screen_update_layout();
647         break;
648     case ConfigureNotify:
649 #ifdef XRANDR
650         XRRUpdateConfiguration(e);
651 #endif
652         screen_resize();
653         break;
654     default:
655         ;
656     }
657 }
658
659 static void event_handle_group(ObGroup *group, XEvent *e)
660 {
661     GSList *it;
662
663     g_assert(e->type == PropertyNotify);
664
665     for (it = group->members; it; it = g_slist_next(it))
666         event_handle_client(it->data, e);
667 }
668
669 void event_enter_client(ObClient *client)
670 {
671     g_assert(config_focus_follow);
672
673     if (client_normal(client) && client_can_focus(client)) {
674         if (config_focus_delay) {
675             ObFocusDelayData *data;
676
677             ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
678
679             data = g_new(ObFocusDelayData, 1);
680             data->client = client;
681             data->time = event_curtime;
682
683             ob_main_loop_timeout_add(ob_main_loop,
684                                      config_focus_delay,
685                                      focus_delay_func,
686                                      data, focus_delay_cmp, focus_delay_dest);
687         } else {
688             ObFocusDelayData data;
689             data.client = client;
690             data.time = event_curtime;
691             focus_delay_func(&data);
692         }
693     }
694 }
695
696 static void event_handle_client(ObClient *client, XEvent *e)
697 {
698     XEvent ce;
699     Atom msgtype;
700     gint i=0;
701     ObFrameContext con;
702      
703     switch (e->type) {
704     case VisibilityNotify:
705         client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
706         break;
707     case ButtonPress:
708     case ButtonRelease:
709         /* Wheel buttons don't draw because they are an instant click, so it
710            is a waste of resources to go drawing it. */
711         if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
712             con = frame_context(client, e->xbutton.window);
713             con = mouse_button_frame_context(con, e->xbutton.button);
714             switch (con) {
715             case OB_FRAME_CONTEXT_MAXIMIZE:
716                 client->frame->max_press = (e->type == ButtonPress);
717                 framerender_frame(client->frame);
718                 break;
719             case OB_FRAME_CONTEXT_CLOSE:
720                 client->frame->close_press = (e->type == ButtonPress);
721                 framerender_frame(client->frame);
722                 break;
723             case OB_FRAME_CONTEXT_ICONIFY:
724                 client->frame->iconify_press = (e->type == ButtonPress);
725                 framerender_frame(client->frame);
726                 break;
727             case OB_FRAME_CONTEXT_ALLDESKTOPS:
728                 client->frame->desk_press = (e->type == ButtonPress);
729                 framerender_frame(client->frame);
730                 break; 
731             case OB_FRAME_CONTEXT_SHADE:
732                 client->frame->shade_press = (e->type == ButtonPress);
733                 framerender_frame(client->frame);
734                 break;
735             default:
736                 /* nothing changes with clicks for any other contexts */
737                 break;
738             }
739         }
740         break;
741     case LeaveNotify:
742         con = frame_context(client, e->xcrossing.window);
743         switch (con) {
744         case OB_FRAME_CONTEXT_MAXIMIZE:
745             client->frame->max_hover = FALSE;
746             frame_adjust_state(client->frame);
747             break;
748         case OB_FRAME_CONTEXT_ALLDESKTOPS:
749             client->frame->desk_hover = FALSE;
750             frame_adjust_state(client->frame);
751             break;
752         case OB_FRAME_CONTEXT_SHADE:
753             client->frame->shade_hover = FALSE;
754             frame_adjust_state(client->frame);
755             break;
756         case OB_FRAME_CONTEXT_ICONIFY:
757             client->frame->iconify_hover = FALSE;
758             frame_adjust_state(client->frame);
759             break;
760         case OB_FRAME_CONTEXT_CLOSE:
761             client->frame->close_hover = FALSE;
762             frame_adjust_state(client->frame);
763             break;
764         case OB_FRAME_CONTEXT_FRAME:
765             if (config_focus_follow && config_focus_delay)
766                 ob_main_loop_timeout_remove_data(ob_main_loop,
767                                                  focus_delay_func,
768                                                  client, FALSE);
769             break;
770         default:
771             break;
772         }
773         break;
774     case EnterNotify:
775     {
776         gboolean nofocus = FALSE;
777
778         if (ignore_enter_focus) {
779             ignore_enter_focus--;
780             nofocus = TRUE;
781         }
782
783         con = frame_context(client, e->xcrossing.window);
784         switch (con) {
785         case OB_FRAME_CONTEXT_MAXIMIZE:
786             client->frame->max_hover = TRUE;
787             frame_adjust_state(client->frame);
788             break;
789         case OB_FRAME_CONTEXT_ALLDESKTOPS:
790             client->frame->desk_hover = TRUE;
791             frame_adjust_state(client->frame);
792             break;
793         case OB_FRAME_CONTEXT_SHADE:
794             client->frame->shade_hover = TRUE;
795             frame_adjust_state(client->frame);
796             break;
797         case OB_FRAME_CONTEXT_ICONIFY:
798             client->frame->iconify_hover = TRUE;
799             frame_adjust_state(client->frame);
800             break;
801         case OB_FRAME_CONTEXT_CLOSE:
802             client->frame->close_hover = TRUE;
803             frame_adjust_state(client->frame);
804             break;
805         case OB_FRAME_CONTEXT_FRAME:
806             if (e->xcrossing.mode == NotifyGrab ||
807                 e->xcrossing.mode == NotifyUngrab)
808             {
809                 ob_debug_type(OB_DEBUG_FOCUS,
810                               "%sNotify mode %d detail %d on %lx IGNORED\n",
811                               (e->type == EnterNotify ? "Enter" : "Leave"),
812                               e->xcrossing.mode,
813                               e->xcrossing.detail, client?client->window:0);
814             } else {
815                 ob_debug_type(OB_DEBUG_FOCUS,
816                               "%sNotify mode %d detail %d on %lx, "
817                               "focusing window: %d\n",
818                               (e->type == EnterNotify ? "Enter" : "Leave"),
819                               e->xcrossing.mode,
820                               e->xcrossing.detail, (client?client->window:0),
821                               !nofocus);
822                 if (!nofocus && config_focus_follow)
823                     event_enter_client(client);
824             }
825             break;
826         default:
827             break;
828         }
829         break;
830     }
831     case ConfigureRequest:
832         /* compress these */
833         while (XCheckTypedWindowEvent(ob_display, client->window,
834                                       ConfigureRequest, &ce)) {
835             ++i;
836             /* XXX if this causes bad things.. we can compress config req's
837                with the same mask. */
838             e->xconfigurerequest.value_mask |=
839                 ce.xconfigurerequest.value_mask;
840             if (ce.xconfigurerequest.value_mask & CWX)
841                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
842             if (ce.xconfigurerequest.value_mask & CWY)
843                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
844             if (ce.xconfigurerequest.value_mask & CWWidth)
845                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
846             if (ce.xconfigurerequest.value_mask & CWHeight)
847                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
848             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
849                 e->xconfigurerequest.border_width =
850                     ce.xconfigurerequest.border_width;
851             if (ce.xconfigurerequest.value_mask & CWStackMode)
852                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
853         }
854
855         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
856         if (client->iconic || client->shaded) return;
857
858         /* resize, then move, as specified in the EWMH section 7.7 */
859         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
860                                                CWX | CWY |
861                                                CWBorderWidth)) {
862             gint x, y, w, h;
863             ObCorner corner;
864
865             if (e->xconfigurerequest.value_mask & CWBorderWidth)
866                 client->border_width = e->xconfigurerequest.border_width;
867
868             x = (e->xconfigurerequest.value_mask & CWX) ?
869                 e->xconfigurerequest.x : client->area.x;
870             y = (e->xconfigurerequest.value_mask & CWY) ?
871                 e->xconfigurerequest.y : client->area.y;
872             w = (e->xconfigurerequest.value_mask & CWWidth) ?
873                 e->xconfigurerequest.width : client->area.width;
874             h = (e->xconfigurerequest.value_mask & CWHeight) ?
875                 e->xconfigurerequest.height : client->area.height;
876
877             {
878                 gint newx = x;
879                 gint newy = y;
880                 gint fw = w +
881                      client->frame->size.left + client->frame->size.right;
882                 gint fh = h +
883                      client->frame->size.top + client->frame->size.bottom;
884                 /* make this rude for size-only changes but not for position
885                    changes.. */
886                 gboolean moving = ((e->xconfigurerequest.value_mask & CWX) ||
887                                    (e->xconfigurerequest.value_mask & CWY));
888
889                 client_find_onscreen(client, &newx, &newy, fw, fh,
890                                      !moving);
891                 if (e->xconfigurerequest.value_mask & CWX)
892                     x = newx;
893                 if (e->xconfigurerequest.value_mask & CWY)
894                     y = newy;
895             }
896
897             switch (client->gravity) {
898             case NorthEastGravity:
899             case EastGravity:
900                 corner = OB_CORNER_TOPRIGHT;
901                 break;
902             case SouthWestGravity:
903             case SouthGravity:
904                 corner = OB_CORNER_BOTTOMLEFT;
905                 break;
906             case SouthEastGravity:
907                 corner = OB_CORNER_BOTTOMRIGHT;
908                 break;
909             default:     /* NorthWest, Static, etc */
910                 corner = OB_CORNER_TOPLEFT;
911             }
912
913             client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
914                                   TRUE);
915         }
916
917         if (e->xconfigurerequest.value_mask & CWStackMode) {
918             switch (e->xconfigurerequest.detail) {
919             case Below:
920             case BottomIf:
921                 /* Apps are so rude. And this is totally disconnected from
922                    activation/focus. Bleh. */
923                 /*client_lower(client);*/
924                 break;
925
926             case Above:
927             case TopIf:
928             default:
929                 /* Apps are so rude. And this is totally disconnected from
930                    activation/focus. Bleh. */
931                 /*client_raise(client);*/
932                 break;
933             }
934         }
935         break;
936     case UnmapNotify:
937         if (client->ignore_unmaps) {
938             client->ignore_unmaps--;
939             break;
940         }
941         ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
942                  "ignores left %d\n",
943                  client->window, e->xunmap.event, e->xunmap.from_configure,
944                  client->ignore_unmaps);
945         client_unmanage(client);
946         break;
947     case DestroyNotify:
948         ob_debug("DestroyNotify for window 0x%x\n", client->window);
949         client_unmanage(client);
950         break;
951     case ReparentNotify:
952         /* this is when the client is first taken captive in the frame */
953         if (e->xreparent.parent == client->frame->plate) break;
954
955         /*
956           This event is quite rare and is usually handled in unmapHandler.
957           However, if the window is unmapped when the reparent event occurs,
958           the window manager never sees it because an unmap event is not sent
959           to an already unmapped window.
960         */
961
962         /* we don't want the reparent event, put it back on the stack for the
963            X server to deal with after we unmanage the window */
964         XPutBackEvent(ob_display, e);
965      
966         ob_debug("ReparentNotify for window 0x%x\n", client->window);
967         client_unmanage(client);
968         break;
969     case MapRequest:
970         ob_debug("MapRequest for 0x%lx\n", client->window);
971         if (!client->iconic) break; /* this normally doesn't happen, but if it
972                                        does, we don't want it!
973                                        it can happen now when the window is on
974                                        another desktop, but we still don't
975                                        want it! */
976         client_activate(client, FALSE, TRUE);
977         break;
978     case ClientMessage:
979         /* validate cuz we query stuff off the client here */
980         if (!client_validate(client)) break;
981
982         if (e->xclient.format != 32) return;
983
984         msgtype = e->xclient.message_type;
985         if (msgtype == prop_atoms.wm_change_state) {
986             /* compress changes into a single change */
987             while (XCheckTypedWindowEvent(ob_display, client->window,
988                                           e->type, &ce)) {
989                 /* XXX: it would be nice to compress ALL messages of a
990                    type, not just messages in a row without other
991                    message types between. */
992                 if (ce.xclient.message_type != msgtype) {
993                     XPutBackEvent(ob_display, &ce);
994                     break;
995                 }
996                 e->xclient = ce.xclient;
997             }
998             client_set_wm_state(client, e->xclient.data.l[0]);
999         } else if (msgtype == prop_atoms.net_wm_desktop) {
1000             /* compress changes into a single change */
1001             while (XCheckTypedWindowEvent(ob_display, client->window,
1002                                           e->type, &ce)) {
1003                 /* XXX: it would be nice to compress ALL messages of a
1004                    type, not just messages in a row without other
1005                    message types between. */
1006                 if (ce.xclient.message_type != msgtype) {
1007                     XPutBackEvent(ob_display, &ce);
1008                     break;
1009                 }
1010                 e->xclient = ce.xclient;
1011             }
1012             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
1013                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
1014                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
1015                                    FALSE);
1016         } else if (msgtype == prop_atoms.net_wm_state) {
1017             /* can't compress these */
1018             ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1019                      (e->xclient.data.l[0] == 0 ? "Remove" :
1020                       e->xclient.data.l[0] == 1 ? "Add" :
1021                       e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
1022                      e->xclient.data.l[1], e->xclient.data.l[2],
1023                      client->window);
1024             client_set_state(client, e->xclient.data.l[0],
1025                              e->xclient.data.l[1], e->xclient.data.l[2]);
1026         } else if (msgtype == prop_atoms.net_close_window) {
1027             ob_debug("net_close_window for 0x%lx\n", client->window);
1028             client_close(client);
1029         } else if (msgtype == prop_atoms.net_active_window) {
1030             ob_debug("net_active_window for 0x%lx source=%s\n",
1031                      client->window,
1032                      (e->xclient.data.l[0] == 0 ? "unknown" :
1033                       (e->xclient.data.l[0] == 1 ? "application" :
1034                        (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
1035             /* XXX make use of data.l[2] ! */
1036             event_curtime = e->xclient.data.l[1];
1037             client_activate(client, FALSE,
1038                             (e->xclient.data.l[0] == 0 ||
1039                              e->xclient.data.l[0] == 2));
1040         } else if (msgtype == prop_atoms.net_wm_moveresize) {
1041             ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1042                      client->window, e->xclient.data.l[2]);
1043             if ((Atom)e->xclient.data.l[2] ==
1044                 prop_atoms.net_wm_moveresize_size_topleft ||
1045                 (Atom)e->xclient.data.l[2] ==
1046                 prop_atoms.net_wm_moveresize_size_top ||
1047                 (Atom)e->xclient.data.l[2] ==
1048                 prop_atoms.net_wm_moveresize_size_topright ||
1049                 (Atom)e->xclient.data.l[2] ==
1050                 prop_atoms.net_wm_moveresize_size_right ||
1051                 (Atom)e->xclient.data.l[2] ==
1052                 prop_atoms.net_wm_moveresize_size_right ||
1053                 (Atom)e->xclient.data.l[2] ==
1054                 prop_atoms.net_wm_moveresize_size_bottomright ||
1055                 (Atom)e->xclient.data.l[2] ==
1056                 prop_atoms.net_wm_moveresize_size_bottom ||
1057                 (Atom)e->xclient.data.l[2] ==
1058                 prop_atoms.net_wm_moveresize_size_bottomleft ||
1059                 (Atom)e->xclient.data.l[2] ==
1060                 prop_atoms.net_wm_moveresize_size_left ||
1061                 (Atom)e->xclient.data.l[2] ==
1062                 prop_atoms.net_wm_moveresize_move ||
1063                 (Atom)e->xclient.data.l[2] ==
1064                 prop_atoms.net_wm_moveresize_size_keyboard ||
1065                 (Atom)e->xclient.data.l[2] ==
1066                 prop_atoms.net_wm_moveresize_move_keyboard) {
1067
1068                 moveresize_start(client, e->xclient.data.l[0],
1069                                  e->xclient.data.l[1], e->xclient.data.l[3],
1070                                  e->xclient.data.l[2]);
1071             }
1072             else if ((Atom)e->xclient.data.l[2] ==
1073                      prop_atoms.net_wm_moveresize_cancel)
1074                 moveresize_end(TRUE);
1075         } else if (msgtype == prop_atoms.net_moveresize_window) {
1076             gint oldg = client->gravity;
1077             gint tmpg, x, y, w, h;
1078
1079             if (e->xclient.data.l[0] & 0xff)
1080                 tmpg = e->xclient.data.l[0] & 0xff;
1081             else
1082                 tmpg = oldg;
1083
1084             if (e->xclient.data.l[0] & 1 << 8)
1085                 x = e->xclient.data.l[1];
1086             else
1087                 x = client->area.x;
1088             if (e->xclient.data.l[0] & 1 << 9)
1089                 y = e->xclient.data.l[2];
1090             else
1091                 y = client->area.y;
1092             if (e->xclient.data.l[0] & 1 << 10)
1093                 w = e->xclient.data.l[3];
1094             else
1095                 w = client->area.width;
1096             if (e->xclient.data.l[0] & 1 << 11)
1097                 h = e->xclient.data.l[4];
1098             else
1099                 h = client->area.height;
1100             client->gravity = tmpg;
1101
1102             {
1103                 gint newx = x;
1104                 gint newy = y;
1105                 gint fw = w +
1106                      client->frame->size.left + client->frame->size.right;
1107                 gint fh = h +
1108                      client->frame->size.top + client->frame->size.bottom;
1109                 client_find_onscreen(client, &newx, &newy, fw, fh,
1110                                      client_normal(client));
1111                 if (e->xclient.data.l[0] & 1 << 8)
1112                     x = newx;
1113                 if (e->xclient.data.l[0] & 1 << 9)
1114                     y = newy;
1115             }
1116
1117             client_configure(client, OB_CORNER_TOPLEFT,
1118                              x, y, w, h, FALSE, TRUE);
1119
1120             client->gravity = oldg;
1121         }
1122         break;
1123     case PropertyNotify:
1124         /* validate cuz we query stuff off the client here */
1125         if (!client_validate(client)) break;
1126   
1127         /* compress changes to a single property into a single change */
1128         while (XCheckTypedWindowEvent(ob_display, client->window,
1129                                       e->type, &ce)) {
1130             Atom a, b;
1131
1132             /* XXX: it would be nice to compress ALL changes to a property,
1133                not just changes in a row without other props between. */
1134
1135             a = ce.xproperty.atom;
1136             b = e->xproperty.atom;
1137
1138             if (a == b)
1139                 continue;
1140             if ((a == prop_atoms.net_wm_name ||
1141                  a == prop_atoms.wm_name ||
1142                  a == prop_atoms.net_wm_icon_name ||
1143                  a == prop_atoms.wm_icon_name)
1144                 &&
1145                 (b == prop_atoms.net_wm_name ||
1146                  b == prop_atoms.wm_name ||
1147                  b == prop_atoms.net_wm_icon_name ||
1148                  b == prop_atoms.wm_icon_name)) {
1149                 continue;
1150             }
1151             if (a == prop_atoms.net_wm_icon &&
1152                 b == prop_atoms.net_wm_icon)
1153                 continue;
1154
1155             XPutBackEvent(ob_display, &ce);
1156             break;
1157         }
1158
1159         msgtype = e->xproperty.atom;
1160         if (msgtype == XA_WM_NORMAL_HINTS) {
1161             client_update_normal_hints(client);
1162             /* normal hints can make a window non-resizable */
1163             client_setup_decor_and_functions(client);
1164         } else if (msgtype == XA_WM_HINTS) {
1165             client_update_wmhints(client);
1166         } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1167             client_update_transient_for(client);
1168             client_get_type(client);
1169             /* type may have changed, so update the layer */
1170             client_calc_layer(client);
1171             client_setup_decor_and_functions(client);
1172         } else if (msgtype == prop_atoms.net_wm_name ||
1173                    msgtype == prop_atoms.wm_name ||
1174                    msgtype == prop_atoms.net_wm_icon_name ||
1175                    msgtype == prop_atoms.wm_icon_name) {
1176             client_update_title(client);
1177         } else if (msgtype == prop_atoms.wm_class) {
1178             client_update_class(client);
1179         } else if (msgtype == prop_atoms.wm_protocols) {
1180             client_update_protocols(client);
1181             client_setup_decor_and_functions(client);
1182         }
1183         else if (msgtype == prop_atoms.net_wm_strut) {
1184             client_update_strut(client);
1185         }
1186         else if (msgtype == prop_atoms.net_wm_icon) {
1187             client_update_icons(client);
1188         }
1189         else if (msgtype == prop_atoms.net_wm_user_time) {
1190             client_update_user_time(client);
1191         }
1192         else if (msgtype == prop_atoms.sm_client_id) {
1193             client_update_sm_client_id(client);
1194         }
1195     default:
1196         ;
1197 #ifdef SHAPE
1198         if (extensions_shape && e->type == extensions_shape_event_basep) {
1199             client->shaped = ((XShapeEvent*)e)->shaped;
1200             frame_adjust_shape(client->frame);
1201         }
1202 #endif
1203     }
1204 }
1205
1206 static void event_handle_dock(ObDock *s, XEvent *e)
1207 {
1208     switch (e->type) {
1209     case ButtonPress:
1210         if (e->xbutton.button == 1)
1211             stacking_raise(DOCK_AS_WINDOW(s));
1212         else if (e->xbutton.button == 2)
1213             stacking_lower(DOCK_AS_WINDOW(s));
1214         break;
1215     case EnterNotify:
1216         dock_hide(FALSE);
1217         break;
1218     case LeaveNotify:
1219         dock_hide(TRUE);
1220         break;
1221     }
1222 }
1223
1224 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1225 {
1226     switch (e->type) {
1227     case MotionNotify:
1228         dock_app_drag(app, &e->xmotion);
1229         break;
1230     case UnmapNotify:
1231         if (app->ignore_unmaps) {
1232             app->ignore_unmaps--;
1233             break;
1234         }
1235         dock_remove(app, TRUE);
1236         break;
1237     case DestroyNotify:
1238         dock_remove(app, FALSE);
1239         break;
1240     case ReparentNotify:
1241         dock_remove(app, FALSE);
1242         break;
1243     case ConfigureNotify:
1244         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1245         break;
1246     }
1247 }
1248
1249 ObMenuFrame* find_active_menu()
1250 {
1251     GList *it;
1252     ObMenuFrame *ret = NULL;
1253
1254     for (it = menu_frame_visible; it; it = g_list_next(it)) {
1255         ret = it->data;
1256         if (ret->selected)
1257             break;
1258         ret = NULL;
1259     }
1260     return ret;
1261 }
1262
1263 ObMenuFrame* find_active_or_last_menu()
1264 {
1265     ObMenuFrame *ret = NULL;
1266
1267     ret = find_active_menu();
1268     if (!ret && menu_frame_visible)
1269         ret = menu_frame_visible->data;
1270     return ret;
1271 }
1272
1273 static void event_handle_menu(XEvent *ev)
1274 {
1275     ObMenuFrame *f;
1276     ObMenuEntryFrame *e;
1277
1278     switch (ev->type) {
1279     case ButtonRelease:
1280         if (menu_can_hide) {
1281             if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1282                                             ev->xbutton.y_root)))
1283                 menu_entry_frame_execute(e, ev->xbutton.state,
1284                                          ev->xbutton.time);
1285             else
1286                 menu_frame_hide_all();
1287         }
1288         break;
1289     case EnterNotify:
1290         if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1291             if (e->ignore_enters)
1292                 --e->ignore_enters;
1293             else
1294                 menu_frame_select(e->frame, e);
1295         }
1296         break;
1297     case LeaveNotify:
1298         if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
1299             (f = find_active_menu()) && f->selected == e &&
1300             e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1301         {
1302             menu_frame_select(e->frame, NULL);
1303         }
1304     case MotionNotify:   
1305         if ((e = menu_entry_frame_under(ev->xmotion.x_root,   
1306                                         ev->xmotion.y_root)))
1307             menu_frame_select(e->frame, e);   
1308         break;
1309     case KeyPress:
1310         if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1311             menu_frame_hide_all();
1312         else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1313             ObMenuFrame *f;
1314             if ((f = find_active_menu()))
1315                 menu_entry_frame_execute(f->selected, ev->xkey.state,
1316                                          ev->xkey.time);
1317         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1318             ObMenuFrame *f;
1319             if ((f = find_active_or_last_menu()) && f->parent)
1320                 menu_frame_select(f, NULL);
1321         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1322             ObMenuFrame *f;
1323             if ((f = find_active_or_last_menu()) && f->child)
1324                 menu_frame_select_next(f->child);
1325         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1326             ObMenuFrame *f;
1327             if ((f = find_active_or_last_menu()))
1328                 menu_frame_select_previous(f);
1329         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1330             ObMenuFrame *f;
1331             if ((f = find_active_or_last_menu()))
1332                 menu_frame_select_next(f);
1333         }
1334         break;
1335     }
1336 }
1337
1338 static gboolean menu_hide_delay_func(gpointer data)
1339 {
1340     menu_can_hide = TRUE;
1341     return FALSE; /* no repeat */
1342 }
1343
1344 static void focus_delay_dest(gpointer data)
1345 {
1346     g_free(data);
1347 }
1348
1349 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
1350 {
1351     const ObFocusDelayData *f1 = d1, *f2 = d2;
1352     return f1->client == f2->client;
1353 }
1354
1355 static gboolean focus_delay_func(gpointer data)
1356 {
1357     ObFocusDelayData *d = data;
1358     Time old = event_curtime;
1359
1360     event_curtime = d->time;
1361     if (focus_client != d->client) {
1362         if (client_focus(d->client) && config_focus_raise)
1363             client_raise(d->client);
1364     }
1365     event_curtime = old;
1366     return FALSE; /* no repeat */
1367 }
1368
1369 static void focus_delay_client_dest(ObClient *client, gpointer data)
1370 {
1371     ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1372                                      client, FALSE);
1373 }
1374
1375 static void event_client_dest(ObClient *client, gpointer data)
1376 {
1377     if (client == focus_hilite)
1378         focus_hilite = NULL;
1379 }
1380
1381 void event_halt_focus_delay()
1382 {
1383     ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1384 }
1385
1386 void event_ignore_queued_enters()
1387 {
1388     GSList *saved = NULL, *it;
1389     XEvent *e;
1390                 
1391     XSync(ob_display, FALSE);
1392
1393     /* count the events */
1394     while (TRUE) {
1395         e = g_new(XEvent, 1);
1396         if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1397             ObWindow *win;
1398             
1399             win = g_hash_table_lookup(window_map, &e->xany.window);
1400             if (win && WINDOW_IS_CLIENT(win))
1401                 ++ignore_enter_focus;
1402             
1403             saved = g_slist_append(saved, e);
1404         } else {
1405             g_free(e);
1406             break;
1407         }
1408     }
1409     /* put the events back */
1410     for (it = saved; it; it = g_slist_next(it)) {
1411         XPutBackEvent(ob_display, it->data);
1412         g_free(it->data);
1413     }
1414     g_slist_free(saved);
1415 }
1416
1417 gboolean event_time_after(Time t1, Time t2)
1418 {
1419     g_assert(t1 != CurrentTime);
1420     g_assert(t2 != CurrentTime);
1421
1422     /*
1423       Timestamp values wrap around (after about 49.7 days). The server, given
1424       its current time is represented by timestamp T, always interprets
1425       timestamps from clients by treating half of the timestamp space as being
1426       later in time than T.
1427       - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1428     */
1429
1430     /* TIME_HALF is half of the number space of a Time type variable */
1431 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1432
1433     if (t2 >= TIME_HALF)
1434         /* t2 is in the second half so t1 might wrap around and be smaller than
1435            t2 */
1436         return t1 >= t2 || t1 < (t2 + TIME_HALF);
1437     else
1438         /* t2 is in the first half so t1 has to come after it */
1439         return t1 >= t2 && t1 < (t2 + TIME_HALF);
1440 }