]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/event.c
all my changes while i was offline.
[mikachu/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     event_set_lasttime(e);
382     event_hack_mods(e);
383     if (event_ignore(e, client))
384         return;
385
386     /* deal with it in the kernel */
387     if (menu) {
388         event_handle_menu(menu, e);
389         return;
390     } else if (client)
391         event_handle_client(client, e);
392     else if (window == ob_root)
393         event_handle_root(e);
394     else if (e->type == MapRequest)
395         client_manage(window);
396     else if (e->type == ConfigureRequest) {
397         /* unhandled configure requests must be used to configure the
398            window directly */
399         XWindowChanges xwc;
400                
401         xwc.x = e->xconfigurerequest.x;
402         xwc.y = e->xconfigurerequest.y;
403         xwc.width = e->xconfigurerequest.width;
404         xwc.height = e->xconfigurerequest.height;
405         xwc.border_width = e->xconfigurerequest.border_width;
406         xwc.sibling = e->xconfigurerequest.above;
407         xwc.stack_mode = e->xconfigurerequest.detail;
408        
409         /* we are not to be held responsible if someone sends us an
410            invalid request! */
411         xerror_set_ignore(TRUE);
412         XConfigureWindow(ob_display, window,
413                          e->xconfigurerequest.value_mask, &xwc);
414         xerror_set_ignore(FALSE);
415     }
416
417     if (moveresize_in_progress)
418         if (e->type == MotionNotify || e->type == ButtonRelease ||
419             e->type == ButtonPress ||
420             e->type == KeyPress || e->type == KeyRelease) {
421             moveresize_event(e);
422
423             return; /* no dispatch! */
424             
425         }
426
427     /* user input (action-bound) events */
428     /*
429     if (e->type == ButtonPress || e->type == ButtonRelease ||
430         e->type == MotionNotify)
431         mouse_event(e, client);
432     else if (e->type == KeyPress || e->type == KeyRelease)
433         ;
434     */
435
436     /* dispatch the event to registered handlers */
437     dispatch_x(e, client);
438 }
439
440 static void event_handle_root(XEvent *e)
441 {
442     Atom msgtype;
443      
444     switch(e->type) {
445     case ClientMessage:
446         if (e->xclient.format != 32) break;
447
448         msgtype = e->xclient.message_type;
449         if (msgtype == prop_atoms.net_current_desktop) {
450             unsigned int d = e->xclient.data.l[0];
451             if (d < screen_num_desktops)
452                 screen_set_desktop(d);
453         } else if (msgtype == prop_atoms.net_number_of_desktops) {
454             unsigned int d = e->xclient.data.l[0];
455             if (d > 0)
456                 screen_set_num_desktops(d);
457         } else if (msgtype == prop_atoms.net_showing_desktop) {
458             screen_show_desktop(e->xclient.data.l[0] != 0);
459         }
460         break;
461     case PropertyNotify:
462         if (e->xproperty.atom == prop_atoms.net_desktop_names)
463             screen_update_desktop_names();
464         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
465             screen_update_layout();
466         break;
467     case ConfigureNotify:
468 #ifdef XRANDR
469         XRRUpdateConfiguration(e);
470 #endif
471         if (e->xconfigure.width != screen_physical_size.width ||
472             e->xconfigure.height != screen_physical_size.height)
473             screen_resize(e->xconfigure.width, e->xconfigure.height);
474         break;
475     default:
476         ;
477 #ifdef VIDMODE
478         if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
479             g_message("VIDMODE EVENT");
480         }
481 #endif
482     }
483 }
484
485 static void event_handle_client(Client *client, XEvent *e)
486 {
487     XEvent ce;
488     Atom msgtype;
489     int i=0;
490      
491     switch (e->type) {
492     case ButtonPress:
493     case ButtonRelease:
494         switch (frame_context(client, e->xbutton.window)) {
495         case Context_Maximize:
496             client->frame->max_press = (e->type == ButtonPress);
497             framerender_frame(client->frame);
498             break;
499         case Context_Close:
500             client->frame->close_press = (e->type == ButtonPress);
501             framerender_frame(client->frame);
502             break;
503         case Context_Iconify:
504             client->frame->iconify_press = (e->type == ButtonPress);
505             framerender_frame(client->frame);
506             break;
507         case Context_AllDesktops:
508             client->frame->desk_press = (e->type == ButtonPress);
509             framerender_frame(client->frame);
510             break; 
511         case Context_Shade:
512             client->frame->shade_press = (e->type == ButtonPress);
513             framerender_frame(client->frame);
514             break;
515         default:
516             /* nothing changes with clicks for any other contexts */
517             break;
518         }
519         break;
520     case FocusIn:
521 #ifdef DEBUG_FOCUS
522         g_message("FocusIn on client for %lx", client->window);
523 #endif
524         focus_set_client(client);
525         frame_adjust_focus(client->frame, TRUE);
526         break;
527     case FocusOut:
528 #ifdef DEBUG_FOCUS
529         g_message("FocusOut on client for %lx", client->window);
530 #endif
531         /* are we a fullscreen window or a transient of one? (checks layer)
532            if we are then we need to be iconified since we are losing focus
533          */
534         if (client->layer == Layer_Fullscreen && !client->iconic &&
535             !client_search_focus_tree_full(client))
536             /* iconify fullscreen windows when they and their transients
537                aren't focused */
538             client_iconify(client, TRUE, TRUE);
539         frame_adjust_focus(client->frame, FALSE);
540         break;
541     case EnterNotify:
542         if (client_normal(client)) {
543             if (ob_state == State_Starting) {
544                 /* move it to the top of the focus order */
545                 guint desktop = client->desktop;
546                 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
547                 focus_order[desktop] = g_list_remove(focus_order[desktop],
548                                                      client);
549                 focus_order[desktop] = g_list_prepend(focus_order[desktop],
550                                                       client);
551             } else if (config_focus_follow) {
552 #ifdef DEBUG_FOCUS
553                 g_message("EnterNotify on %lx, focusing window",
554                           client->window);
555 #endif
556                 client_focus(client);
557             }
558         }
559         break;
560     case ConfigureRequest:
561         /* compress these */
562         while (XCheckTypedWindowEvent(ob_display, client->window,
563                                       ConfigureRequest, &ce)) {
564             ++i;
565             /* XXX if this causes bad things.. we can compress config req's
566                with the same mask. */
567             e->xconfigurerequest.value_mask |=
568                 ce.xconfigurerequest.value_mask;
569             if (ce.xconfigurerequest.value_mask & CWX)
570                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
571             if (ce.xconfigurerequest.value_mask & CWY)
572                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
573             if (ce.xconfigurerequest.value_mask & CWWidth)
574                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
575             if (ce.xconfigurerequest.value_mask & CWHeight)
576                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
577             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
578                 e->xconfigurerequest.border_width =
579                     ce.xconfigurerequest.border_width;
580             if (ce.xconfigurerequest.value_mask & CWStackMode)
581                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
582         }
583
584         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
585         if (client->iconic || client->shaded) return;
586
587         if (e->xconfigurerequest.value_mask & CWBorderWidth)
588             client->border_width = e->xconfigurerequest.border_width;
589
590         /* resize, then move, as specified in the EWMH section 7.7 */
591         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
592                                                CWX | CWY)) {
593             int x, y, w, h;
594             Corner corner;
595                
596             x = (e->xconfigurerequest.value_mask & CWX) ?
597                 e->xconfigurerequest.x : client->area.x;
598             y = (e->xconfigurerequest.value_mask & CWY) ?
599                 e->xconfigurerequest.y : client->area.y;
600             w = (e->xconfigurerequest.value_mask & CWWidth) ?
601                 e->xconfigurerequest.width : client->area.width;
602             h = (e->xconfigurerequest.value_mask & CWHeight) ?
603                 e->xconfigurerequest.height : client->area.height;
604                
605             switch (client->gravity) {
606             case NorthEastGravity:
607             case EastGravity:
608                 corner = Corner_TopRight;
609                 break;
610             case SouthWestGravity:
611             case SouthGravity:
612                 corner = Corner_BottomLeft;
613                 break;
614             case SouthEastGravity:
615                 corner = Corner_BottomRight;
616                 break;
617             default:     /* NorthWest, Static, etc */
618                 corner = Corner_TopLeft;
619             }
620
621             client_configure(client, corner, x, y, w, h, FALSE, FALSE);
622         }
623
624         if (e->xconfigurerequest.value_mask & CWStackMode) {
625             switch (e->xconfigurerequest.detail) {
626             case Below:
627             case BottomIf:
628                 stacking_lower(client);
629                 break;
630
631             case Above:
632             case TopIf:
633             default:
634                 stacking_raise(client);
635                 break;
636             }
637         }
638         break;
639     case UnmapNotify:
640         if (client->ignore_unmaps) {
641             client->ignore_unmaps--;
642             break;
643         }
644         client_unmanage(client);
645         break;
646     case DestroyNotify:
647         client_unmanage(client);
648         break;
649     case ReparentNotify:
650         /* this is when the client is first taken captive in the frame */
651         if (e->xreparent.parent == client->frame->plate) break;
652
653         /*
654           This event is quite rare and is usually handled in unmapHandler.
655           However, if the window is unmapped when the reparent event occurs,
656           the window manager never sees it because an unmap event is not sent
657           to an already unmapped window.
658         */
659
660         /* we don't want the reparent event, put it back on the stack for the
661            X server to deal with after we unmanage the window */
662         XPutBackEvent(ob_display, e);
663      
664         client_unmanage(client);
665         break;
666     case MapRequest:
667         g_message("MapRequest for 0x%lx", client->window);
668         if (!client->iconic) break; /* this normally doesn't happen, but if it
669                                        does, we don't want it! */
670         if (screen_showing_desktop)
671             screen_show_desktop(FALSE);
672         client_iconify(client, FALSE, TRUE);
673         if (!client->frame->visible)
674             /* if its not visible still, then don't mess with it */
675             break;
676         if (client->shaded)
677             client_shade(client, FALSE);
678         client_focus(client);
679         stacking_raise(client);
680         break;
681     case ClientMessage:
682         /* validate cuz we query stuff off the client here */
683         if (!client_validate(client)) break;
684   
685         if (e->xclient.format != 32) return;
686
687         msgtype = e->xclient.message_type;
688         if (msgtype == prop_atoms.wm_change_state) {
689             /* compress changes into a single change */
690             while (XCheckTypedWindowEvent(ob_display, e->type,
691                                           client->window, &ce)) {
692                 /* XXX: it would be nice to compress ALL messages of a
693                    type, not just messages in a row without other
694                    message types between. */
695                 if (ce.xclient.message_type != msgtype) {
696                     XPutBackEvent(ob_display, &ce);
697                     break;
698                 }
699                 e->xclient = ce.xclient;
700             }
701             client_set_wm_state(client, e->xclient.data.l[0]);
702         } else if (msgtype == prop_atoms.net_wm_desktop) {
703             /* compress changes into a single change */
704             while (XCheckTypedWindowEvent(ob_display, e->type,
705                                           client->window, &ce)) {
706                 /* XXX: it would be nice to compress ALL messages of a
707                    type, not just messages in a row without other
708                    message types between. */
709                 if (ce.xclient.message_type != msgtype) {
710                     XPutBackEvent(ob_display, &ce);
711                     break;
712                 }
713                 e->xclient = ce.xclient;
714             }
715             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
716                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
717                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
718                                    FALSE);
719         } else if (msgtype == prop_atoms.net_wm_state) {
720             /* can't compress these */
721             g_message("net_wm_state %s %ld %ld for 0x%lx",
722                       (e->xclient.data.l[0] == 0 ? "Remove" :
723                        e->xclient.data.l[0] == 1 ? "Add" :
724                        e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
725                       e->xclient.data.l[1], e->xclient.data.l[2],
726                       client->window);
727             client_set_state(client, e->xclient.data.l[0],
728                              e->xclient.data.l[1], e->xclient.data.l[2]);
729         } else if (msgtype == prop_atoms.net_close_window) {
730             g_message("net_close_window for 0x%lx", client->window);
731             client_close(client);
732         } else if (msgtype == prop_atoms.net_active_window) {
733             g_message("net_active_window for 0x%lx", client->window);
734             if (screen_showing_desktop)
735                 screen_show_desktop(FALSE);
736             if (client->iconic)
737                 client_iconify(client, FALSE, TRUE);
738             else if (!client->frame->visible)
739                 /* if its not visible for other reasons, then don't mess
740                    with it */
741                 break;
742             if (client->shaded)
743                 client_shade(client, FALSE);
744             client_focus(client);
745             stacking_raise(client);
746         } else if (msgtype == prop_atoms.net_wm_moveresize) {
747             g_message("net_wm_moveresize for 0x%lx", client->window);
748             if ((Atom)e->xclient.data.l[2] ==
749                 prop_atoms.net_wm_moveresize_size_topleft ||
750                 (Atom)e->xclient.data.l[2] ==
751                 prop_atoms.net_wm_moveresize_size_top ||
752                 (Atom)e->xclient.data.l[2] ==
753                 prop_atoms.net_wm_moveresize_size_topright ||
754                 (Atom)e->xclient.data.l[2] ==
755                 prop_atoms.net_wm_moveresize_size_right ||
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_bottomright ||
760                 (Atom)e->xclient.data.l[2] ==
761                 prop_atoms.net_wm_moveresize_size_bottom ||
762                 (Atom)e->xclient.data.l[2] ==
763                 prop_atoms.net_wm_moveresize_size_bottomleft ||
764                 (Atom)e->xclient.data.l[2] ==
765                 prop_atoms.net_wm_moveresize_size_left ||
766                 (Atom)e->xclient.data.l[2] ==
767                 prop_atoms.net_wm_moveresize_move ||
768                 (Atom)e->xclient.data.l[2] ==
769                 prop_atoms.net_wm_moveresize_size_keyboard ||
770                 (Atom)e->xclient.data.l[2] ==
771                 prop_atoms.net_wm_moveresize_move_keyboard) {
772
773                 moveresize_start(client, e->xclient.data.l[0],
774                                  e->xclient.data.l[1], e->xclient.data.l[3],
775                                  e->xclient.data.l[2]);
776             }
777         } else if (msgtype == prop_atoms.net_moveresize_window) {
778             int oldg = client->gravity;
779             int tmpg, x, y, w, h;
780
781             if (e->xclient.data.l[0] & 0xff)
782                 tmpg = e->xclient.data.l[0] & 0xff;
783             else
784                 tmpg = oldg;
785
786             if (e->xclient.data.l[0] & 1 << 8)
787                 x = e->xclient.data.l[1];
788             else
789                 x = client->area.x;
790             if (e->xclient.data.l[0] & 1 << 9)
791                 y = e->xclient.data.l[2];
792             else
793                 y = client->area.y;
794             if (e->xclient.data.l[0] & 1 << 10)
795                 w = e->xclient.data.l[3];
796             else
797                 w = client->area.y;
798             if (e->xclient.data.l[0] & 1 << 11)
799                 h = e->xclient.data.l[4];
800             else
801                 h = client->area.y;
802             client->gravity = tmpg;
803             client_configure(client, Corner_TopLeft, x, y, w, h, TRUE, TRUE);
804             client->gravity = oldg;
805         }
806         break;
807     case PropertyNotify:
808         /* validate cuz we query stuff off the client here */
809         if (!client_validate(client)) break;
810   
811         /* compress changes to a single property into a single change */
812         while (XCheckTypedWindowEvent(ob_display, e->type,
813                                       client->window, &ce)) {
814             /* XXX: it would be nice to compress ALL changes to a property,
815                not just changes in a row without other props between. */
816             if (ce.xproperty.atom != e->xproperty.atom) {
817                 XPutBackEvent(ob_display, &ce);
818                 break;
819             }
820         }
821
822         msgtype = e->xproperty.atom;
823         if (msgtype == XA_WM_NORMAL_HINTS) {
824             client_update_normal_hints(client);
825             /* normal hints can make a window non-resizable */
826             client_setup_decor_and_functions(client);
827         }
828         else if (msgtype == XA_WM_HINTS)
829             client_update_wmhints(client);
830         else if (msgtype == XA_WM_TRANSIENT_FOR) {
831             client_update_transient_for(client);
832             client_get_type(client);
833             /* type may have changed, so update the layer */
834             client_calc_layer(client);
835             client_setup_decor_and_functions(client);
836         }
837         else if (msgtype == prop_atoms.net_wm_name ||
838                  msgtype == prop_atoms.wm_name ||
839                  msgtype == prop_atoms.net_wm_icon_name ||
840                  msgtype == prop_atoms.wm_icon_name)
841             client_update_title(client);
842         else if (msgtype == prop_atoms.wm_class)
843             client_update_class(client);
844         else if (msgtype == prop_atoms.wm_protocols) {
845             client_update_protocols(client);
846             client_setup_decor_and_functions(client);
847         }
848         else if (msgtype == prop_atoms.net_wm_strut)
849             client_update_strut(client);
850         else if (msgtype == prop_atoms.net_wm_icon)
851             client_update_icons(client);
852         else if (msgtype == prop_atoms.kwm_win_icon)
853             client_update_kwm_icon(client);
854     default:
855         ;
856 #ifdef SHAPE
857         if (extensions_shape && e->type == extensions_shape_event_basep) {
858             client->shaped = ((XShapeEvent*)e)->shaped;
859             frame_adjust_shape(client->frame);
860         }
861 #endif
862     }
863 }
864
865 static void event_handle_menu(Menu *menu, XEvent *e)
866 {
867     MenuEntry *entry;
868
869     g_message("EVENT %d", e->type);
870     switch (e->type) {
871     case ButtonPress:
872         if (e->xbutton.button == 3)
873             menu_hide(menu);
874         break;
875     case ButtonRelease:
876         if (!menu->shown) break;
877
878 /*        grab_pointer_window(FALSE, None, menu->frame);*/
879
880         entry = menu_find_entry(menu, e->xbutton.window);
881         if (entry) {
882             int junk;
883             Window wjunk;
884             guint ujunk, b, w, h;
885             XGetGeometry(ob_display, e->xbutton.window,
886                          &wjunk, &junk, &junk, &w, &h, &b, &ujunk);
887             if (e->xbutton.x >= (signed)-b &&
888                 e->xbutton.y >= (signed)-b &&
889                 e->xbutton.x < (signed)(w+b) &&
890                 e->xbutton.y < (signed)(h+b)) {
891                 menu_entry_fire(entry);
892             }
893         }
894         break;
895     case EnterNotify:
896     case LeaveNotify:
897         g_message("enter/leave");
898         entry = menu_find_entry(menu, e->xcrossing.window);
899         if (entry) {
900             entry->hilite = e->type == EnterNotify;
901             menu_entry_render(entry);
902         }
903         break;
904     }
905 }