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