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