]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/event.c
dont send menu events to plugins
[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         return;
333     } else if (client)
334         event_handle_client(client, e);
335     else if (window == ob_root)
336         event_handle_root(e);
337     else if (e->type == MapRequest)
338         client_manage(window);
339     else if (e->type == ConfigureRequest) {
340         /* unhandled configure requests must be used to configure the
341            window directly */
342         XWindowChanges xwc;
343                
344         xwc.x = e->xconfigurerequest.x;
345         xwc.y = e->xconfigurerequest.y;
346         xwc.width = e->xconfigurerequest.width;
347         xwc.height = e->xconfigurerequest.height;
348         xwc.border_width = e->xconfigurerequest.border_width;
349         xwc.sibling = e->xconfigurerequest.above;
350         xwc.stack_mode = e->xconfigurerequest.detail;
351        
352         /* we are not to be held responsible if someone sends us an
353            invalid request! */
354         xerror_set_ignore(TRUE);
355         XConfigureWindow(ob_display, window,
356                          e->xconfigurerequest.value_mask, &xwc);
357         xerror_set_ignore(FALSE);
358     }
359
360     /* user input (action-bound) events */
361     /*
362     if (e->type == ButtonPress || e->type == ButtonRelease ||
363         e->type == MotionNotify)
364         mouse_event(e, client);
365     else if (e->type == KeyPress || e->type == KeyRelease)
366         ;
367     */
368
369     /* dispatch the event to registered handlers */
370     dispatch_x(e, client);
371 }
372
373 static void event_handle_root(XEvent *e)
374 {
375     Atom msgtype;
376      
377     switch(e->type) {
378     case ClientMessage:
379         if (e->xclient.format != 32) break;
380
381         msgtype = e->xclient.message_type;
382         if (msgtype == prop_atoms.net_current_desktop) {
383             unsigned int d = e->xclient.data.l[0];
384             if (d < screen_num_desktops)
385                 screen_set_desktop(d);
386         } else if (msgtype == prop_atoms.net_number_of_desktops) {
387             unsigned int d = e->xclient.data.l[0];
388             if (d > 0)
389                 screen_set_num_desktops(d);
390         } else if (msgtype == prop_atoms.net_showing_desktop) {
391             screen_show_desktop(e->xclient.data.l[0] != 0);
392         }
393         break;
394     case PropertyNotify:
395         if (e->xproperty.atom == prop_atoms.net_desktop_names)
396             screen_update_desktop_names();
397         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
398             screen_update_layout();
399         break;
400     }
401 }
402
403 static void event_handle_client(Client *client, XEvent *e)
404 {
405     XEvent ce;
406     Atom msgtype;
407     int i=0;
408      
409     switch (e->type) {
410     case ButtonPress:
411     case ButtonRelease:
412         switch (frame_context(client, e->xbutton.window)) {
413         case Context_Maximize:
414             client->frame->max_press = (e->type == ButtonPress);
415             framerender_frame(client->frame);
416             break;
417         case Context_Close:
418             client->frame->close_press = (e->type == ButtonPress);
419             framerender_frame(client->frame);
420             break;
421         case Context_Iconify:
422             client->frame->iconify_press = (e->type == ButtonPress);
423             framerender_frame(client->frame);
424             break;
425         case Context_AllDesktops:
426             client->frame->desk_press = (e->type == ButtonPress);
427             framerender_frame(client->frame);
428             break; 
429         case Context_Shade:
430             client->frame->shade_press = (e->type == ButtonPress);
431             framerender_frame(client->frame);
432             break;
433         default:
434             /* nothing changes with clicks for any other contexts */
435             break;
436         }
437         break;
438     case FocusIn:
439         focus_set_client(client);
440     case FocusOut:
441 #ifdef DEBUG_FOCUS
442         g_message("Focus%s on client for %lx", (e->type==FocusIn?"In":"Out"),
443                   client->window);
444 #endif
445         /* focus state can affect the stacking layer */
446         client_calc_layer(client);
447         frame_adjust_focus(client->frame);
448         break;
449     case EnterNotify:
450         if (client_normal(client)) {
451             if (ob_state == State_Starting) {
452                 /* move it to the top of the focus order */
453                 guint desktop = client->desktop;
454                 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
455                 focus_order[desktop] = g_list_remove(focus_order[desktop],
456                                                      client);
457                 focus_order[desktop] = g_list_prepend(focus_order[desktop],
458                                                       client);
459             } else if (config_focus_follow) {
460 #ifdef DEBUG_FOCUS
461                 g_message("EnterNotify on %lx, focusing window",
462                           client->window);
463 #endif
464                 client_focus(client);
465             }
466         }
467         break;
468     case ConfigureRequest:
469         /* compress these */
470         while (XCheckTypedWindowEvent(ob_display, client->window,
471                                       ConfigureRequest, &ce)) {
472             ++i;
473             /* XXX if this causes bad things.. we can compress config req's
474                with the same mask. */
475             e->xconfigurerequest.value_mask |=
476                 ce.xconfigurerequest.value_mask;
477             if (ce.xconfigurerequest.value_mask & CWX)
478                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
479             if (ce.xconfigurerequest.value_mask & CWY)
480                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
481             if (ce.xconfigurerequest.value_mask & CWWidth)
482                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
483             if (ce.xconfigurerequest.value_mask & CWHeight)
484                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
485             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
486                 e->xconfigurerequest.border_width =
487                     ce.xconfigurerequest.border_width;
488             if (ce.xconfigurerequest.value_mask & CWStackMode)
489                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
490         }
491
492         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
493         if (client->iconic || client->shaded) return;
494
495         if (e->xconfigurerequest.value_mask & CWBorderWidth)
496             client->border_width = e->xconfigurerequest.border_width;
497
498         /* resize, then move, as specified in the EWMH section 7.7 */
499         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
500                                                CWX | CWY)) {
501             int x, y, w, h;
502             Corner corner;
503                
504             x = (e->xconfigurerequest.value_mask & CWX) ?
505                 e->xconfigurerequest.x : client->area.x;
506             y = (e->xconfigurerequest.value_mask & CWY) ?
507                 e->xconfigurerequest.y : client->area.y;
508             w = (e->xconfigurerequest.value_mask & CWWidth) ?
509                 e->xconfigurerequest.width : client->area.width;
510             h = (e->xconfigurerequest.value_mask & CWHeight) ?
511                 e->xconfigurerequest.height : client->area.height;
512                
513             switch (client->gravity) {
514             case NorthEastGravity:
515             case EastGravity:
516                 corner = Corner_TopRight;
517                 break;
518             case SouthWestGravity:
519             case SouthGravity:
520                 corner = Corner_BottomLeft;
521                 break;
522             case SouthEastGravity:
523                 corner = Corner_BottomRight;
524                 break;
525             default:     /* NorthWest, Static, etc */
526                 corner = Corner_TopLeft;
527             }
528
529             client_configure(client, corner, x, y, w, h, FALSE, FALSE);
530         }
531
532         if (e->xconfigurerequest.value_mask & CWStackMode) {
533             switch (e->xconfigurerequest.detail) {
534             case Below:
535             case BottomIf:
536                 stacking_lower(client);
537                 break;
538
539             case Above:
540             case TopIf:
541             default:
542                 stacking_raise(client);
543                 break;
544             }
545         }
546         break;
547     case UnmapNotify:
548         if (client->ignore_unmaps) {
549             client->ignore_unmaps--;
550             break;
551         }
552         client_unmanage(client);
553         break;
554     case DestroyNotify:
555         client_unmanage(client);
556         break;
557     case ReparentNotify:
558         /* this is when the client is first taken captive in the frame */
559         if (e->xreparent.parent == client->frame->plate) break;
560
561         /*
562           This event is quite rare and is usually handled in unmapHandler.
563           However, if the window is unmapped when the reparent event occurs,
564           the window manager never sees it because an unmap event is not sent
565           to an already unmapped window.
566         */
567
568         /* we don't want the reparent event, put it back on the stack for the
569            X server to deal with after we unmanage the window */
570         XPutBackEvent(ob_display, e);
571      
572         client_unmanage(client);
573         break;
574     case MapRequest:
575         g_message("MapRequest for 0x%lx", client->window);
576         if (!client->iconic) break; /* this normally doesn't happen, but if it
577                                        does, we don't want it! */
578         if (screen_showing_desktop)
579             screen_show_desktop(FALSE);
580         client_iconify(client, FALSE, TRUE);
581         if (!client->frame->visible)
582             /* if its not visible still, then don't mess with it */
583             break;
584         if (client->shaded)
585             client_shade(client, FALSE);
586         client_focus(client);
587         stacking_raise(client);
588         break;
589     case ClientMessage:
590         /* validate cuz we query stuff off the client here */
591         if (!client_validate(client)) break;
592   
593         if (e->xclient.format != 32) return;
594
595         msgtype = e->xclient.message_type;
596         if (msgtype == prop_atoms.wm_change_state) {
597             /* compress changes into a single change */
598             while (XCheckTypedWindowEvent(ob_display, e->type,
599                                           client->window, &ce)) {
600                 /* XXX: it would be nice to compress ALL messages of a
601                    type, not just messages in a row without other
602                    message types between. */
603                 if (ce.xclient.message_type != msgtype) {
604                     XPutBackEvent(ob_display, &ce);
605                     break;
606                 }
607                 e->xclient = ce.xclient;
608             }
609             client_set_wm_state(client, e->xclient.data.l[0]);
610         } else if (msgtype == prop_atoms.net_wm_desktop) {
611             /* compress changes into a single change */
612             while (XCheckTypedWindowEvent(ob_display, e->type,
613                                           client->window, &ce)) {
614                 /* XXX: it would be nice to compress ALL messages of a
615                    type, not just messages in a row without other
616                    message types between. */
617                 if (ce.xclient.message_type != msgtype) {
618                     XPutBackEvent(ob_display, &ce);
619                     break;
620                 }
621                 e->xclient = ce.xclient;
622             }
623             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
624                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
625                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
626                                    FALSE);
627         } else if (msgtype == prop_atoms.net_wm_state) {
628             /* can't compress these */
629             g_message("net_wm_state %s %ld %ld for 0x%lx",
630                       (e->xclient.data.l[0] == 0 ? "Remove" :
631                        e->xclient.data.l[0] == 1 ? "Add" :
632                        e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
633                       e->xclient.data.l[1], e->xclient.data.l[2],
634                       client->window);
635             client_set_state(client, e->xclient.data.l[0],
636                              e->xclient.data.l[1], e->xclient.data.l[2]);
637         } else if (msgtype == prop_atoms.net_close_window) {
638             g_message("net_close_window for 0x%lx", client->window);
639             client_close(client);
640         } else if (msgtype == prop_atoms.net_active_window) {
641             g_message("net_active_window for 0x%lx", client->window);
642             if (screen_showing_desktop)
643                 screen_show_desktop(FALSE);
644             if (client->iconic)
645                 client_iconify(client, FALSE, TRUE);
646             else if (!client->frame->visible)
647                 /* if its not visible for other reasons, then don't mess
648                    with it */
649                 break;
650             if (client->shaded)
651                 client_shade(client, FALSE);
652             client_focus(client);
653             stacking_raise(client);
654         }
655         break;
656     case PropertyNotify:
657         /* validate cuz we query stuff off the client here */
658         if (!client_validate(client)) break;
659   
660         /* compress changes to a single property into a single change */
661         while (XCheckTypedWindowEvent(ob_display, e->type,
662                                       client->window, &ce)) {
663             /* XXX: it would be nice to compress ALL changes to a property,
664                not just changes in a row without other props between. */
665             if (ce.xproperty.atom != e->xproperty.atom) {
666                 XPutBackEvent(ob_display, &ce);
667                 break;
668             }
669         }
670
671         msgtype = e->xproperty.atom;
672         if (msgtype == XA_WM_NORMAL_HINTS) {
673             client_update_normal_hints(client);
674             /* normal hints can make a window non-resizable */
675             client_setup_decor_and_functions(client);
676         }
677         else if (msgtype == XA_WM_HINTS)
678             client_update_wmhints(client);
679         else if (msgtype == XA_WM_TRANSIENT_FOR) {
680             client_update_transient_for(client);
681             client_get_type(client);
682             /* type may have changed, so update the layer */
683             client_calc_layer(client);
684             client_setup_decor_and_functions(client);
685         }
686         else if (msgtype == prop_atoms.net_wm_name ||
687                  msgtype == prop_atoms.wm_name)
688             client_update_title(client);
689         else if (msgtype == prop_atoms.net_wm_icon_name ||
690                  msgtype == prop_atoms.wm_icon_name)
691             client_update_icon_title(client);
692         else if (msgtype == prop_atoms.wm_class)
693             client_update_class(client);
694         else if (msgtype == prop_atoms.wm_protocols) {
695             client_update_protocols(client);
696             client_setup_decor_and_functions(client);
697         }
698         else if (msgtype == prop_atoms.net_wm_strut)
699             client_update_strut(client);
700         else if (msgtype == prop_atoms.net_wm_icon)
701             client_update_icons(client);
702         else if (msgtype == prop_atoms.kwm_win_icon)
703             client_update_kwm_icon(client);
704     default:
705         ;
706 #ifdef SHAPE
707         if (extensions_shape && e->type == extensions_shape_event_basep) {
708             client->shaped = ((XShapeEvent*)e)->shaped;
709             frame_adjust_shape(client->frame);
710         }
711 #endif
712     }
713 }
714
715 static void event_handle_menu(Menu *menu, XEvent *e)
716 {
717     MenuEntry *entry;
718
719     g_message("EVENT %d", e->type);
720     switch (e->type) {
721     case ButtonPress:
722         if (e->xbutton.button == 3)
723             menu_hide(menu);
724         break;
725     case ButtonRelease:
726         if (!menu->shown) break;
727
728 /*        grab_pointer_window(FALSE, None, menu->frame);*/
729
730         entry = menu_find_entry(menu, e->xbutton.window);
731         if (entry) {
732             int junk;
733             Window wjunk;
734             guint ujunk, b, w, h;
735             XGetGeometry(ob_display, e->xbutton.window,
736                          &wjunk, &junk, &junk, &w, &h, &b, &ujunk);
737             if (e->xbutton.x >= (signed)-b &&
738                 e->xbutton.y >= (signed)-b &&
739                 e->xbutton.x < (signed)(w+b) &&
740                 e->xbutton.y < (signed)(h+b)) {
741                 menu_entry_fire(entry);
742             }
743         }
744         break;
745     case EnterNotify:
746     case LeaveNotify:
747         g_message("enter/leave");
748         entry = menu_find_entry(menu, e->xcrossing.window);
749         if (entry) {
750             entry->hilite = e->type == EnterNotify;
751             menu_entry_render(entry);
752         }
753         break;
754     }
755 }