the mouse grab screws that up
[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     default:
148 #ifdef XKB
149         if (extensions_xkb && e->type == extensions_xkb_event_basep) {
150             switch (((XkbAnyEvent*)&e)->xkb_type) {
151             case XkbBellNotify:
152                 window = ((XkbBellNotifyEvent*)&e)->window;
153             default:
154                 window = None;
155             }
156         } else
157 #endif
158             window = e->xany.window;
159     }
160     return window;
161 }
162
163 static void event_set_lasttime(XEvent *e)
164 {
165     /* grab the lasttime and hack up the state */
166     switch (e->type) {
167     case ButtonPress:
168     case ButtonRelease:
169         event_lasttime = e->xbutton.time;
170         break;
171     case KeyPress:
172         event_lasttime = e->xkey.time;
173         break;
174     case KeyRelease:
175         event_lasttime = e->xkey.time;
176         break;
177     case MotionNotify:
178         event_lasttime = e->xmotion.time;
179         break;
180     case PropertyNotify:
181         event_lasttime = e->xproperty.time;
182         break;
183     case EnterNotify:
184     case LeaveNotify:
185         event_lasttime = e->xcrossing.time;
186         break;
187     default:
188         event_lasttime = CurrentTime;
189         break;
190     }
191 }
192
193 #define STRIP_MODS(s) \
194         s &= ~(LockMask | NumLockMask | ScrollLockMask), \
195         /* kill off the Button1Mask etc, only want the modifiers */ \
196         s &= (ControlMask | ShiftMask | Mod1Mask | \
197               Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
198
199 static void event_hack_mods(XEvent *e)
200 {
201     KeyCode *kp;
202     int i, k;
203
204     switch (e->type) {
205     case ButtonPress:
206     case ButtonRelease:
207         STRIP_MODS(e->xbutton.state);
208         break;
209     case KeyPress:
210         STRIP_MODS(e->xkey.state);
211         break;
212     case KeyRelease:
213         STRIP_MODS(e->xkey.state);
214         /* remove from the state the mask of the modifier being released, if
215            it is a modifier key being released (this is a little ugly..) */
216         kp = modmap->modifiermap;
217         for (i = 0; i < mask_table_size; ++i) {
218             for (k = 0; k < modmap->max_keypermod; ++k) {
219                 if (*kp == e->xkey.keycode) { /* found the keycode */
220                     /* remove the mask for it */
221                     e->xkey.state &= ~mask_table[i];
222                     /* cause the first loop to break; */
223                     i = mask_table_size;
224                     break; /* get outta here! */
225                 }
226                 ++kp;
227             }
228         }
229         break;
230     case MotionNotify:
231         STRIP_MODS(e->xmotion.state);
232         /* compress events */
233         {
234             XEvent ce;
235             while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
236                                           e->type, &ce)) {
237                 e->xmotion.x_root = ce.xmotion.x_root;
238                 e->xmotion.y_root = ce.xmotion.y_root;
239             }
240         }
241         break;
242     }
243 }
244
245 static gboolean event_ignore(XEvent *e, Client *client)
246 {
247     switch(e->type) {
248     case FocusIn:
249         /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
250            because of RevertToPointerRoot. If the focus ends up reverting to
251            pointer root on a workspace change, then the FocusIn event that we
252            want will be of type NotifyAncestor. This situation does not occur
253            for FocusOut, so it is safely ignored there.
254         */
255         if (INVALID_FOCUSIN(e) ||
256             client == NULL) {
257 #ifdef DEBUG_FOCUS
258         g_message("FocusIn on %lx mode %d detail %d IGNORED", e->xfocus.window,
259                   e->xfocus.mode, e->xfocus.detail);
260 #endif
261             /* says a client was not found for the event (or a valid FocusIn
262                event was not found.
263             */
264             e->xfocus.window = None;
265             return TRUE;
266         }
267
268 #ifdef DEBUG_FOCUS
269         g_message("FocusIn on %lx mode %d detail %d", e->xfocus.window,
270                   e->xfocus.mode, e->xfocus.detail);
271 #endif
272         break;
273     case FocusOut:
274         if (INVALID_FOCUSOUT(e)) {
275 #ifdef DEBUG_FOCUS
276         g_message("FocusOut on %lx mode %d detail %d IGNORED",
277                   e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
278 #endif
279             return TRUE;
280         }
281
282 #ifdef DEBUG_FOCUS
283         g_message("FocusOut on %lx mode %d detail %d",
284                   e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
285 #endif
286
287         /* Try process a FocusIn first, and if a legit one isn't found, then
288            do the fallback shiznit. */
289         {
290             XEvent fe;
291             gboolean fallback = TRUE;
292
293             while (TRUE) {
294                 if (!XCheckTypedEvent(ob_display, FocusOut, &fe))
295                     if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
296                         break;
297                 if (fe.type == FocusOut) {
298 #ifdef DEBUG_FOCUS
299                     g_message("found pending FocusOut");
300 #endif
301                     if (!INVALID_FOCUSOUT(&fe)) {
302                         /* if there is a VALID FocusOut still coming, don't
303                            fallback focus yet, we'll deal with it then */
304                         XPutBackEvent(ob_display, &fe);
305                         fallback = FALSE;
306                         break;
307                     }
308                 } else {
309 #ifdef DEBUG_FOCUS
310                     g_message("found pending FocusIn");
311 #endif
312                     /* once all the FocusOut's have been dealt with, if there
313                        is a FocusIn still left and it is valid, then use it */
314                     event_process(&fe);
315                     /* secret magic way of event_process telling us that no
316                        client was found for the FocusIn event. ^_^ */
317                     if (fe.xfocus.window != None) {
318                         fallback = FALSE;
319                         break;
320                     }
321                 }
322             }
323             if (fallback) {
324 #ifdef DEBUG_FOCUS
325                 g_message("no valid FocusIn and no FocusOut events found, "
326                           "falling back");
327 #endif
328                 focus_fallback(Fallback_NoFocus);
329             }
330         }
331         break;
332     case EnterNotify:
333     case LeaveNotify:
334         /* NotifyUngrab occurs when a mouse button is released and the event is
335            caused, like when lowering a window */
336         /* NotifyVirtual occurs when ungrabbing the pointer */
337         if (e->xcrossing.mode == NotifyGrab ||
338             e->xcrossing.detail == NotifyInferior ||
339             (e->xcrossing.mode == NotifyUngrab &&
340              e->xcrossing.detail == NotifyVirtual)) {
341 #ifdef DEBUG_FOCUS
342             g_message("%sNotify mode %d detail %d on %lx IGNORED",
343                       (e->type == EnterNotify ? "Enter" : "Leave"),
344                       e->xcrossing.mode,
345                       e->xcrossing.detail, client?client->window:0);
346 #endif
347             return TRUE;
348         }
349 #ifdef DEBUG_FOCUS
350         g_message("%sNotify mode %d detail %d on %lx",
351                   (e->type == EnterNotify ? "Enter" : "Leave"),
352                   e->xcrossing.mode,
353                   e->xcrossing.detail, client?client->window:0);
354 #endif
355         break;
356     }
357     return FALSE;
358 }
359
360 static void event_process(XEvent *e)
361 {
362     Window window;
363     Client *client;
364     Menu *menu = NULL;
365
366     window = event_get_window(e);
367     if (!(client = g_hash_table_lookup(client_map, &window)))
368         menu = g_hash_table_lookup(menu_map, &window);
369     event_set_lasttime(e);
370     event_hack_mods(e);
371     if (event_ignore(e, client))
372         return;
373
374     /* deal with it in the kernel */
375     if (menu) {
376         event_handle_menu(menu, e);
377         return;
378     } else if (client)
379         event_handle_client(client, e);
380     else if (window == ob_root)
381         event_handle_root(e);
382     else if (e->type == MapRequest)
383         client_manage(window);
384     else if (e->type == ConfigureRequest) {
385         /* unhandled configure requests must be used to configure the
386            window directly */
387         XWindowChanges xwc;
388                
389         xwc.x = e->xconfigurerequest.x;
390         xwc.y = e->xconfigurerequest.y;
391         xwc.width = e->xconfigurerequest.width;
392         xwc.height = e->xconfigurerequest.height;
393         xwc.border_width = e->xconfigurerequest.border_width;
394         xwc.sibling = e->xconfigurerequest.above;
395         xwc.stack_mode = e->xconfigurerequest.detail;
396        
397         /* we are not to be held responsible if someone sends us an
398            invalid request! */
399         xerror_set_ignore(TRUE);
400         XConfigureWindow(ob_display, window,
401                          e->xconfigurerequest.value_mask, &xwc);
402         xerror_set_ignore(FALSE);
403     }
404
405     if (moveresize_in_progress)
406         if (e->type == MotionNotify || e->type == ButtonRelease ||
407             e->type == ButtonPress ||
408             e->type == KeyPress || e->type == KeyRelease) {
409             moveresize_event(e);
410
411             return; /* no dispatch! */
412             
413         }
414
415     /* user input (action-bound) events */
416     /*
417     if (e->type == ButtonPress || e->type == ButtonRelease ||
418         e->type == MotionNotify)
419         mouse_event(e, client);
420     else if (e->type == KeyPress || e->type == KeyRelease)
421         ;
422     */
423
424     /* dispatch the event to registered handlers */
425     dispatch_x(e, client);
426 }
427
428 static void event_handle_root(XEvent *e)
429 {
430     Atom msgtype;
431      
432     switch(e->type) {
433     case ClientMessage:
434         if (e->xclient.format != 32) break;
435
436         msgtype = e->xclient.message_type;
437         if (msgtype == prop_atoms.net_current_desktop) {
438             unsigned int d = e->xclient.data.l[0];
439             if (d < screen_num_desktops)
440                 screen_set_desktop(d);
441         } else if (msgtype == prop_atoms.net_number_of_desktops) {
442             unsigned int d = e->xclient.data.l[0];
443             if (d > 0)
444                 screen_set_num_desktops(d);
445         } else if (msgtype == prop_atoms.net_showing_desktop) {
446             screen_show_desktop(e->xclient.data.l[0] != 0);
447         }
448         break;
449     case PropertyNotify:
450         if (e->xproperty.atom == prop_atoms.net_desktop_names)
451             screen_update_desktop_names();
452         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
453             screen_update_layout();
454         break;
455     }
456 }
457
458 static void event_handle_client(Client *client, XEvent *e)
459 {
460     XEvent ce;
461     Atom msgtype;
462     int i=0;
463      
464     switch (e->type) {
465     case ButtonPress:
466     case ButtonRelease:
467         switch (frame_context(client, e->xbutton.window)) {
468         case Context_Maximize:
469             client->frame->max_press = (e->type == ButtonPress);
470             framerender_frame(client->frame);
471             break;
472         case Context_Close:
473             client->frame->close_press = (e->type == ButtonPress);
474             framerender_frame(client->frame);
475             break;
476         case Context_Iconify:
477             client->frame->iconify_press = (e->type == ButtonPress);
478             framerender_frame(client->frame);
479             break;
480         case Context_AllDesktops:
481             client->frame->desk_press = (e->type == ButtonPress);
482             framerender_frame(client->frame);
483             break; 
484         case Context_Shade:
485             client->frame->shade_press = (e->type == ButtonPress);
486             framerender_frame(client->frame);
487             break;
488         default:
489             /* nothing changes with clicks for any other contexts */
490             break;
491         }
492         break;
493     case FocusIn:
494         focus_set_client(client);
495     case FocusOut:
496 #ifdef DEBUG_FOCUS
497         g_message("Focus%s on client for %lx", (e->type==FocusIn?"In":"Out"),
498                   client->window);
499 #endif
500         /* focus state can affect the stacking layer */
501         client_calc_layer(client);
502         frame_adjust_focus(client->frame);
503         break;
504     case EnterNotify:
505         if (client_normal(client)) {
506             if (ob_state == State_Starting) {
507                 /* move it to the top of the focus order */
508                 guint desktop = client->desktop;
509                 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
510                 focus_order[desktop] = g_list_remove(focus_order[desktop],
511                                                      client);
512                 focus_order[desktop] = g_list_prepend(focus_order[desktop],
513                                                       client);
514             } else if (config_focus_follow) {
515 #ifdef DEBUG_FOCUS
516                 g_message("EnterNotify on %lx, focusing window",
517                           client->window);
518 #endif
519                 client_focus(client);
520             }
521         }
522         break;
523     case ConfigureRequest:
524         /* compress these */
525         while (XCheckTypedWindowEvent(ob_display, client->window,
526                                       ConfigureRequest, &ce)) {
527             ++i;
528             /* XXX if this causes bad things.. we can compress config req's
529                with the same mask. */
530             e->xconfigurerequest.value_mask |=
531                 ce.xconfigurerequest.value_mask;
532             if (ce.xconfigurerequest.value_mask & CWX)
533                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
534             if (ce.xconfigurerequest.value_mask & CWY)
535                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
536             if (ce.xconfigurerequest.value_mask & CWWidth)
537                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
538             if (ce.xconfigurerequest.value_mask & CWHeight)
539                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
540             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
541                 e->xconfigurerequest.border_width =
542                     ce.xconfigurerequest.border_width;
543             if (ce.xconfigurerequest.value_mask & CWStackMode)
544                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
545         }
546
547         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
548         if (client->iconic || client->shaded) return;
549
550         if (e->xconfigurerequest.value_mask & CWBorderWidth)
551             client->border_width = e->xconfigurerequest.border_width;
552
553         /* resize, then move, as specified in the EWMH section 7.7 */
554         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
555                                                CWX | CWY)) {
556             int x, y, w, h;
557             Corner corner;
558                
559             x = (e->xconfigurerequest.value_mask & CWX) ?
560                 e->xconfigurerequest.x : client->area.x;
561             y = (e->xconfigurerequest.value_mask & CWY) ?
562                 e->xconfigurerequest.y : client->area.y;
563             w = (e->xconfigurerequest.value_mask & CWWidth) ?
564                 e->xconfigurerequest.width : client->area.width;
565             h = (e->xconfigurerequest.value_mask & CWHeight) ?
566                 e->xconfigurerequest.height : client->area.height;
567                
568             switch (client->gravity) {
569             case NorthEastGravity:
570             case EastGravity:
571                 corner = Corner_TopRight;
572                 break;
573             case SouthWestGravity:
574             case SouthGravity:
575                 corner = Corner_BottomLeft;
576                 break;
577             case SouthEastGravity:
578                 corner = Corner_BottomRight;
579                 break;
580             default:     /* NorthWest, Static, etc */
581                 corner = Corner_TopLeft;
582             }
583
584             client_configure(client, corner, x, y, w, h, FALSE, FALSE);
585         }
586
587         if (e->xconfigurerequest.value_mask & CWStackMode) {
588             switch (e->xconfigurerequest.detail) {
589             case Below:
590             case BottomIf:
591                 stacking_lower(client);
592                 break;
593
594             case Above:
595             case TopIf:
596             default:
597                 stacking_raise(client);
598                 break;
599             }
600         }
601         break;
602     case UnmapNotify:
603         if (client->ignore_unmaps) {
604             client->ignore_unmaps--;
605             break;
606         }
607         client_unmanage(client);
608         break;
609     case DestroyNotify:
610         client_unmanage(client);
611         break;
612     case ReparentNotify:
613         /* this is when the client is first taken captive in the frame */
614         if (e->xreparent.parent == client->frame->plate) break;
615
616         /*
617           This event is quite rare and is usually handled in unmapHandler.
618           However, if the window is unmapped when the reparent event occurs,
619           the window manager never sees it because an unmap event is not sent
620           to an already unmapped window.
621         */
622
623         /* we don't want the reparent event, put it back on the stack for the
624            X server to deal with after we unmanage the window */
625         XPutBackEvent(ob_display, e);
626      
627         client_unmanage(client);
628         break;
629     case MapRequest:
630         g_message("MapRequest for 0x%lx", client->window);
631         if (!client->iconic) break; /* this normally doesn't happen, but if it
632                                        does, we don't want it! */
633         if (screen_showing_desktop)
634             screen_show_desktop(FALSE);
635         client_iconify(client, FALSE, TRUE);
636         if (!client->frame->visible)
637             /* if its not visible still, then don't mess with it */
638             break;
639         if (client->shaded)
640             client_shade(client, FALSE);
641         client_focus(client);
642         stacking_raise(client);
643         break;
644     case ClientMessage:
645         /* validate cuz we query stuff off the client here */
646         if (!client_validate(client)) break;
647   
648         if (e->xclient.format != 32) return;
649
650         msgtype = e->xclient.message_type;
651         if (msgtype == prop_atoms.wm_change_state) {
652             /* compress changes into a single change */
653             while (XCheckTypedWindowEvent(ob_display, e->type,
654                                           client->window, &ce)) {
655                 /* XXX: it would be nice to compress ALL messages of a
656                    type, not just messages in a row without other
657                    message types between. */
658                 if (ce.xclient.message_type != msgtype) {
659                     XPutBackEvent(ob_display, &ce);
660                     break;
661                 }
662                 e->xclient = ce.xclient;
663             }
664             client_set_wm_state(client, e->xclient.data.l[0]);
665         } else if (msgtype == prop_atoms.net_wm_desktop) {
666             /* compress changes into a single change */
667             while (XCheckTypedWindowEvent(ob_display, e->type,
668                                           client->window, &ce)) {
669                 /* XXX: it would be nice to compress ALL messages of a
670                    type, not just messages in a row without other
671                    message types between. */
672                 if (ce.xclient.message_type != msgtype) {
673                     XPutBackEvent(ob_display, &ce);
674                     break;
675                 }
676                 e->xclient = ce.xclient;
677             }
678             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
679                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
680                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
681                                    FALSE);
682         } else if (msgtype == prop_atoms.net_wm_state) {
683             /* can't compress these */
684             g_message("net_wm_state %s %ld %ld for 0x%lx",
685                       (e->xclient.data.l[0] == 0 ? "Remove" :
686                        e->xclient.data.l[0] == 1 ? "Add" :
687                        e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
688                       e->xclient.data.l[1], e->xclient.data.l[2],
689                       client->window);
690             client_set_state(client, e->xclient.data.l[0],
691                              e->xclient.data.l[1], e->xclient.data.l[2]);
692         } else if (msgtype == prop_atoms.net_close_window) {
693             g_message("net_close_window for 0x%lx", client->window);
694             client_close(client);
695         } else if (msgtype == prop_atoms.net_active_window) {
696             g_message("net_active_window for 0x%lx", client->window);
697             if (screen_showing_desktop)
698                 screen_show_desktop(FALSE);
699             if (client->iconic)
700                 client_iconify(client, FALSE, TRUE);
701             else if (!client->frame->visible)
702                 /* if its not visible for other reasons, then don't mess
703                    with it */
704                 break;
705             if (client->shaded)
706                 client_shade(client, FALSE);
707             client_focus(client);
708             stacking_raise(client);
709         } else if (msgtype == prop_atoms.net_wm_moveresize) {
710             g_message("net_wm_moveresize for 0x%lx", client->window);
711             if ((Atom)e->xclient.data.l[2] ==
712                 prop_atoms.net_wm_moveresize_size_topleft ||
713                 (Atom)e->xclient.data.l[2] ==
714                 prop_atoms.net_wm_moveresize_size_top ||
715                 (Atom)e->xclient.data.l[2] ==
716                 prop_atoms.net_wm_moveresize_size_topright ||
717                 (Atom)e->xclient.data.l[2] ==
718                 prop_atoms.net_wm_moveresize_size_right ||
719                 (Atom)e->xclient.data.l[2] ==
720                 prop_atoms.net_wm_moveresize_size_right ||
721                 (Atom)e->xclient.data.l[2] ==
722                 prop_atoms.net_wm_moveresize_size_bottomright ||
723                 (Atom)e->xclient.data.l[2] ==
724                 prop_atoms.net_wm_moveresize_size_bottom ||
725                 (Atom)e->xclient.data.l[2] ==
726                 prop_atoms.net_wm_moveresize_size_bottomleft ||
727                 (Atom)e->xclient.data.l[2] ==
728                 prop_atoms.net_wm_moveresize_size_left ||
729                 (Atom)e->xclient.data.l[2] ==
730                 prop_atoms.net_wm_moveresize_move ||
731                 (Atom)e->xclient.data.l[2] ==
732                 prop_atoms.net_wm_moveresize_size_keyboard ||
733                 (Atom)e->xclient.data.l[2] ==
734                 prop_atoms.net_wm_moveresize_move_keyboard) {
735
736                 moveresize_start(client, e->xclient.data.l[0],
737                                  e->xclient.data.l[1], e->xclient.data.l[3],
738                                  e->xclient.data.l[2]);
739             }
740         } else if (msgtype == prop_atoms.net_moveresize_window) {
741             int oldg = client->gravity;
742             int tmpg, x, y, w, h;
743
744             if (e->xclient.data.l[0] & 0xff)
745                 tmpg = e->xclient.data.l[0] & 0xff;
746             else
747                 tmpg = oldg;
748
749             if (e->xclient.data.l[0] & 1 << 8)
750                 x = e->xclient.data.l[1];
751             else
752                 x = client->area.x;
753             if (e->xclient.data.l[0] & 1 << 9)
754                 y = e->xclient.data.l[2];
755             else
756                 y = client->area.y;
757             if (e->xclient.data.l[0] & 1 << 10)
758                 w = e->xclient.data.l[3];
759             else
760                 w = client->area.y;
761             if (e->xclient.data.l[0] & 1 << 11)
762                 h = e->xclient.data.l[4];
763             else
764                 h = client->area.y;
765             client->gravity = tmpg;
766             client_configure(client, Corner_TopLeft, x, y, w, h, TRUE, TRUE);
767             client->gravity = oldg;
768         }
769         break;
770     case PropertyNotify:
771         /* validate cuz we query stuff off the client here */
772         if (!client_validate(client)) break;
773   
774         /* compress changes to a single property into a single change */
775         while (XCheckTypedWindowEvent(ob_display, e->type,
776                                       client->window, &ce)) {
777             /* XXX: it would be nice to compress ALL changes to a property,
778                not just changes in a row without other props between. */
779             if (ce.xproperty.atom != e->xproperty.atom) {
780                 XPutBackEvent(ob_display, &ce);
781                 break;
782             }
783         }
784
785         msgtype = e->xproperty.atom;
786         if (msgtype == XA_WM_NORMAL_HINTS) {
787             client_update_normal_hints(client);
788             /* normal hints can make a window non-resizable */
789             client_setup_decor_and_functions(client);
790         }
791         else if (msgtype == XA_WM_HINTS)
792             client_update_wmhints(client);
793         else if (msgtype == XA_WM_TRANSIENT_FOR) {
794             client_update_transient_for(client);
795             client_get_type(client);
796             /* type may have changed, so update the layer */
797             client_calc_layer(client);
798             client_setup_decor_and_functions(client);
799         }
800         else if (msgtype == prop_atoms.net_wm_name ||
801                  msgtype == prop_atoms.wm_name)
802             client_update_title(client);
803         else if (msgtype == prop_atoms.net_wm_icon_name ||
804                  msgtype == prop_atoms.wm_icon_name)
805             client_update_icon_title(client);
806         else if (msgtype == prop_atoms.wm_class)
807             client_update_class(client);
808         else if (msgtype == prop_atoms.wm_protocols) {
809             client_update_protocols(client);
810             client_setup_decor_and_functions(client);
811         }
812         else if (msgtype == prop_atoms.net_wm_strut)
813             client_update_strut(client);
814         else if (msgtype == prop_atoms.net_wm_icon)
815             client_update_icons(client);
816         else if (msgtype == prop_atoms.kwm_win_icon)
817             client_update_kwm_icon(client);
818     default:
819         ;
820 #ifdef SHAPE
821         if (extensions_shape && e->type == extensions_shape_event_basep) {
822             client->shaped = ((XShapeEvent*)e)->shaped;
823             frame_adjust_shape(client->frame);
824         }
825 #endif
826     }
827 }
828
829 static void event_handle_menu(Menu *menu, XEvent *e)
830 {
831     MenuEntry *entry;
832
833     g_message("EVENT %d", e->type);
834     switch (e->type) {
835     case ButtonPress:
836         if (e->xbutton.button == 3)
837             menu_hide(menu);
838         break;
839     case ButtonRelease:
840         if (!menu->shown) break;
841
842 /*        grab_pointer_window(FALSE, None, menu->frame);*/
843
844         entry = menu_find_entry(menu, e->xbutton.window);
845         if (entry) {
846             int junk;
847             Window wjunk;
848             guint ujunk, b, w, h;
849             XGetGeometry(ob_display, e->xbutton.window,
850                          &wjunk, &junk, &junk, &w, &h, &b, &ujunk);
851             if (e->xbutton.x >= (signed)-b &&
852                 e->xbutton.y >= (signed)-b &&
853                 e->xbutton.x < (signed)(w+b) &&
854                 e->xbutton.y < (signed)(h+b)) {
855                 menu_entry_fire(entry);
856             }
857         }
858         break;
859     case EnterNotify:
860     case LeaveNotify:
861         g_message("enter/leave");
862         entry = menu_find_entry(menu, e->xcrossing.window);
863         if (entry) {
864             entry->hilite = e->type == EnterNotify;
865             menu_entry_render(entry);
866         }
867         break;
868     }
869 }