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