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