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