]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/event.c
focus fallback when focus is on PointerRoot
[mikachu/openbox.git] / openbox / event.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 event.c for the Openbox window manager
4 Copyright (c) 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 guint NumLockMask;
77 /*! The value of the mask for the ScrollLock modifier */
78 guint ScrollLockMask;
79 /*! The key codes for the modifier keys */
80 static XModifierKeymap *modmap;
81 /*! Table of the constant modifier masks */
82 static const gint mask_table[] = {
83     ShiftMask, LockMask, ControlMask, Mod1Mask,
84     Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
85 };
86 static gint 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(gint 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     gint 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             gint r;
364
365             /* is focus anywhere valid? */
366             XGetInputFocus(ob_display, &w, &r);
367
368 #ifdef DEBUG_FOCUS
369             ob_debug("Focus was found on 0x%x revert %d\n", w, r);
370 #endif
371
372             if (!w || w == PointerRoot)
373                 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
374         }
375         last = focus_client;
376     }
377
378     focus_in = focus_out = NULL;
379 }
380
381 static void event_process(const XEvent *ec, gpointer data)
382 {
383     Window window;
384     ObGroup *group = NULL;
385     ObClient *client = NULL;
386     ObDock *dock = NULL;
387     ObDockApp *dockapp = NULL;
388     ObWindow *obwin = NULL;
389     XEvent ee, *e;
390
391     /* make a copy we can mangle */
392     ee = *ec;
393     e = &ee;
394
395     window = event_get_window(e);
396     if (!(e->type == PropertyNotify &&
397           (group = g_hash_table_lookup(group_map, &window))))
398         if ((obwin = g_hash_table_lookup(window_map, &window))) {
399             switch (obwin->type) {
400             case Window_Dock:
401                 dock = WINDOW_AS_DOCK(obwin);
402                 break;
403             case Window_DockApp:
404                 dockapp = WINDOW_AS_DOCKAPP(obwin);
405                 break;
406             case Window_Client:
407                 client = WINDOW_AS_CLIENT(obwin);
408                 break;
409             case Window_Menu:
410             case Window_Internal:
411                 /* not to be used for events */
412                 g_assert_not_reached();
413                 break;
414             }
415         }
416
417     event_set_lasttime(e);
418     event_hack_mods(e);
419     if (event_ignore(e, client))
420         return;
421
422     /* deal with it in the kernel */
423     if (group)
424         event_handle_group(group, e);
425     else if (client)
426         event_handle_client(client, e);
427     else if (dockapp)
428         event_handle_dockapp(dockapp, e);
429     else if (dock)
430         event_handle_dock(dock, e);
431     else if (window == RootWindow(ob_display, ob_screen))
432         event_handle_root(e);
433     else if (e->type == MapRequest)
434         client_manage(window);
435     else if (e->type == ConfigureRequest) {
436         /* unhandled configure requests must be used to configure the
437            window directly */
438         XWindowChanges xwc;
439                
440         xwc.x = e->xconfigurerequest.x;
441         xwc.y = e->xconfigurerequest.y;
442         xwc.width = e->xconfigurerequest.width;
443         xwc.height = e->xconfigurerequest.height;
444         xwc.border_width = e->xconfigurerequest.border_width;
445         xwc.sibling = e->xconfigurerequest.above;
446         xwc.stack_mode = e->xconfigurerequest.detail;
447        
448         /* we are not to be held responsible if someone sends us an
449            invalid request! */
450         xerror_set_ignore(TRUE);
451         XConfigureWindow(ob_display, window,
452                          e->xconfigurerequest.value_mask, &xwc);
453         xerror_set_ignore(FALSE);
454     }
455
456     /* user input (action-bound) events */
457     if (e->type == ButtonPress || e->type == ButtonRelease ||
458         e->type == MotionNotify || e->type == KeyPress ||
459         e->type == KeyRelease)
460     {
461         if (menu_frame_visible)
462             event_handle_menu(e);
463         else {
464             if (!keyboard_process_interactive_grab(e, &client)) {
465                 if (moveresize_in_progress) {
466                     moveresize_event(e);
467
468                     /* make further actions work on the client being
469                        moved/resized */
470                     client = moveresize_client;
471                 }
472
473                 menu_can_hide = FALSE;
474                 ob_main_loop_timeout_add(ob_main_loop,
475                                          G_USEC_PER_SEC / 4,
476                                          menu_hide_delay_func,
477                                          NULL, NULL);
478
479                 if (e->type == ButtonPress || e->type == ButtonRelease ||
480                     e->type == MotionNotify)
481                     mouse_event(client, e);
482                 else if (e->type == KeyPress)
483                     keyboard_event((focus_cycle_target ? focus_cycle_target :
484                                     (focus_hilite ? focus_hilite : client)),
485                                    e);
486             }
487         }
488     }
489 }
490
491 static void event_handle_root(XEvent *e)
492 {
493     Atom msgtype;
494      
495     switch(e->type) {
496     case SelectionClear:
497         ob_debug("Another WM has requested to replace us. Exiting.\n");
498         ob_exit(0);
499         break;
500
501     case ClientMessage:
502         if (e->xclient.format != 32) break;
503
504         msgtype = e->xclient.message_type;
505         if (msgtype == prop_atoms.net_current_desktop) {
506             guint d = e->xclient.data.l[0];
507             if (d < screen_num_desktops)
508                 screen_set_desktop(d);
509         } else if (msgtype == prop_atoms.net_number_of_desktops) {
510             guint d = e->xclient.data.l[0];
511             if (d > 0)
512                 screen_set_num_desktops(d);
513         } else if (msgtype == prop_atoms.net_showing_desktop) {
514             screen_show_desktop(e->xclient.data.l[0] != 0);
515         }
516         break;
517     case PropertyNotify:
518         if (e->xproperty.atom == prop_atoms.net_desktop_names)
519             screen_update_desktop_names();
520         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
521             screen_update_layout();
522         break;
523     case ConfigureNotify:
524 #ifdef XRANDR
525         XRRUpdateConfiguration(e);
526 #endif
527         screen_resize();
528         break;
529     default:
530         ;
531 #ifdef VIDMODE
532         if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
533             ob_debug("VIDMODE EVENT\n");
534         }
535 #endif
536     }
537 }
538
539 static void event_handle_group(ObGroup *group, XEvent *e)
540 {
541     GSList *it;
542
543     g_assert(e->type == PropertyNotify);
544
545     for (it = group->members; it; it = g_slist_next(it))
546         event_handle_client(it->data, e);
547 }
548
549 void event_enter_client(ObClient *client)
550 {
551     g_assert(config_focus_follow);
552
553     if (client_normal(client) && client_can_focus(client)) {
554         if (config_focus_delay) {
555             ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
556             ob_main_loop_timeout_add(ob_main_loop,
557                                      config_focus_delay,
558                                      focus_delay_func,
559                                      client, NULL);
560         } else
561             focus_delay_func(client);
562     }
563 }
564
565 static void event_handle_client(ObClient *client, XEvent *e)
566 {
567     XEvent ce;
568     Atom msgtype;
569     gint i=0;
570     ObFrameContext con;
571      
572     switch (e->type) {
573     case VisibilityNotify:
574         client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
575         break;
576     case ButtonPress:
577     case ButtonRelease:
578         /* Wheel buttons don't draw because they are an instant click, so it
579            is a waste of resources to go drawing it. */
580         if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
581             con = frame_context(client, e->xbutton.window);
582             con = mouse_button_frame_context(con, e->xbutton.button);
583             switch (con) {
584             case OB_FRAME_CONTEXT_MAXIMIZE:
585                 client->frame->max_press = (e->type == ButtonPress);
586                 framerender_frame(client->frame);
587                 break;
588             case OB_FRAME_CONTEXT_CLOSE:
589                 client->frame->close_press = (e->type == ButtonPress);
590                 framerender_frame(client->frame);
591                 break;
592             case OB_FRAME_CONTEXT_ICONIFY:
593                 client->frame->iconify_press = (e->type == ButtonPress);
594                 framerender_frame(client->frame);
595                 break;
596             case OB_FRAME_CONTEXT_ALLDESKTOPS:
597                 client->frame->desk_press = (e->type == ButtonPress);
598                 framerender_frame(client->frame);
599                 break; 
600             case OB_FRAME_CONTEXT_SHADE:
601                 client->frame->shade_press = (e->type == ButtonPress);
602                 framerender_frame(client->frame);
603                 break;
604             default:
605                 /* nothing changes with clicks for any other contexts */
606                 break;
607             }
608         }
609         break;
610     case FocusIn:
611 #ifdef DEBUG_FOCUS
612         ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
613                  e->xfocus.window, client->window,
614                  e->xfocus.mode, e->xfocus.detail);
615 #endif
616         focus_in = client;
617         if (focus_out == client)
618             focus_out = NULL;
619         break;
620     case FocusOut:
621 #ifdef DEBUG_FOCUS
622         ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
623                  e->xfocus.window, client->window,
624                  e->xfocus.mode, e->xfocus.detail);
625 #endif
626         if (focus_hilite == client || focus_client == client)
627             focus_out = client;
628         if (focus_in == client)
629             focus_in = NULL;
630         break;
631     case LeaveNotify:
632         con = frame_context(client, e->xcrossing.window);
633         switch (con) {
634         case OB_FRAME_CONTEXT_MAXIMIZE:
635             client->frame->max_hover = FALSE;
636             frame_adjust_state(client->frame);
637             break;
638         case OB_FRAME_CONTEXT_ALLDESKTOPS:
639             client->frame->desk_hover = FALSE;
640             frame_adjust_state(client->frame);
641             break;
642         case OB_FRAME_CONTEXT_SHADE:
643             client->frame->shade_hover = FALSE;
644             frame_adjust_state(client->frame);
645             break;
646         case OB_FRAME_CONTEXT_ICONIFY:
647             client->frame->iconify_hover = FALSE;
648             frame_adjust_state(client->frame);
649             break;
650         case OB_FRAME_CONTEXT_CLOSE:
651             client->frame->close_hover = FALSE;
652             frame_adjust_state(client->frame);
653             break;
654         case OB_FRAME_CONTEXT_FRAME:
655             if (config_focus_follow && config_focus_delay)
656                 ob_main_loop_timeout_remove_data(ob_main_loop,
657                                                  focus_delay_func,
658                                                  client);
659             break;
660         default:
661             break;
662         }
663         break;
664     case EnterNotify:
665     {
666         gboolean nofocus = FALSE;
667
668         if (ignore_enter_focus) {
669             ignore_enter_focus--;
670             nofocus = TRUE;
671         }
672
673         con = frame_context(client, e->xcrossing.window);
674         switch (con) {
675         case OB_FRAME_CONTEXT_MAXIMIZE:
676             client->frame->max_hover = TRUE;
677             frame_adjust_state(client->frame);
678             break;
679         case OB_FRAME_CONTEXT_ALLDESKTOPS:
680             client->frame->desk_hover = TRUE;
681             frame_adjust_state(client->frame);
682             break;
683         case OB_FRAME_CONTEXT_SHADE:
684             client->frame->shade_hover = TRUE;
685             frame_adjust_state(client->frame);
686             break;
687         case OB_FRAME_CONTEXT_ICONIFY:
688             client->frame->iconify_hover = TRUE;
689             frame_adjust_state(client->frame);
690             break;
691         case OB_FRAME_CONTEXT_CLOSE:
692             client->frame->close_hover = TRUE;
693             frame_adjust_state(client->frame);
694             break;
695         case OB_FRAME_CONTEXT_FRAME:
696             if (e->xcrossing.mode == NotifyGrab ||
697                 e->xcrossing.mode == NotifyUngrab)
698             {
699 #ifdef DEBUG_FOCUS
700                 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
701                          (e->type == EnterNotify ? "Enter" : "Leave"),
702                          e->xcrossing.mode,
703                          e->xcrossing.detail, client?client->window:0);
704 #endif
705             } else {
706 #ifdef DEBUG_FOCUS
707                 ob_debug("%sNotify mode %d detail %d on %lx, "
708                          "focusing window: %d\n",
709                          (e->type == EnterNotify ? "Enter" : "Leave"),
710                          e->xcrossing.mode,
711                          e->xcrossing.detail, (client?client->window:0),
712                          !nofocus);
713 #endif
714                 if (!nofocus && config_focus_follow)
715                     event_enter_client(client);
716             }
717             break;
718         default:
719             break;
720         }
721         break;
722     }
723     case ConfigureRequest:
724         /* compress these */
725         while (XCheckTypedWindowEvent(ob_display, client->window,
726                                       ConfigureRequest, &ce)) {
727             ++i;
728             /* XXX if this causes bad things.. we can compress config req's
729                with the same mask. */
730             e->xconfigurerequest.value_mask |=
731                 ce.xconfigurerequest.value_mask;
732             if (ce.xconfigurerequest.value_mask & CWX)
733                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
734             if (ce.xconfigurerequest.value_mask & CWY)
735                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
736             if (ce.xconfigurerequest.value_mask & CWWidth)
737                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
738             if (ce.xconfigurerequest.value_mask & CWHeight)
739                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
740             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
741                 e->xconfigurerequest.border_width =
742                     ce.xconfigurerequest.border_width;
743             if (ce.xconfigurerequest.value_mask & CWStackMode)
744                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
745         }
746
747         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
748         if (client->iconic || client->shaded) return;
749
750         /* resize, then move, as specified in the EWMH section 7.7 */
751         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
752                                                CWX | CWY |
753                                                CWBorderWidth)) {
754             gint x, y, w, h;
755             ObCorner corner;
756
757             if (e->xconfigurerequest.value_mask & CWBorderWidth)
758                 client->border_width = e->xconfigurerequest.border_width;
759
760             x = (e->xconfigurerequest.value_mask & CWX) ?
761                 e->xconfigurerequest.x : client->area.x;
762             y = (e->xconfigurerequest.value_mask & CWY) ?
763                 e->xconfigurerequest.y : client->area.y;
764             w = (e->xconfigurerequest.value_mask & CWWidth) ?
765                 e->xconfigurerequest.width : client->area.width;
766             h = (e->xconfigurerequest.value_mask & CWHeight) ?
767                 e->xconfigurerequest.height : client->area.height;
768
769             {
770                 gint newx = x;
771                 gint newy = y;
772                 gint fw = w +
773                      client->frame->size.left + client->frame->size.right;
774                 gint fh = h +
775                      client->frame->size.top + client->frame->size.bottom;
776                 client_find_onscreen(client, &newx, &newy, fw, fh,
777                                      client_normal(client));
778                 if (e->xconfigurerequest.value_mask & CWX)
779                     x = newx;
780                 if (e->xconfigurerequest.value_mask & CWY)
781                     y = newy;
782             }
783                
784             switch (client->gravity) {
785             case NorthEastGravity:
786             case EastGravity:
787                 corner = OB_CORNER_TOPRIGHT;
788                 break;
789             case SouthWestGravity:
790             case SouthGravity:
791                 corner = OB_CORNER_BOTTOMLEFT;
792                 break;
793             case SouthEastGravity:
794                 corner = OB_CORNER_BOTTOMRIGHT;
795                 break;
796             default:     /* NorthWest, Static, etc */
797                 corner = OB_CORNER_TOPLEFT;
798             }
799
800             client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
801                                   TRUE);
802         }
803
804         if (e->xconfigurerequest.value_mask & CWStackMode) {
805             switch (e->xconfigurerequest.detail) {
806             case Below:
807             case BottomIf:
808                 client_lower(client);
809                 break;
810
811             case Above:
812             case TopIf:
813             default:
814                 client_raise(client);
815                 break;
816             }
817         }
818         break;
819     case UnmapNotify:
820         if (client->ignore_unmaps) {
821             client->ignore_unmaps--;
822             break;
823         }
824         client_unmanage(client);
825         break;
826     case DestroyNotify:
827         client_unmanage(client);
828         break;
829     case ReparentNotify:
830         /* this is when the client is first taken captive in the frame */
831         if (e->xreparent.parent == client->frame->plate) break;
832
833         /*
834           This event is quite rare and is usually handled in unmapHandler.
835           However, if the window is unmapped when the reparent event occurs,
836           the window manager never sees it because an unmap event is not sent
837           to an already unmapped window.
838         */
839
840         /* we don't want the reparent event, put it back on the stack for the
841            X server to deal with after we unmanage the window */
842         XPutBackEvent(ob_display, e);
843      
844         client_unmanage(client);
845         break;
846     case MapRequest:
847         ob_debug("MapRequest for 0x%lx\n", client->window);
848         if (!client->iconic) break; /* this normally doesn't happen, but if it
849                                        does, we don't want it! */
850         client_activate(client, FALSE);
851         break;
852     case ClientMessage:
853         /* validate cuz we query stuff off the client here */
854         if (!client_validate(client)) break;
855
856         if (e->xclient.format != 32) return;
857
858         msgtype = e->xclient.message_type;
859         if (msgtype == prop_atoms.wm_change_state) {
860             /* compress changes into a single change */
861             while (XCheckTypedWindowEvent(ob_display, client->window,
862                                           e->type, &ce)) {
863                 /* XXX: it would be nice to compress ALL messages of a
864                    type, not just messages in a row without other
865                    message types between. */
866                 if (ce.xclient.message_type != msgtype) {
867                     XPutBackEvent(ob_display, &ce);
868                     break;
869                 }
870                 e->xclient = ce.xclient;
871             }
872             client_set_wm_state(client, e->xclient.data.l[0]);
873         } else if (msgtype == prop_atoms.net_wm_desktop) {
874             /* compress changes into a single change */
875             while (XCheckTypedWindowEvent(ob_display, client->window,
876                                           e->type, &ce)) {
877                 /* XXX: it would be nice to compress ALL messages of a
878                    type, not just messages in a row without other
879                    message types between. */
880                 if (ce.xclient.message_type != msgtype) {
881                     XPutBackEvent(ob_display, &ce);
882                     break;
883                 }
884                 e->xclient = ce.xclient;
885             }
886             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
887                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
888                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
889                                    FALSE);
890         } else if (msgtype == prop_atoms.net_wm_state) {
891             /* can't compress these */
892             ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
893                      (e->xclient.data.l[0] == 0 ? "Remove" :
894                       e->xclient.data.l[0] == 1 ? "Add" :
895                       e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
896                      e->xclient.data.l[1], e->xclient.data.l[2],
897                      client->window);
898             client_set_state(client, e->xclient.data.l[0],
899                              e->xclient.data.l[1], e->xclient.data.l[2]);
900         } else if (msgtype == prop_atoms.net_close_window) {
901             ob_debug("net_close_window for 0x%lx\n", client->window);
902             client_close(client);
903         } else if (msgtype == prop_atoms.net_active_window) {
904             ob_debug("net_active_window for 0x%lx\n", client->window);
905             client_activate(client, FALSE);
906         } else if (msgtype == prop_atoms.net_wm_moveresize) {
907             ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
908             if ((Atom)e->xclient.data.l[2] ==
909                 prop_atoms.net_wm_moveresize_size_topleft ||
910                 (Atom)e->xclient.data.l[2] ==
911                 prop_atoms.net_wm_moveresize_size_top ||
912                 (Atom)e->xclient.data.l[2] ==
913                 prop_atoms.net_wm_moveresize_size_topright ||
914                 (Atom)e->xclient.data.l[2] ==
915                 prop_atoms.net_wm_moveresize_size_right ||
916                 (Atom)e->xclient.data.l[2] ==
917                 prop_atoms.net_wm_moveresize_size_right ||
918                 (Atom)e->xclient.data.l[2] ==
919                 prop_atoms.net_wm_moveresize_size_bottomright ||
920                 (Atom)e->xclient.data.l[2] ==
921                 prop_atoms.net_wm_moveresize_size_bottom ||
922                 (Atom)e->xclient.data.l[2] ==
923                 prop_atoms.net_wm_moveresize_size_bottomleft ||
924                 (Atom)e->xclient.data.l[2] ==
925                 prop_atoms.net_wm_moveresize_size_left ||
926                 (Atom)e->xclient.data.l[2] ==
927                 prop_atoms.net_wm_moveresize_move ||
928                 (Atom)e->xclient.data.l[2] ==
929                 prop_atoms.net_wm_moveresize_size_keyboard ||
930                 (Atom)e->xclient.data.l[2] ==
931                 prop_atoms.net_wm_moveresize_move_keyboard) {
932
933                 moveresize_start(client, e->xclient.data.l[0],
934                                  e->xclient.data.l[1], e->xclient.data.l[3],
935                                  e->xclient.data.l[2]);
936             }
937         } else if (msgtype == prop_atoms.net_moveresize_window) {
938             gint oldg = client->gravity;
939             gint tmpg, x, y, w, h;
940
941             if (e->xclient.data.l[0] & 0xff)
942                 tmpg = e->xclient.data.l[0] & 0xff;
943             else
944                 tmpg = oldg;
945
946             if (e->xclient.data.l[0] & 1 << 8)
947                 x = e->xclient.data.l[1];
948             else
949                 x = client->area.x;
950             if (e->xclient.data.l[0] & 1 << 9)
951                 y = e->xclient.data.l[2];
952             else
953                 y = client->area.y;
954             if (e->xclient.data.l[0] & 1 << 10)
955                 w = e->xclient.data.l[3];
956             else
957                 w = client->area.width;
958             if (e->xclient.data.l[0] & 1 << 11)
959                 h = e->xclient.data.l[4];
960             else
961                 h = client->area.height;
962             client->gravity = tmpg;
963
964             {
965                 gint newx = x;
966                 gint newy = y;
967                 gint fw = w +
968                      client->frame->size.left + client->frame->size.right;
969                 gint fh = h +
970                      client->frame->size.top + client->frame->size.bottom;
971                 client_find_onscreen(client, &newx, &newy, fw, fh,
972                                      client_normal(client));
973                 if (e->xclient.data.l[0] & 1 << 8)
974                     x = newx;
975                 if (e->xclient.data.l[0] & 1 << 9)
976                     y = newy;
977             }
978                
979             client_configure(client, OB_CORNER_TOPLEFT,
980                              x, y, w, h, FALSE, TRUE);
981
982             client->gravity = oldg;
983         }
984         break;
985     case PropertyNotify:
986         /* validate cuz we query stuff off the client here */
987         if (!client_validate(client)) break;
988   
989         /* compress changes to a single property into a single change */
990         while (XCheckTypedWindowEvent(ob_display, client->window,
991                                       e->type, &ce)) {
992             Atom a, b;
993
994             /* XXX: it would be nice to compress ALL changes to a property,
995                not just changes in a row without other props between. */
996
997             a = ce.xproperty.atom;
998             b = e->xproperty.atom;
999
1000             if (a == b)
1001                 continue;
1002             if ((a == prop_atoms.net_wm_name ||
1003                  a == prop_atoms.wm_name ||
1004                  a == prop_atoms.net_wm_icon_name ||
1005                  a == prop_atoms.wm_icon_name)
1006                 &&
1007                 (b == prop_atoms.net_wm_name ||
1008                  b == prop_atoms.wm_name ||
1009                  b == prop_atoms.net_wm_icon_name ||
1010                  b == prop_atoms.wm_icon_name)) {
1011                 continue;
1012             }
1013             if ((a == prop_atoms.net_wm_icon ||
1014                  a == prop_atoms.kwm_win_icon)
1015                 &&
1016                 (b == prop_atoms.net_wm_icon ||
1017                  b == prop_atoms.kwm_win_icon))
1018                 continue;
1019
1020             XPutBackEvent(ob_display, &ce);
1021             break;
1022         }
1023
1024         msgtype = e->xproperty.atom;
1025         if (msgtype == XA_WM_NORMAL_HINTS) {
1026             client_update_normal_hints(client);
1027             /* normal hints can make a window non-resizable */
1028             client_setup_decor_and_functions(client);
1029         } else if (msgtype == XA_WM_HINTS) {
1030             client_update_wmhints(client);
1031         } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1032             client_update_transient_for(client);
1033             client_get_type(client);
1034             /* type may have changed, so update the layer */
1035             client_calc_layer(client);
1036             client_setup_decor_and_functions(client);
1037         } else if (msgtype == prop_atoms.net_wm_name ||
1038                    msgtype == prop_atoms.wm_name ||
1039                    msgtype == prop_atoms.net_wm_icon_name ||
1040                    msgtype == prop_atoms.wm_icon_name) {
1041             client_update_title(client);
1042         } else if (msgtype == prop_atoms.wm_class) {
1043             client_update_class(client);
1044         } else if (msgtype == prop_atoms.wm_protocols) {
1045             client_update_protocols(client);
1046             client_setup_decor_and_functions(client);
1047         }
1048         else if (msgtype == prop_atoms.net_wm_strut) {
1049             client_update_strut(client);
1050         }
1051         else if (msgtype == prop_atoms.net_wm_icon ||
1052                  msgtype == prop_atoms.kwm_win_icon) {
1053             client_update_icons(client);
1054         }
1055         else if (msgtype == prop_atoms.sm_client_id) {
1056             client_update_sm_client_id(client);
1057         }
1058     default:
1059         ;
1060 #ifdef SHAPE
1061         if (extensions_shape && e->type == extensions_shape_event_basep) {
1062             client->shaped = ((XShapeEvent*)e)->shaped;
1063             frame_adjust_shape(client->frame);
1064         }
1065 #endif
1066     }
1067 }
1068
1069 static void event_handle_dock(ObDock *s, XEvent *e)
1070 {
1071     switch (e->type) {
1072     case ButtonPress:
1073         stacking_raise(DOCK_AS_WINDOW(s));
1074         break;
1075     case EnterNotify:
1076         dock_hide(FALSE);
1077         break;
1078     case LeaveNotify:
1079         dock_hide(TRUE);
1080         break;
1081     }
1082 }
1083
1084 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1085 {
1086     switch (e->type) {
1087     case MotionNotify:
1088         dock_app_drag(app, &e->xmotion);
1089         break;
1090     case UnmapNotify:
1091         if (app->ignore_unmaps) {
1092             app->ignore_unmaps--;
1093             break;
1094         }
1095         dock_remove(app, TRUE);
1096         break;
1097     case DestroyNotify:
1098         dock_remove(app, FALSE);
1099         break;
1100     case ReparentNotify:
1101         dock_remove(app, FALSE);
1102         break;
1103     case ConfigureNotify:
1104         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1105         break;
1106     }
1107 }
1108
1109 ObMenuFrame* find_active_menu()
1110 {
1111     GList *it;
1112     ObMenuFrame *ret = NULL;
1113
1114     for (it = menu_frame_visible; it; it = g_list_next(it)) {
1115         ret = it->data;
1116         if (ret->selected)
1117             break;
1118         ret = NULL;
1119     }
1120     return ret;
1121 }
1122
1123 ObMenuFrame* find_active_or_last_menu()
1124 {
1125     ObMenuFrame *ret = NULL;
1126
1127     ret = find_active_menu();
1128     if (!ret && menu_frame_visible)
1129         ret = menu_frame_visible->data;
1130     return ret;
1131 }
1132
1133 static void event_handle_menu(XEvent *ev)
1134 {
1135     ObMenuFrame *f;
1136     ObMenuEntryFrame *e;
1137
1138     switch (ev->type) {
1139     case ButtonRelease:
1140         if (menu_can_hide) {
1141             if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1142                                             ev->xbutton.y_root)))
1143                 menu_entry_frame_execute(e, ev->xbutton.state);
1144             else
1145                 menu_frame_hide_all();
1146         }
1147         break;
1148     case MotionNotify:
1149         if ((f = menu_frame_under(ev->xmotion.x_root,
1150                                   ev->xmotion.y_root))) {
1151             menu_frame_move_on_screen(f);
1152             if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1153                                             ev->xmotion.y_root)))
1154                 menu_frame_select(f, e);
1155         }
1156         {
1157             ObMenuFrame *a;
1158
1159             a = find_active_menu();
1160             if (a && a != f &&
1161                 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1162             {
1163                 menu_frame_select(a, NULL);
1164             }
1165         }
1166         break;
1167     case KeyPress:
1168         if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1169             menu_frame_hide_all();
1170         else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1171             ObMenuFrame *f;
1172             if ((f = find_active_menu()))
1173                 menu_entry_frame_execute(f->selected, ev->xkey.state);
1174         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1175             ObMenuFrame *f;
1176             if ((f = find_active_or_last_menu()) && f->parent)
1177                 menu_frame_select(f, NULL);
1178         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1179             ObMenuFrame *f;
1180             if ((f = find_active_or_last_menu()) && f->child)
1181                 menu_frame_select_next(f->child);
1182         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1183             ObMenuFrame *f;
1184             if ((f = find_active_or_last_menu()))
1185                 menu_frame_select_previous(f);
1186         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1187             ObMenuFrame *f;
1188             if ((f = find_active_or_last_menu()))
1189                 menu_frame_select_next(f);
1190         }
1191         break;
1192     }
1193 }
1194
1195 static gboolean menu_hide_delay_func(gpointer data)
1196 {
1197     menu_can_hide = TRUE;
1198     return FALSE; /* no repeat */
1199 }
1200
1201 static gboolean focus_delay_func(gpointer data)
1202 {
1203     ObClient *c = data;
1204
1205     if (focus_client != c) {
1206         client_focus(c);
1207         if (config_focus_raise)
1208             client_raise(c);
1209     }
1210     return FALSE; /* no repeat */
1211 }
1212
1213 static void focus_delay_client_dest(ObClient *client, gpointer data)
1214 {
1215     ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, client);
1216 }
1217
1218 void event_halt_focus_delay()
1219 {
1220     ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1221 }
1222
1223 void event_ignore_queued_enters()
1224 {
1225     GSList *saved = NULL, *it;
1226     XEvent *e;
1227                 
1228     XSync(ob_display, FALSE);
1229
1230     /* count the events */
1231     while (TRUE) {
1232         e = g_new(XEvent, 1);
1233         if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1234             ObWindow *win;
1235             
1236             win = g_hash_table_lookup(window_map, &e->xany.window);
1237             if (win && WINDOW_IS_CLIENT(win))
1238                 ++ignore_enter_focus;
1239             
1240             saved = g_slist_append(saved, e);
1241         } else {
1242             g_free(e);
1243             break;
1244         }
1245     }
1246     /* put the events back */
1247     for (it = saved; it; it = g_slist_next(it)) {
1248         XPutBackEvent(ob_display, it->data);
1249         g_free(it->data);
1250     }
1251     g_slist_free(saved);
1252 }