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