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