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