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