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