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