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