]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/event.c
add comment
[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                                        it can happen now when the window is on
851                                        another desktop, but we still don't
852                                        want it! */
853         client_activate(client, FALSE);
854         break;
855     case ClientMessage:
856         /* validate cuz we query stuff off the client here */
857         if (!client_validate(client)) break;
858
859         if (e->xclient.format != 32) return;
860
861         msgtype = e->xclient.message_type;
862         if (msgtype == prop_atoms.wm_change_state) {
863             /* compress changes into a single change */
864             while (XCheckTypedWindowEvent(ob_display, client->window,
865                                           e->type, &ce)) {
866                 /* XXX: it would be nice to compress ALL messages of a
867                    type, not just messages in a row without other
868                    message types between. */
869                 if (ce.xclient.message_type != msgtype) {
870                     XPutBackEvent(ob_display, &ce);
871                     break;
872                 }
873                 e->xclient = ce.xclient;
874             }
875             client_set_wm_state(client, e->xclient.data.l[0]);
876         } else if (msgtype == prop_atoms.net_wm_desktop) {
877             /* compress changes into a single change */
878             while (XCheckTypedWindowEvent(ob_display, client->window,
879                                           e->type, &ce)) {
880                 /* XXX: it would be nice to compress ALL messages of a
881                    type, not just messages in a row without other
882                    message types between. */
883                 if (ce.xclient.message_type != msgtype) {
884                     XPutBackEvent(ob_display, &ce);
885                     break;
886                 }
887                 e->xclient = ce.xclient;
888             }
889             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
890                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
891                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
892                                    FALSE);
893         } else if (msgtype == prop_atoms.net_wm_state) {
894             /* can't compress these */
895             ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
896                      (e->xclient.data.l[0] == 0 ? "Remove" :
897                       e->xclient.data.l[0] == 1 ? "Add" :
898                       e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
899                      e->xclient.data.l[1], e->xclient.data.l[2],
900                      client->window);
901             client_set_state(client, e->xclient.data.l[0],
902                              e->xclient.data.l[1], e->xclient.data.l[2]);
903         } else if (msgtype == prop_atoms.net_close_window) {
904             ob_debug("net_close_window for 0x%lx\n", client->window);
905             client_close(client);
906         } else if (msgtype == prop_atoms.net_active_window) {
907             ob_debug("net_active_window for 0x%lx\n", client->window);
908             client_activate(client, FALSE);
909         } else if (msgtype == prop_atoms.net_wm_moveresize) {
910             ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
911             if ((Atom)e->xclient.data.l[2] ==
912                 prop_atoms.net_wm_moveresize_size_topleft ||
913                 (Atom)e->xclient.data.l[2] ==
914                 prop_atoms.net_wm_moveresize_size_top ||
915                 (Atom)e->xclient.data.l[2] ==
916                 prop_atoms.net_wm_moveresize_size_topright ||
917                 (Atom)e->xclient.data.l[2] ==
918                 prop_atoms.net_wm_moveresize_size_right ||
919                 (Atom)e->xclient.data.l[2] ==
920                 prop_atoms.net_wm_moveresize_size_right ||
921                 (Atom)e->xclient.data.l[2] ==
922                 prop_atoms.net_wm_moveresize_size_bottomright ||
923                 (Atom)e->xclient.data.l[2] ==
924                 prop_atoms.net_wm_moveresize_size_bottom ||
925                 (Atom)e->xclient.data.l[2] ==
926                 prop_atoms.net_wm_moveresize_size_bottomleft ||
927                 (Atom)e->xclient.data.l[2] ==
928                 prop_atoms.net_wm_moveresize_size_left ||
929                 (Atom)e->xclient.data.l[2] ==
930                 prop_atoms.net_wm_moveresize_move ||
931                 (Atom)e->xclient.data.l[2] ==
932                 prop_atoms.net_wm_moveresize_size_keyboard ||
933                 (Atom)e->xclient.data.l[2] ==
934                 prop_atoms.net_wm_moveresize_move_keyboard) {
935
936                 moveresize_start(client, e->xclient.data.l[0],
937                                  e->xclient.data.l[1], e->xclient.data.l[3],
938                                  e->xclient.data.l[2]);
939             }
940         } else if (msgtype == prop_atoms.net_moveresize_window) {
941             gint oldg = client->gravity;
942             gint tmpg, x, y, w, h;
943
944             if (e->xclient.data.l[0] & 0xff)
945                 tmpg = e->xclient.data.l[0] & 0xff;
946             else
947                 tmpg = oldg;
948
949             if (e->xclient.data.l[0] & 1 << 8)
950                 x = e->xclient.data.l[1];
951             else
952                 x = client->area.x;
953             if (e->xclient.data.l[0] & 1 << 9)
954                 y = e->xclient.data.l[2];
955             else
956                 y = client->area.y;
957             if (e->xclient.data.l[0] & 1 << 10)
958                 w = e->xclient.data.l[3];
959             else
960                 w = client->area.width;
961             if (e->xclient.data.l[0] & 1 << 11)
962                 h = e->xclient.data.l[4];
963             else
964                 h = client->area.height;
965             client->gravity = tmpg;
966
967             {
968                 gint newx = x;
969                 gint newy = y;
970                 gint fw = w +
971                      client->frame->size.left + client->frame->size.right;
972                 gint fh = h +
973                      client->frame->size.top + client->frame->size.bottom;
974                 client_find_onscreen(client, &newx, &newy, fw, fh,
975                                      client_normal(client));
976                 if (e->xclient.data.l[0] & 1 << 8)
977                     x = newx;
978                 if (e->xclient.data.l[0] & 1 << 9)
979                     y = newy;
980             }
981                
982             client_configure(client, OB_CORNER_TOPLEFT,
983                              x, y, w, h, FALSE, TRUE);
984
985             client->gravity = oldg;
986         }
987         break;
988     case PropertyNotify:
989         /* validate cuz we query stuff off the client here */
990         if (!client_validate(client)) break;
991   
992         /* compress changes to a single property into a single change */
993         while (XCheckTypedWindowEvent(ob_display, client->window,
994                                       e->type, &ce)) {
995             Atom a, b;
996
997             /* XXX: it would be nice to compress ALL changes to a property,
998                not just changes in a row without other props between. */
999
1000             a = ce.xproperty.atom;
1001             b = e->xproperty.atom;
1002
1003             if (a == b)
1004                 continue;
1005             if ((a == prop_atoms.net_wm_name ||
1006                  a == prop_atoms.wm_name ||
1007                  a == prop_atoms.net_wm_icon_name ||
1008                  a == prop_atoms.wm_icon_name)
1009                 &&
1010                 (b == prop_atoms.net_wm_name ||
1011                  b == prop_atoms.wm_name ||
1012                  b == prop_atoms.net_wm_icon_name ||
1013                  b == prop_atoms.wm_icon_name)) {
1014                 continue;
1015             }
1016             if ((a == prop_atoms.net_wm_icon ||
1017                  a == prop_atoms.kwm_win_icon)
1018                 &&
1019                 (b == prop_atoms.net_wm_icon ||
1020                  b == prop_atoms.kwm_win_icon))
1021                 continue;
1022
1023             XPutBackEvent(ob_display, &ce);
1024             break;
1025         }
1026
1027         msgtype = e->xproperty.atom;
1028         if (msgtype == XA_WM_NORMAL_HINTS) {
1029             client_update_normal_hints(client);
1030             /* normal hints can make a window non-resizable */
1031             client_setup_decor_and_functions(client);
1032         } else if (msgtype == XA_WM_HINTS) {
1033             client_update_wmhints(client);
1034         } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1035             client_update_transient_for(client);
1036             client_get_type(client);
1037             /* type may have changed, so update the layer */
1038             client_calc_layer(client);
1039             client_setup_decor_and_functions(client);
1040         } else if (msgtype == prop_atoms.net_wm_name ||
1041                    msgtype == prop_atoms.wm_name ||
1042                    msgtype == prop_atoms.net_wm_icon_name ||
1043                    msgtype == prop_atoms.wm_icon_name) {
1044             client_update_title(client);
1045         } else if (msgtype == prop_atoms.wm_class) {
1046             client_update_class(client);
1047         } else if (msgtype == prop_atoms.wm_protocols) {
1048             client_update_protocols(client);
1049             client_setup_decor_and_functions(client);
1050         }
1051         else if (msgtype == prop_atoms.net_wm_strut) {
1052             client_update_strut(client);
1053         }
1054         else if (msgtype == prop_atoms.net_wm_icon ||
1055                  msgtype == prop_atoms.kwm_win_icon) {
1056             client_update_icons(client);
1057         }
1058         else if (msgtype == prop_atoms.sm_client_id) {
1059             client_update_sm_client_id(client);
1060         }
1061     default:
1062         ;
1063 #ifdef SHAPE
1064         if (extensions_shape && e->type == extensions_shape_event_basep) {
1065             client->shaped = ((XShapeEvent*)e)->shaped;
1066             frame_adjust_shape(client->frame);
1067         }
1068 #endif
1069     }
1070 }
1071
1072 static void event_handle_dock(ObDock *s, XEvent *e)
1073 {
1074     switch (e->type) {
1075     case ButtonPress:
1076         stacking_raise(DOCK_AS_WINDOW(s));
1077         break;
1078     case EnterNotify:
1079         dock_hide(FALSE);
1080         break;
1081     case LeaveNotify:
1082         dock_hide(TRUE);
1083         break;
1084     }
1085 }
1086
1087 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1088 {
1089     switch (e->type) {
1090     case MotionNotify:
1091         dock_app_drag(app, &e->xmotion);
1092         break;
1093     case UnmapNotify:
1094         if (app->ignore_unmaps) {
1095             app->ignore_unmaps--;
1096             break;
1097         }
1098         dock_remove(app, TRUE);
1099         break;
1100     case DestroyNotify:
1101         dock_remove(app, FALSE);
1102         break;
1103     case ReparentNotify:
1104         dock_remove(app, FALSE);
1105         break;
1106     case ConfigureNotify:
1107         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1108         break;
1109     }
1110 }
1111
1112 ObMenuFrame* find_active_menu()
1113 {
1114     GList *it;
1115     ObMenuFrame *ret = NULL;
1116
1117     for (it = menu_frame_visible; it; it = g_list_next(it)) {
1118         ret = it->data;
1119         if (ret->selected)
1120             break;
1121         ret = NULL;
1122     }
1123     return ret;
1124 }
1125
1126 ObMenuFrame* find_active_or_last_menu()
1127 {
1128     ObMenuFrame *ret = NULL;
1129
1130     ret = find_active_menu();
1131     if (!ret && menu_frame_visible)
1132         ret = menu_frame_visible->data;
1133     return ret;
1134 }
1135
1136 static void event_handle_menu(XEvent *ev)
1137 {
1138     ObMenuFrame *f;
1139     ObMenuEntryFrame *e;
1140
1141     switch (ev->type) {
1142     case ButtonRelease:
1143         if (menu_can_hide) {
1144             if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1145                                             ev->xbutton.y_root)))
1146                 menu_entry_frame_execute(e, ev->xbutton.state);
1147             else
1148                 menu_frame_hide_all();
1149         }
1150         break;
1151     case MotionNotify:
1152         if ((f = menu_frame_under(ev->xmotion.x_root,
1153                                   ev->xmotion.y_root))) {
1154             menu_frame_move_on_screen(f);
1155             if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1156                                             ev->xmotion.y_root)))
1157                 menu_frame_select(f, e);
1158         }
1159         {
1160             ObMenuFrame *a;
1161
1162             a = find_active_menu();
1163             if (a && a != f &&
1164                 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1165             {
1166                 menu_frame_select(a, NULL);
1167             }
1168         }
1169         break;
1170     case KeyPress:
1171         if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1172             menu_frame_hide_all();
1173         else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1174             ObMenuFrame *f;
1175             if ((f = find_active_menu()))
1176                 menu_entry_frame_execute(f->selected, ev->xkey.state);
1177         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1178             ObMenuFrame *f;
1179             if ((f = find_active_or_last_menu()) && f->parent)
1180                 menu_frame_select(f, NULL);
1181         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1182             ObMenuFrame *f;
1183             if ((f = find_active_or_last_menu()) && f->child)
1184                 menu_frame_select_next(f->child);
1185         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1186             ObMenuFrame *f;
1187             if ((f = find_active_or_last_menu()))
1188                 menu_frame_select_previous(f);
1189         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1190             ObMenuFrame *f;
1191             if ((f = find_active_or_last_menu()))
1192                 menu_frame_select_next(f);
1193         }
1194         break;
1195     }
1196 }
1197
1198 static gboolean menu_hide_delay_func(gpointer data)
1199 {
1200     menu_can_hide = TRUE;
1201     return FALSE; /* no repeat */
1202 }
1203
1204 static gboolean focus_delay_func(gpointer data)
1205 {
1206     ObClient *c = data;
1207
1208     if (focus_client != c) {
1209         client_focus(c);
1210         if (config_focus_raise)
1211             client_raise(c);
1212     }
1213     return FALSE; /* no repeat */
1214 }
1215
1216 static void focus_delay_client_dest(ObClient *client, gpointer data)
1217 {
1218     ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, client);
1219 }
1220
1221 void event_halt_focus_delay()
1222 {
1223     ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1224 }
1225
1226 void event_ignore_queued_enters()
1227 {
1228     GSList *saved = NULL, *it;
1229     XEvent *e;
1230                 
1231     XSync(ob_display, FALSE);
1232
1233     /* count the events */
1234     while (TRUE) {
1235         e = g_new(XEvent, 1);
1236         if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1237             ObWindow *win;
1238             
1239             win = g_hash_table_lookup(window_map, &e->xany.window);
1240             if (win && WINDOW_IS_CLIENT(win))
1241                 ++ignore_enter_focus;
1242             
1243             saved = g_slist_append(saved, e);
1244         } else {
1245             g_free(e);
1246             break;
1247         }
1248     }
1249     /* put the events back */
1250     for (it = saved; it; it = g_slist_next(it)) {
1251         XPutBackEvent(ob_display, it->data);
1252         g_free(it->data);
1253     }
1254     g_slist_free(saved);
1255 }