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