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