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