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