Menus put themselves into the stacking list.
[dana/openbox.git] / openbox / event.c
1 #include "openbox.h"
2 #include "slit.h"
3 #include "client.h"
4 #include "xerror.h"
5 #include "prop.h"
6 #include "config.h"
7 #include "screen.h"
8 #include "frame.h"
9 #include "menu.h"
10 #include "framerender.h"
11 #include "focus.h"
12 #include "moveresize.h"
13 #include "stacking.h"
14 #include "extensions.h"
15 #include "timer.h"
16 #include "dispatch.h"
17
18 #include <X11/Xlib.h>
19 #include <X11/keysym.h>
20 #include <X11/Xatom.h>
21 #ifdef HAVE_SYS_SELECT_H
22 #  include <sys/select.h>
23 #endif
24
25 static void event_process(XEvent *e);
26 static void event_handle_root(XEvent *e);
27 static void event_handle_slit(Slit *s, XEvent *e);
28 static void event_handle_slitapp(SlitApp *app, XEvent *e);
29 static void event_handle_client(Client *c, XEvent *e);
30 static void event_handle_menu(Menu *menu, XEvent *e);
31
32 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
33                             (e)->xfocus.detail > NotifyNonlinearVirtual)
34 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
35                              (e)->xfocus.detail == NotifyInferior || \
36                              (e)->xfocus.detail == NotifyAncestor || \
37                              (e)->xfocus.detail > NotifyNonlinearVirtual)
38
39 Time event_lasttime = 0;
40
41 /*! The value of the mask for the NumLock modifier */
42 unsigned int NumLockMask;
43 /*! The value of the mask for the ScrollLock modifier */
44 unsigned int ScrollLockMask;
45 /*! The key codes for the modifier keys */
46 static XModifierKeymap *modmap;
47 /*! Table of the constant modifier masks */
48 static const int mask_table[] = {
49     ShiftMask, LockMask, ControlMask, Mod1Mask,
50     Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
51 };
52 static int mask_table_size;
53
54 void event_startup()
55 {
56     mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
57      
58     /* get lock masks that are defined by the display (not constant) */
59     modmap = XGetModifierMapping(ob_display);
60     g_assert(modmap);
61     if (modmap && modmap->max_keypermod > 0) {
62         size_t cnt;
63         const size_t size = mask_table_size * modmap->max_keypermod;
64         /* get the values of the keyboard lock modifiers
65            Note: Caps lock is not retrieved the same way as Scroll and Num
66            lock since it doesn't need to be. */
67         const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
68         const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
69                                                      XK_Scroll_Lock);
70           
71         for (cnt = 0; cnt < size; ++cnt) {
72             if (! modmap->modifiermap[cnt]) continue;
73                
74             if (num_lock == modmap->modifiermap[cnt])
75                 NumLockMask = mask_table[cnt / modmap->max_keypermod];
76             if (scroll_lock == modmap->modifiermap[cnt])
77                 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
78         }
79     }
80 }
81
82 void event_shutdown()
83 {
84     XFreeModifiermap(modmap);
85 }
86
87 void event_loop()
88 {
89     fd_set selset;
90     XEvent e;
91     int x_fd;
92     struct timeval *wait;
93     gboolean had_event = FALSE;
94
95     while (TRUE) {
96         /*
97           There are slightly different event retrieval semantics here for
98           local (or high bandwidth) versus remote (or low bandwidth)
99           connections to the display/Xserver.
100         */
101         if (ob_remote) {
102             if (!XPending(ob_display))
103                 break;
104         } else {
105             /*
106               This XSync allows for far more compression of events, which
107               makes things like Motion events perform far far better. Since
108               it also means network traffic for every event instead of every
109               X events (where X is the number retrieved at a time), it
110               probably should not be used for setups where Openbox is
111               running on a remote/low bandwidth display/Xserver.
112             */
113             XSync(ob_display, FALSE);
114             if (!XEventsQueued(ob_display, QueuedAlready))
115                 break;
116         }
117         XNextEvent(ob_display, &e);
118
119         event_process(&e);
120        had_event = TRUE;
121     }
122
123     if (!had_event) {
124         timer_dispatch((GTimeVal**)&wait);
125         x_fd = ConnectionNumber(ob_display);
126         FD_ZERO(&selset);
127         FD_SET(x_fd, &selset);
128         select(x_fd + 1, &selset, NULL, NULL, wait);
129     }
130 }
131
132 static Window event_get_window(XEvent *e)
133 {
134     Window window;
135
136     /* pick a window */
137     switch (e->type) {
138     case MapRequest:
139         window = e->xmap.window;
140         break;
141     case UnmapNotify:
142         window = e->xunmap.window;
143         break;
144     case DestroyNotify:
145         window = e->xdestroywindow.window;
146         break;
147     case ConfigureRequest:
148         window = e->xconfigurerequest.window;
149         break;
150     case ConfigureNotify:
151         window = e->xconfigure.window;
152         break;
153     default:
154 #ifdef XKB
155         if (extensions_xkb && e->type == extensions_xkb_event_basep) {
156             switch (((XkbAnyEvent*)&e)->xkb_type) {
157             case XkbBellNotify:
158                 window = ((XkbBellNotifyEvent*)&e)->window;
159             default:
160                 window = None;
161             }
162         } else
163 #endif
164             window = e->xany.window;
165     }
166     return window;
167 }
168
169 static void event_set_lasttime(XEvent *e)
170 {
171     /* grab the lasttime and hack up the state */
172     switch (e->type) {
173     case ButtonPress:
174     case ButtonRelease:
175         event_lasttime = e->xbutton.time;
176         break;
177     case KeyPress:
178         event_lasttime = e->xkey.time;
179         break;
180     case KeyRelease:
181         event_lasttime = e->xkey.time;
182         break;
183     case MotionNotify:
184         event_lasttime = e->xmotion.time;
185         break;
186     case PropertyNotify:
187         event_lasttime = e->xproperty.time;
188         break;
189     case EnterNotify:
190     case LeaveNotify:
191         event_lasttime = e->xcrossing.time;
192         break;
193     default:
194         event_lasttime = CurrentTime;
195         break;
196     }
197 }
198
199 #define STRIP_MODS(s) \
200         s &= ~(LockMask | NumLockMask | ScrollLockMask), \
201         /* kill off the Button1Mask etc, only want the modifiers */ \
202         s &= (ControlMask | ShiftMask | Mod1Mask | \
203               Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
204
205 static void event_hack_mods(XEvent *e)
206 {
207     KeyCode *kp;
208     int i, k;
209
210     switch (e->type) {
211     case ButtonPress:
212     case ButtonRelease:
213         STRIP_MODS(e->xbutton.state);
214         break;
215     case KeyPress:
216         STRIP_MODS(e->xkey.state);
217         break;
218     case KeyRelease:
219         STRIP_MODS(e->xkey.state);
220         /* remove from the state the mask of the modifier being released, if
221            it is a modifier key being released (this is a little ugly..) */
222         kp = modmap->modifiermap;
223         for (i = 0; i < mask_table_size; ++i) {
224             for (k = 0; k < modmap->max_keypermod; ++k) {
225                 if (*kp == e->xkey.keycode) { /* found the keycode */
226                     /* remove the mask for it */
227                     e->xkey.state &= ~mask_table[i];
228                     /* cause the first loop to break; */
229                     i = mask_table_size;
230                     break; /* get outta here! */
231                 }
232                 ++kp;
233             }
234         }
235         break;
236     case MotionNotify:
237         STRIP_MODS(e->xmotion.state);
238         /* compress events */
239         {
240             XEvent ce;
241             while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
242                                           e->type, &ce)) {
243                 e->xmotion.x_root = ce.xmotion.x_root;
244                 e->xmotion.y_root = ce.xmotion.y_root;
245             }
246         }
247         break;
248     }
249 }
250
251 static gboolean event_ignore(XEvent *e, Client *client)
252 {
253     switch(e->type) {
254     case FocusIn:
255         /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
256            because of RevertToPointerRoot. If the focus ends up reverting to
257            pointer root on a workspace change, then the FocusIn event that we
258            want will be of type NotifyAncestor. This situation does not occur
259            for FocusOut, so it is safely ignored there.
260         */
261         if (INVALID_FOCUSIN(e) ||
262             client == NULL) {
263 #ifdef DEBUG_FOCUS
264         g_message("FocusIn on %lx mode %d detail %d IGNORED", e->xfocus.window,
265                   e->xfocus.mode, e->xfocus.detail);
266 #endif
267             /* says a client was not found for the event (or a valid FocusIn
268                event was not found.
269             */
270             e->xfocus.window = None;
271             return TRUE;
272         }
273
274 #ifdef DEBUG_FOCUS
275         g_message("FocusIn on %lx mode %d detail %d", e->xfocus.window,
276                   e->xfocus.mode, e->xfocus.detail);
277 #endif
278         break;
279     case FocusOut:
280         if (INVALID_FOCUSOUT(e)) {
281 #ifdef DEBUG_FOCUS
282         g_message("FocusOut on %lx mode %d detail %d IGNORED",
283                   e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
284 #endif
285             return TRUE;
286         }
287
288 #ifdef DEBUG_FOCUS
289         g_message("FocusOut on %lx mode %d detail %d",
290                   e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
291 #endif
292
293         {
294             XEvent fe;
295             gboolean fallback = TRUE;
296
297             while (TRUE) {
298                 if (!XCheckTypedWindowEvent(ob_display, FocusOut,
299                                             e->xfocus.window,&fe))
300                     if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
301                         break;
302                 if (fe.type == FocusOut) {
303 #ifdef DEBUG_FOCUS
304                     g_message("found pending FocusOut");
305 #endif
306                     if (!INVALID_FOCUSOUT(&fe)) {
307                         /* if there is a VALID FocusOut still coming, don't
308                            fallback focus yet, we'll deal with it then */
309                         XPutBackEvent(ob_display, &fe);
310                         fallback = FALSE;
311                         break;
312                     }
313                 } else {
314 #ifdef DEBUG_FOCUS
315                     g_message("found pending FocusIn");
316 #endif
317                     /* is the focused window getting a FocusOut/In back to
318                        itself? */
319                     if (fe.xfocus.window == e->xfocus.window &&
320                         !event_ignore(&fe, client)) {
321 #ifdef DEBUG_FOCUS
322                         g_message("focused window got an Out/In back to "
323                                   "itself IGNORED both");
324 #endif
325                         return TRUE;
326                     }
327
328                     /* once all the FocusOut's have been dealt with, if there
329                        is a FocusIn still left and it is valid, then use it */
330                     event_process(&fe);
331                     /* secret magic way of event_process telling us that no
332                        client was found for the FocusIn event. ^_^ */
333                     if (fe.xfocus.window != None) {
334                         fallback = FALSE;
335                         break;
336                     }
337                 }
338             }
339             if (fallback) {
340 #ifdef DEBUG_FOCUS
341                 g_message("no valid FocusIn and no FocusOut events found, "
342                           "falling back");
343 #endif
344                 focus_fallback(Fallback_NoFocus);
345             }
346         }
347         break;
348     case EnterNotify:
349     case LeaveNotify:
350         /* NotifyUngrab occurs when a mouse button is released and the event is
351            caused, like when lowering a window */
352         /* NotifyVirtual occurs when ungrabbing the pointer */
353         if (e->xcrossing.mode == NotifyGrab ||
354             e->xcrossing.detail == NotifyInferior ||
355             (e->xcrossing.mode == NotifyUngrab &&
356              e->xcrossing.detail == NotifyVirtual)) {
357 #ifdef DEBUG_FOCUS
358             g_message("%sNotify mode %d detail %d on %lx IGNORED",
359                       (e->type == EnterNotify ? "Enter" : "Leave"),
360                       e->xcrossing.mode,
361                       e->xcrossing.detail, client?client->window:0);
362 #endif
363             return TRUE;
364         }
365 #ifdef DEBUG_FOCUS
366         g_message("%sNotify mode %d detail %d on %lx",
367                   (e->type == EnterNotify ? "Enter" : "Leave"),
368                   e->xcrossing.mode,
369                   e->xcrossing.detail, client?client->window:0);
370 #endif
371         break;
372     }
373     return FALSE;
374 }
375
376 static void event_process(XEvent *e)
377 {
378     Window window;
379     Client *client = NULL;
380     Slit *slit = NULL;
381     SlitApp *slitapp = NULL;
382     Menu *menu = NULL;
383
384     window = event_get_window(e);
385     if (!(client = g_hash_table_lookup(client_map, &window)))
386         if (!(slitapp = g_hash_table_lookup(slit_app_map, &window)))
387             if (!(slit = g_hash_table_lookup(slit_map, &window)))
388                 menu = g_hash_table_lookup(menu_map, &window);
389
390     event_set_lasttime(e);
391     event_hack_mods(e);
392     if (event_ignore(e, client))
393         return;
394
395     /* deal with it in the kernel */
396     if (menu) {
397         event_handle_menu(menu, e);
398         return;
399     } else if (client)
400         event_handle_client(client, e);
401     else if (slitapp)
402         event_handle_slitapp(slitapp, e);
403     else if (slit)
404         event_handle_slit(slit, e);
405     else if (window == ob_root)
406         event_handle_root(e);
407     else if (e->type == MapRequest)
408         client_manage(window);
409     else if (e->type == ConfigureRequest) {
410         /* unhandled configure requests must be used to configure the
411            window directly */
412         XWindowChanges xwc;
413                
414         xwc.x = e->xconfigurerequest.x;
415         xwc.y = e->xconfigurerequest.y;
416         xwc.width = e->xconfigurerequest.width;
417         xwc.height = e->xconfigurerequest.height;
418         xwc.border_width = e->xconfigurerequest.border_width;
419         xwc.sibling = e->xconfigurerequest.above;
420         xwc.stack_mode = e->xconfigurerequest.detail;
421        
422         /* we are not to be held responsible if someone sends us an
423            invalid request! */
424         xerror_set_ignore(TRUE);
425         XConfigureWindow(ob_display, window,
426                          e->xconfigurerequest.value_mask, &xwc);
427         xerror_set_ignore(FALSE);
428     }
429
430     if (moveresize_in_progress)
431         if (e->type == MotionNotify || e->type == ButtonRelease ||
432             e->type == ButtonPress ||
433             e->type == KeyPress || e->type == KeyRelease) {
434             moveresize_event(e);
435
436             return; /* no dispatch! */
437             
438         }
439
440     /* user input (action-bound) events */
441     /*
442     if (e->type == ButtonPress || e->type == ButtonRelease ||
443         e->type == MotionNotify)
444         mouse_event(e, client);
445     else if (e->type == KeyPress || e->type == KeyRelease)
446         ;
447     */
448
449     /* dispatch the event to registered handlers */
450     dispatch_x(e, client);
451 }
452
453 static void event_handle_root(XEvent *e)
454 {
455     Atom msgtype;
456      
457     switch(e->type) {
458     case ClientMessage:
459         if (e->xclient.format != 32) break;
460
461         msgtype = e->xclient.message_type;
462         if (msgtype == prop_atoms.net_current_desktop) {
463             unsigned int d = e->xclient.data.l[0];
464             if (d < screen_num_desktops)
465                 screen_set_desktop(d);
466         } else if (msgtype == prop_atoms.net_number_of_desktops) {
467             unsigned int d = e->xclient.data.l[0];
468             if (d > 0)
469                 screen_set_num_desktops(d);
470         } else if (msgtype == prop_atoms.net_showing_desktop) {
471             screen_show_desktop(e->xclient.data.l[0] != 0);
472         }
473         break;
474     case PropertyNotify:
475         if (e->xproperty.atom == prop_atoms.net_desktop_names)
476             screen_update_desktop_names();
477         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
478             screen_update_layout();
479         break;
480     case ConfigureNotify:
481 #ifdef XRANDR
482         XRRUpdateConfiguration(e);
483 #endif
484         if (e->xconfigure.width != screen_physical_size.width ||
485             e->xconfigure.height != screen_physical_size.height)
486             screen_resize(e->xconfigure.width, e->xconfigure.height);
487         break;
488     default:
489         ;
490 #ifdef VIDMODE
491         if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
492             g_message("VIDMODE EVENT");
493         }
494 #endif
495     }
496 }
497
498 static void event_handle_client(Client *client, XEvent *e)
499 {
500     XEvent ce;
501     Atom msgtype;
502     int i=0;
503      
504     switch (e->type) {
505     case ButtonPress:
506     case ButtonRelease:
507         switch (frame_context(client, e->xbutton.window)) {
508         case Context_Maximize:
509             client->frame->max_press = (e->type == ButtonPress);
510             framerender_frame(client->frame);
511             break;
512         case Context_Close:
513             client->frame->close_press = (e->type == ButtonPress);
514             framerender_frame(client->frame);
515             break;
516         case Context_Iconify:
517             client->frame->iconify_press = (e->type == ButtonPress);
518             framerender_frame(client->frame);
519             break;
520         case Context_AllDesktops:
521             client->frame->desk_press = (e->type == ButtonPress);
522             framerender_frame(client->frame);
523             break; 
524         case Context_Shade:
525             client->frame->shade_press = (e->type == ButtonPress);
526             framerender_frame(client->frame);
527             break;
528         default:
529             /* nothing changes with clicks for any other contexts */
530             break;
531         }
532         break;
533     case FocusIn:
534 #ifdef DEBUG_FOCUS
535         g_message("FocusIn on client for %lx", client->window);
536 #endif
537         focus_set_client(client);
538         frame_adjust_focus(client->frame, TRUE);
539         break;
540     case FocusOut:
541 #ifdef DEBUG_FOCUS
542         g_message("FocusOut on client for %lx", client->window);
543 #endif
544         /* are we a fullscreen window or a transient of one? (checks layer)
545            if we are then we need to be iconified since we are losing focus
546          */
547         if (client->layer == Layer_Fullscreen && !client->iconic &&
548             !client_search_focus_tree_full(client))
549             /* iconify fullscreen windows when they and their transients
550                aren't focused */
551             client_iconify(client, TRUE, TRUE);
552         frame_adjust_focus(client->frame, FALSE);
553         break;
554     case EnterNotify:
555         if (client_normal(client)) {
556             if (ob_state == State_Starting) {
557                 /* move it to the top of the focus order */
558                 guint desktop = client->desktop;
559                 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
560                 focus_order[desktop] = g_list_remove(focus_order[desktop],
561                                                      client);
562                 focus_order[desktop] = g_list_prepend(focus_order[desktop],
563                                                       client);
564             } else if (config_focus_follow) {
565 #ifdef DEBUG_FOCUS
566                 g_message("EnterNotify on %lx, focusing window",
567                           client->window);
568 #endif
569                 client_focus(client);
570             }
571         }
572         break;
573     case ConfigureRequest:
574         /* compress these */
575         while (XCheckTypedWindowEvent(ob_display, client->window,
576                                       ConfigureRequest, &ce)) {
577             ++i;
578             /* XXX if this causes bad things.. we can compress config req's
579                with the same mask. */
580             e->xconfigurerequest.value_mask |=
581                 ce.xconfigurerequest.value_mask;
582             if (ce.xconfigurerequest.value_mask & CWX)
583                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
584             if (ce.xconfigurerequest.value_mask & CWY)
585                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
586             if (ce.xconfigurerequest.value_mask & CWWidth)
587                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
588             if (ce.xconfigurerequest.value_mask & CWHeight)
589                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
590             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
591                 e->xconfigurerequest.border_width =
592                     ce.xconfigurerequest.border_width;
593             if (ce.xconfigurerequest.value_mask & CWStackMode)
594                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
595         }
596
597         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
598         if (client->iconic || client->shaded) return;
599
600         if (e->xconfigurerequest.value_mask & CWBorderWidth)
601             client->border_width = e->xconfigurerequest.border_width;
602
603         /* resize, then move, as specified in the EWMH section 7.7 */
604         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
605                                                CWX | CWY)) {
606             int x, y, w, h;
607             Corner corner;
608                
609             x = (e->xconfigurerequest.value_mask & CWX) ?
610                 e->xconfigurerequest.x : client->area.x;
611             y = (e->xconfigurerequest.value_mask & CWY) ?
612                 e->xconfigurerequest.y : client->area.y;
613             w = (e->xconfigurerequest.value_mask & CWWidth) ?
614                 e->xconfigurerequest.width : client->area.width;
615             h = (e->xconfigurerequest.value_mask & CWHeight) ?
616                 e->xconfigurerequest.height : client->area.height;
617                
618             switch (client->gravity) {
619             case NorthEastGravity:
620             case EastGravity:
621                 corner = Corner_TopRight;
622                 break;
623             case SouthWestGravity:
624             case SouthGravity:
625                 corner = Corner_BottomLeft;
626                 break;
627             case SouthEastGravity:
628                 corner = Corner_BottomRight;
629                 break;
630             default:     /* NorthWest, Static, etc */
631                 corner = Corner_TopLeft;
632             }
633
634             client_configure(client, corner, x, y, w, h, FALSE, FALSE);
635         }
636
637         if (e->xconfigurerequest.value_mask & CWStackMode) {
638             switch (e->xconfigurerequest.detail) {
639             case Below:
640             case BottomIf:
641                 stacking_lower(client);
642                 break;
643
644             case Above:
645             case TopIf:
646             default:
647                 stacking_raise(client);
648                 break;
649             }
650         }
651         break;
652     case UnmapNotify:
653         if (client->ignore_unmaps) {
654             client->ignore_unmaps--;
655             break;
656         }
657         client_unmanage(client);
658         break;
659     case DestroyNotify:
660         client_unmanage(client);
661         break;
662     case ReparentNotify:
663         /* this is when the client is first taken captive in the frame */
664         if (e->xreparent.parent == client->frame->plate) break;
665
666         /*
667           This event is quite rare and is usually handled in unmapHandler.
668           However, if the window is unmapped when the reparent event occurs,
669           the window manager never sees it because an unmap event is not sent
670           to an already unmapped window.
671         */
672
673         /* we don't want the reparent event, put it back on the stack for the
674            X server to deal with after we unmanage the window */
675         XPutBackEvent(ob_display, e);
676      
677         client_unmanage(client);
678         break;
679     case MapRequest:
680         g_message("MapRequest for 0x%lx", client->window);
681         if (!client->iconic) break; /* this normally doesn't happen, but if it
682                                        does, we don't want it! */
683         if (screen_showing_desktop)
684             screen_show_desktop(FALSE);
685         client_iconify(client, FALSE, TRUE);
686         if (!client->frame->visible)
687             /* if its not visible still, then don't mess with it */
688             break;
689         if (client->shaded)
690             client_shade(client, FALSE);
691         client_focus(client);
692         stacking_raise(client);
693         break;
694     case ClientMessage:
695         /* validate cuz we query stuff off the client here */
696         if (!client_validate(client)) break;
697   
698         if (e->xclient.format != 32) return;
699
700         msgtype = e->xclient.message_type;
701         if (msgtype == prop_atoms.wm_change_state) {
702             /* compress changes into a single change */
703             while (XCheckTypedWindowEvent(ob_display, e->type,
704                                           client->window, &ce)) {
705                 /* XXX: it would be nice to compress ALL messages of a
706                    type, not just messages in a row without other
707                    message types between. */
708                 if (ce.xclient.message_type != msgtype) {
709                     XPutBackEvent(ob_display, &ce);
710                     break;
711                 }
712                 e->xclient = ce.xclient;
713             }
714             client_set_wm_state(client, e->xclient.data.l[0]);
715         } else if (msgtype == prop_atoms.net_wm_desktop) {
716             /* compress changes into a single change */
717             while (XCheckTypedWindowEvent(ob_display, e->type,
718                                           client->window, &ce)) {
719                 /* XXX: it would be nice to compress ALL messages of a
720                    type, not just messages in a row without other
721                    message types between. */
722                 if (ce.xclient.message_type != msgtype) {
723                     XPutBackEvent(ob_display, &ce);
724                     break;
725                 }
726                 e->xclient = ce.xclient;
727             }
728             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
729                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
730                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
731                                    FALSE);
732         } else if (msgtype == prop_atoms.net_wm_state) {
733             /* can't compress these */
734             g_message("net_wm_state %s %ld %ld for 0x%lx",
735                       (e->xclient.data.l[0] == 0 ? "Remove" :
736                        e->xclient.data.l[0] == 1 ? "Add" :
737                        e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
738                       e->xclient.data.l[1], e->xclient.data.l[2],
739                       client->window);
740             client_set_state(client, e->xclient.data.l[0],
741                              e->xclient.data.l[1], e->xclient.data.l[2]);
742         } else if (msgtype == prop_atoms.net_close_window) {
743             g_message("net_close_window for 0x%lx", client->window);
744             client_close(client);
745         } else if (msgtype == prop_atoms.net_active_window) {
746             g_message("net_active_window for 0x%lx", client->window);
747             client_activate(client);
748         } else if (msgtype == prop_atoms.net_wm_moveresize) {
749             g_message("net_wm_moveresize for 0x%lx", client->window);
750             if ((Atom)e->xclient.data.l[2] ==
751                 prop_atoms.net_wm_moveresize_size_topleft ||
752                 (Atom)e->xclient.data.l[2] ==
753                 prop_atoms.net_wm_moveresize_size_top ||
754                 (Atom)e->xclient.data.l[2] ==
755                 prop_atoms.net_wm_moveresize_size_topright ||
756                 (Atom)e->xclient.data.l[2] ==
757                 prop_atoms.net_wm_moveresize_size_right ||
758                 (Atom)e->xclient.data.l[2] ==
759                 prop_atoms.net_wm_moveresize_size_right ||
760                 (Atom)e->xclient.data.l[2] ==
761                 prop_atoms.net_wm_moveresize_size_bottomright ||
762                 (Atom)e->xclient.data.l[2] ==
763                 prop_atoms.net_wm_moveresize_size_bottom ||
764                 (Atom)e->xclient.data.l[2] ==
765                 prop_atoms.net_wm_moveresize_size_bottomleft ||
766                 (Atom)e->xclient.data.l[2] ==
767                 prop_atoms.net_wm_moveresize_size_left ||
768                 (Atom)e->xclient.data.l[2] ==
769                 prop_atoms.net_wm_moveresize_move ||
770                 (Atom)e->xclient.data.l[2] ==
771                 prop_atoms.net_wm_moveresize_size_keyboard ||
772                 (Atom)e->xclient.data.l[2] ==
773                 prop_atoms.net_wm_moveresize_move_keyboard) {
774
775                 moveresize_start(client, e->xclient.data.l[0],
776                                  e->xclient.data.l[1], e->xclient.data.l[3],
777                                  e->xclient.data.l[2]);
778             }
779         } else if (msgtype == prop_atoms.net_moveresize_window) {
780             int oldg = client->gravity;
781             int tmpg, x, y, w, h;
782
783             if (e->xclient.data.l[0] & 0xff)
784                 tmpg = e->xclient.data.l[0] & 0xff;
785             else
786                 tmpg = oldg;
787
788             if (e->xclient.data.l[0] & 1 << 8)
789                 x = e->xclient.data.l[1];
790             else
791                 x = client->area.x;
792             if (e->xclient.data.l[0] & 1 << 9)
793                 y = e->xclient.data.l[2];
794             else
795                 y = client->area.y;
796             if (e->xclient.data.l[0] & 1 << 10)
797                 w = e->xclient.data.l[3];
798             else
799                 w = client->area.y;
800             if (e->xclient.data.l[0] & 1 << 11)
801                 h = e->xclient.data.l[4];
802             else
803                 h = client->area.y;
804             client->gravity = tmpg;
805             client_configure(client, Corner_TopLeft, x, y, w, h, TRUE, TRUE);
806             client->gravity = oldg;
807         }
808         break;
809     case PropertyNotify:
810         /* validate cuz we query stuff off the client here */
811         if (!client_validate(client)) break;
812   
813         /* compress changes to a single property into a single change */
814         while (XCheckTypedWindowEvent(ob_display, e->type,
815                                       client->window, &ce)) {
816             /* XXX: it would be nice to compress ALL changes to a property,
817                not just changes in a row without other props between. */
818             if (ce.xproperty.atom != e->xproperty.atom) {
819                 XPutBackEvent(ob_display, &ce);
820                 break;
821             }
822         }
823
824         msgtype = e->xproperty.atom;
825         if (msgtype == XA_WM_NORMAL_HINTS) {
826             client_update_normal_hints(client);
827             /* normal hints can make a window non-resizable */
828             client_setup_decor_and_functions(client);
829         }
830         else if (msgtype == XA_WM_HINTS)
831             client_update_wmhints(client);
832         else if (msgtype == XA_WM_TRANSIENT_FOR) {
833             client_update_transient_for(client);
834             client_get_type(client);
835             /* type may have changed, so update the layer */
836             client_calc_layer(client);
837             client_setup_decor_and_functions(client);
838         }
839         else if (msgtype == prop_atoms.net_wm_name ||
840                  msgtype == prop_atoms.wm_name ||
841                  msgtype == prop_atoms.net_wm_icon_name ||
842                  msgtype == prop_atoms.wm_icon_name)
843             client_update_title(client);
844         else if (msgtype == prop_atoms.wm_class)
845             client_update_class(client);
846         else if (msgtype == prop_atoms.wm_protocols) {
847             client_update_protocols(client);
848             client_setup_decor_and_functions(client);
849         }
850         else if (msgtype == prop_atoms.net_wm_strut)
851             client_update_strut(client);
852         else if (msgtype == prop_atoms.net_wm_icon)
853             client_update_icons(client);
854         else if (msgtype == prop_atoms.kwm_win_icon)
855             client_update_kwm_icon(client);
856     default:
857         ;
858 #ifdef SHAPE
859         if (extensions_shape && e->type == extensions_shape_event_basep) {
860             client->shaped = ((XShapeEvent*)e)->shaped;
861             frame_adjust_shape(client->frame);
862         }
863 #endif
864     }
865 }
866
867 static void event_handle_menu(Menu *menu, XEvent *e)
868 {
869     MenuEntry *entry;
870
871     g_message("EVENT %d", e->type);
872     switch (e->type) {
873     case ButtonPress:
874         g_message("BUTTON PRESS");
875         if (e->xbutton.button == 3)
876             menu_hide(menu);
877         else if (e->xbutton.button == 1) {
878             entry = menu_find_entry(menu, e->xbutton.window);
879             if (!entry)
880                 stacking_raise(MENU_AS_WINDOW(menu));
881         }
882         break;
883     case ButtonRelease:
884         g_message("BUTTON RELEASED");
885         if (!menu->shown) break;
886
887 /*        grab_pointer_window(FALSE, None, menu->frame);*/
888
889         entry = menu_find_entry(menu, e->xbutton.window);
890         if (entry) {
891             int junk;
892             Window wjunk;
893             guint ujunk, b, w, h;
894             XGetGeometry(ob_display, e->xbutton.window,
895                          &wjunk, &junk, &junk, &w, &h, &b, &ujunk);
896             if (e->xbutton.x >= (signed)-b &&
897                 e->xbutton.y >= (signed)-b &&
898                 e->xbutton.x < (signed)(w+b) &&
899                 e->xbutton.y < (signed)(h+b)) {
900                 menu_entry_fire(entry);
901             }
902         }
903         
904         break;
905     case EnterNotify:
906     case LeaveNotify:
907         g_message("enter/leave");
908         entry = menu_find_entry(menu, e->xcrossing.window);
909         if (entry) {
910             if (menu->mouseover)
911                 menu->mouseover(entry, e->type == EnterNotify);
912             else
913                 menu_control_mouseover(entry, e->type == EnterNotify);
914             
915             menu_entry_render(entry);
916         }
917         break;
918     }
919 }
920
921 static void event_handle_slit(Slit *s, XEvent *e)
922 {
923     switch (e->type) {
924     case ButtonPress:
925         stacking_raise(SLIT_AS_WINDOW(s));
926     case EnterNotify:
927         slit_hide(s, FALSE);
928         break;
929     case LeaveNotify:
930         slit_hide(s, TRUE);
931         break;
932     }
933 }
934
935 static void event_handle_slitapp(SlitApp *app, XEvent *e)
936 {
937     switch (e->type) {
938     case MotionNotify:
939         slit_app_drag(app, &e->xmotion);
940         break;
941     case UnmapNotify:
942         if (app->ignore_unmaps) {
943             app->ignore_unmaps--;
944             break;
945         }
946         slit_remove(app, TRUE);
947         break;
948     case DestroyNotify:
949         slit_remove(app, FALSE);
950         break;
951     case ReparentNotify:
952         slit_remove(app, FALSE);
953         break;
954     case ConfigureNotify:
955         slit_app_configure(app, e->xconfigure.width, e->xconfigure.height);
956         break;
957     }
958 }