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