]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/event.c
make session management optional
[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(Dock *s, XEvent *e);
42 static void event_handle_dockapp(DockApp *app, XEvent *e);
43 static void event_handle_client(Client *c, XEvent *e);
44 static void event_handle_menu(Menu *menu, Client *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, Client *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     Client *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 (menu) {
505         event_handle_menu(menu, client, e);
506         return;
507     } else if (client)
508         event_handle_client(client, e);
509     else if (dockapp)
510         event_handle_dockapp(dockapp, e);
511     else if (dock)
512         event_handle_dock(dock, e);
513     else if (window == ob_root)
514         event_handle_root(e);
515     else if (e->type == MapRequest)
516         client_manage(window);
517     else if (e->type == ConfigureRequest) {
518         /* unhandled configure requests must be used to configure the
519            window directly */
520         XWindowChanges xwc;
521                
522         xwc.x = e->xconfigurerequest.x;
523         xwc.y = e->xconfigurerequest.y;
524         xwc.width = e->xconfigurerequest.width;
525         xwc.height = e->xconfigurerequest.height;
526         xwc.border_width = e->xconfigurerequest.border_width;
527         xwc.sibling = e->xconfigurerequest.above;
528         xwc.stack_mode = e->xconfigurerequest.detail;
529        
530         /* we are not to be held responsible if someone sends us an
531            invalid request! */
532         xerror_set_ignore(TRUE);
533         XConfigureWindow(ob_display, window,
534                          e->xconfigurerequest.value_mask, &xwc);
535         xerror_set_ignore(FALSE);
536     }
537
538     if (moveresize_in_progress)
539         if (e->type == MotionNotify || e->type == ButtonRelease ||
540             e->type == ButtonPress ||
541             e->type == KeyPress || e->type == KeyRelease) {
542             moveresize_event(e);
543
544             return; /* no dispatch! */
545             
546         }
547
548     /* user input (action-bound) events */
549     /*
550     if (e->type == ButtonPress || e->type == ButtonRelease ||
551         e->type == MotionNotify)
552         mouse_event(e, client);
553     else if (e->type == KeyPress || e->type == KeyRelease)
554         ;
555     */
556
557     /* dispatch the event to registered handlers */
558     dispatch_x(e, client);
559 }
560
561 static void event_handle_root(XEvent *e)
562 {
563     Atom msgtype;
564      
565     switch(e->type) {
566     case ClientMessage:
567         if (e->xclient.format != 32) break;
568
569         msgtype = e->xclient.message_type;
570         if (msgtype == prop_atoms.net_current_desktop) {
571             unsigned int d = e->xclient.data.l[0];
572             if (d < screen_num_desktops)
573                 screen_set_desktop(d);
574         } else if (msgtype == prop_atoms.net_number_of_desktops) {
575             unsigned int d = e->xclient.data.l[0];
576             if (d > 0)
577                 screen_set_num_desktops(d);
578         } else if (msgtype == prop_atoms.net_showing_desktop) {
579             screen_show_desktop(e->xclient.data.l[0] != 0);
580         }
581         break;
582     case PropertyNotify:
583         if (e->xproperty.atom == prop_atoms.net_desktop_names)
584             screen_update_desktop_names();
585         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
586             screen_update_layout();
587         break;
588     case ConfigureNotify:
589 #ifdef XRANDR
590         XRRUpdateConfiguration(e);
591 #endif
592         if (e->xconfigure.width != screen_physical_size.width ||
593             e->xconfigure.height != screen_physical_size.height)
594             screen_resize(e->xconfigure.width, e->xconfigure.height);
595         break;
596     default:
597         ;
598 #ifdef VIDMODE
599         if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
600             g_message("VIDMODE EVENT");
601         }
602 #endif
603     }
604 }
605
606 static void event_handle_client(Client *client, XEvent *e)
607 {
608     XEvent ce;
609     Atom msgtype;
610     int i=0;
611      
612     switch (e->type) {
613     case ButtonPress:
614     case ButtonRelease:
615         /* Wheel buttons don't draw because they are an instant click, so it
616            is a waste of resources to go drawing it. */
617         if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
618             switch (frame_context(client, e->xbutton.window)) {
619             case Context_Maximize:
620                 client->frame->max_press = (e->type == ButtonPress);
621                 framerender_frame(client->frame);
622                 break;
623             case Context_Close:
624                 client->frame->close_press = (e->type == ButtonPress);
625                 framerender_frame(client->frame);
626                 break;
627             case Context_Iconify:
628                 client->frame->iconify_press = (e->type == ButtonPress);
629                 framerender_frame(client->frame);
630                 break;
631             case Context_AllDesktops:
632                 client->frame->desk_press = (e->type == ButtonPress);
633                 framerender_frame(client->frame);
634                 break; 
635             case Context_Shade:
636                 client->frame->shade_press = (e->type == ButtonPress);
637                 framerender_frame(client->frame);
638                 break;
639             default:
640                 /* nothing changes with clicks for any other contexts */
641                 break;
642             }
643         }
644         break;
645     case FocusIn:
646 #ifdef DEBUG_FOCUS
647         g_message("FocusIn on client for %lx", client->window);
648 #endif
649         if (client != focus_client) {
650             focus_set_client(client);
651             frame_adjust_focus(client->frame, TRUE);
652         }
653         break;
654     case FocusOut:
655 #ifdef DEBUG_FOCUS
656         g_message("FocusOut on client for %lx", client->window);
657 #endif
658         /* are we a fullscreen window or a transient of one? (checks layer)
659            if we are then we need to be iconified since we are losing focus
660          */
661         if (client->layer == Layer_Fullscreen && !client->iconic &&
662             !client_search_focus_tree_full(client))
663             /* iconify fullscreen windows when they and their transients
664                aren't focused */
665             client_iconify(client, TRUE, TRUE);
666         frame_adjust_focus(client->frame, FALSE);
667         break;
668     case EnterNotify:
669         if (client_normal(client)) {
670             if (ob_state == State_Starting) {
671                 /* move it to the top of the focus order */
672                 guint desktop = client->desktop;
673                 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
674                 focus_order[desktop] = g_list_remove(focus_order[desktop],
675                                                      client);
676                 focus_order[desktop] = g_list_prepend(focus_order[desktop],
677                                                       client);
678             } else if (config_focus_follow) {
679 #ifdef DEBUG_FOCUS
680                 g_message("EnterNotify on %lx, focusing window",
681                           client->window);
682 #endif
683                 client_focus(client);
684             }
685         }
686         break;
687     case ConfigureRequest:
688         /* compress these */
689         while (XCheckTypedWindowEvent(ob_display, client->window,
690                                       ConfigureRequest, &ce)) {
691             ++i;
692             /* XXX if this causes bad things.. we can compress config req's
693                with the same mask. */
694             e->xconfigurerequest.value_mask |=
695                 ce.xconfigurerequest.value_mask;
696             if (ce.xconfigurerequest.value_mask & CWX)
697                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
698             if (ce.xconfigurerequest.value_mask & CWY)
699                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
700             if (ce.xconfigurerequest.value_mask & CWWidth)
701                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
702             if (ce.xconfigurerequest.value_mask & CWHeight)
703                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
704             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
705                 e->xconfigurerequest.border_width =
706                     ce.xconfigurerequest.border_width;
707             if (ce.xconfigurerequest.value_mask & CWStackMode)
708                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
709         }
710
711         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
712         if (client->iconic || client->shaded) return;
713
714         if (e->xconfigurerequest.value_mask & CWBorderWidth)
715             client->border_width = e->xconfigurerequest.border_width;
716
717         /* resize, then move, as specified in the EWMH section 7.7 */
718         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
719                                                CWX | CWY)) {
720             int x, y, w, h;
721             Corner corner;
722                
723             x = (e->xconfigurerequest.value_mask & CWX) ?
724                 e->xconfigurerequest.x : client->area.x;
725             y = (e->xconfigurerequest.value_mask & CWY) ?
726                 e->xconfigurerequest.y : client->area.y;
727             w = (e->xconfigurerequest.value_mask & CWWidth) ?
728                 e->xconfigurerequest.width : client->area.width;
729             h = (e->xconfigurerequest.value_mask & CWHeight) ?
730                 e->xconfigurerequest.height : client->area.height;
731                
732             switch (client->gravity) {
733             case NorthEastGravity:
734             case EastGravity:
735                 corner = Corner_TopRight;
736                 break;
737             case SouthWestGravity:
738             case SouthGravity:
739                 corner = Corner_BottomLeft;
740                 break;
741             case SouthEastGravity:
742                 corner = Corner_BottomRight;
743                 break;
744             default:     /* NorthWest, Static, etc */
745                 corner = Corner_TopLeft;
746             }
747
748             client_configure(client, corner, x, y, w, h, FALSE, TRUE);
749         }
750
751         if (e->xconfigurerequest.value_mask & CWStackMode) {
752             switch (e->xconfigurerequest.detail) {
753             case Below:
754             case BottomIf:
755                 stacking_lower(CLIENT_AS_WINDOW(client));
756                 break;
757
758             case Above:
759             case TopIf:
760             default:
761                 stacking_raise(CLIENT_AS_WINDOW(client));
762                 break;
763             }
764         }
765         break;
766     case UnmapNotify:
767         if (client->ignore_unmaps) {
768             client->ignore_unmaps--;
769             break;
770         }
771         client_unmanage(client);
772         break;
773     case DestroyNotify:
774         client_unmanage(client);
775         break;
776     case ReparentNotify:
777         /* this is when the client is first taken captive in the frame */
778         if (e->xreparent.parent == client->frame->plate) break;
779
780         /*
781           This event is quite rare and is usually handled in unmapHandler.
782           However, if the window is unmapped when the reparent event occurs,
783           the window manager never sees it because an unmap event is not sent
784           to an already unmapped window.
785         */
786
787         /* we don't want the reparent event, put it back on the stack for the
788            X server to deal with after we unmanage the window */
789         XPutBackEvent(ob_display, e);
790      
791         client_unmanage(client);
792         break;
793     case MapRequest:
794         g_message("MapRequest for 0x%lx", client->window);
795         if (!client->iconic) break; /* this normally doesn't happen, but if it
796                                        does, we don't want it! */
797         if (screen_showing_desktop)
798             screen_show_desktop(FALSE);
799         client_iconify(client, FALSE, TRUE);
800         if (!client->frame->visible)
801             /* if its not visible still, then don't mess with it */
802             break;
803         if (client->shaded)
804             client_shade(client, FALSE);
805         client_focus(client);
806         stacking_raise(CLIENT_AS_WINDOW(client));
807         break;
808     case ClientMessage:
809         /* validate cuz we query stuff off the client here */
810         if (!client_validate(client)) break;
811   
812         if (e->xclient.format != 32) return;
813
814         msgtype = e->xclient.message_type;
815         if (msgtype == prop_atoms.wm_change_state) {
816             /* compress changes into a single change */
817             while (XCheckTypedWindowEvent(ob_display, e->type,
818                                           client->window, &ce)) {
819                 /* XXX: it would be nice to compress ALL messages of a
820                    type, not just messages in a row without other
821                    message types between. */
822                 if (ce.xclient.message_type != msgtype) {
823                     XPutBackEvent(ob_display, &ce);
824                     break;
825                 }
826                 e->xclient = ce.xclient;
827             }
828             client_set_wm_state(client, e->xclient.data.l[0]);
829         } else if (msgtype == prop_atoms.net_wm_desktop) {
830             /* compress changes into a single change */
831             while (XCheckTypedWindowEvent(ob_display, e->type,
832                                           client->window, &ce)) {
833                 /* XXX: it would be nice to compress ALL messages of a
834                    type, not just messages in a row without other
835                    message types between. */
836                 if (ce.xclient.message_type != msgtype) {
837                     XPutBackEvent(ob_display, &ce);
838                     break;
839                 }
840                 e->xclient = ce.xclient;
841             }
842             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
843                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
844                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
845                                    FALSE);
846         } else if (msgtype == prop_atoms.net_wm_state) {
847             /* can't compress these */
848             g_message("net_wm_state %s %ld %ld for 0x%lx",
849                       (e->xclient.data.l[0] == 0 ? "Remove" :
850                        e->xclient.data.l[0] == 1 ? "Add" :
851                        e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
852                       e->xclient.data.l[1], e->xclient.data.l[2],
853                       client->window);
854             client_set_state(client, e->xclient.data.l[0],
855                              e->xclient.data.l[1], e->xclient.data.l[2]);
856         } else if (msgtype == prop_atoms.net_close_window) {
857             g_message("net_close_window for 0x%lx", client->window);
858             client_close(client);
859         } else if (msgtype == prop_atoms.net_active_window) {
860             g_message("net_active_window for 0x%lx", client->window);
861             client_activate(client);
862         } else if (msgtype == prop_atoms.net_wm_moveresize) {
863             g_message("net_wm_moveresize for 0x%lx", client->window);
864             if ((Atom)e->xclient.data.l[2] ==
865                 prop_atoms.net_wm_moveresize_size_topleft ||
866                 (Atom)e->xclient.data.l[2] ==
867                 prop_atoms.net_wm_moveresize_size_top ||
868                 (Atom)e->xclient.data.l[2] ==
869                 prop_atoms.net_wm_moveresize_size_topright ||
870                 (Atom)e->xclient.data.l[2] ==
871                 prop_atoms.net_wm_moveresize_size_right ||
872                 (Atom)e->xclient.data.l[2] ==
873                 prop_atoms.net_wm_moveresize_size_right ||
874                 (Atom)e->xclient.data.l[2] ==
875                 prop_atoms.net_wm_moveresize_size_bottomright ||
876                 (Atom)e->xclient.data.l[2] ==
877                 prop_atoms.net_wm_moveresize_size_bottom ||
878                 (Atom)e->xclient.data.l[2] ==
879                 prop_atoms.net_wm_moveresize_size_bottomleft ||
880                 (Atom)e->xclient.data.l[2] ==
881                 prop_atoms.net_wm_moveresize_size_left ||
882                 (Atom)e->xclient.data.l[2] ==
883                 prop_atoms.net_wm_moveresize_move ||
884                 (Atom)e->xclient.data.l[2] ==
885                 prop_atoms.net_wm_moveresize_size_keyboard ||
886                 (Atom)e->xclient.data.l[2] ==
887                 prop_atoms.net_wm_moveresize_move_keyboard) {
888
889                 moveresize_start(client, e->xclient.data.l[0],
890                                  e->xclient.data.l[1], e->xclient.data.l[3],
891                                  e->xclient.data.l[2]);
892             }
893         } else if (msgtype == prop_atoms.net_moveresize_window) {
894             int oldg = client->gravity;
895             int tmpg, x, y, w, h;
896
897             if (e->xclient.data.l[0] & 0xff)
898                 tmpg = e->xclient.data.l[0] & 0xff;
899             else
900                 tmpg = oldg;
901
902             if (e->xclient.data.l[0] & 1 << 8)
903                 x = e->xclient.data.l[1];
904             else
905                 x = client->area.x;
906             if (e->xclient.data.l[0] & 1 << 9)
907                 y = e->xclient.data.l[2];
908             else
909                 y = client->area.y;
910             if (e->xclient.data.l[0] & 1 << 10)
911                 w = e->xclient.data.l[3];
912             else
913                 w = client->area.y;
914             if (e->xclient.data.l[0] & 1 << 11)
915                 h = e->xclient.data.l[4];
916             else
917                 h = client->area.y;
918             client->gravity = tmpg;
919             client_configure(client, Corner_TopLeft, x, y, w, h, TRUE, TRUE);
920             client->gravity = oldg;
921         }
922         break;
923     case PropertyNotify:
924         /* validate cuz we query stuff off the client here */
925         if (!client_validate(client)) break;
926   
927         /* compress changes to a single property into a single change */
928         while (XCheckTypedWindowEvent(ob_display, e->type,
929                                       client->window, &ce)) {
930             /* XXX: it would be nice to compress ALL changes to a property,
931                not just changes in a row without other props between. */
932             if (ce.xproperty.atom != e->xproperty.atom) {
933                 XPutBackEvent(ob_display, &ce);
934                 break;
935             }
936         }
937
938         msgtype = e->xproperty.atom;
939         if (msgtype == XA_WM_NORMAL_HINTS) {
940             client_update_normal_hints(client);
941             /* normal hints can make a window non-resizable */
942             client_setup_decor_and_functions(client);
943         }
944         else if (msgtype == XA_WM_HINTS)
945             client_update_wmhints(client);
946         else if (msgtype == XA_WM_TRANSIENT_FOR) {
947             client_update_transient_for(client);
948             client_get_type(client);
949             /* type may have changed, so update the layer */
950             client_calc_layer(client);
951             client_setup_decor_and_functions(client);
952         }
953         else if (msgtype == prop_atoms.net_wm_name ||
954                  msgtype == prop_atoms.wm_name ||
955                  msgtype == prop_atoms.net_wm_icon_name ||
956                  msgtype == prop_atoms.wm_icon_name)
957             client_update_title(client);
958         else if (msgtype == prop_atoms.wm_class)
959             client_update_class(client);
960         else if (msgtype == prop_atoms.wm_protocols) {
961             client_update_protocols(client);
962             client_setup_decor_and_functions(client);
963         }
964         else if (msgtype == prop_atoms.net_wm_strut)
965             client_update_strut(client);
966         else if (msgtype == prop_atoms.net_wm_icon ||
967                  msgtype == prop_atoms.kwm_win_icon)
968             client_update_icons(client);
969     default:
970         ;
971 #ifdef SHAPE
972         if (extensions_shape && e->type == extensions_shape_event_basep) {
973             client->shaped = ((XShapeEvent*)e)->shaped;
974             frame_adjust_shape(client->frame);
975         }
976 #endif
977     }
978 }
979
980 static void event_handle_menu(Menu *menu, Client *client, XEvent *e)
981 {
982     MenuEntry *entry;
983
984     g_message("EVENT %d", e->type);
985     switch (e->type) {
986     case ButtonPress:
987         g_message("BUTTON PRESS");
988         if (e->xbutton.button == 3)
989             menu_hide(menu);
990         else if (e->xbutton.button == 1) {
991             entry = menu_find_entry(menu, e->xbutton.window);
992             if (!entry)
993                 stacking_raise(MENU_AS_WINDOW(menu));
994         }
995         break;
996     case ButtonRelease:
997         g_message("BUTTON RELEASED");
998         if (!menu->shown) break;
999
1000 /*        grab_pointer_window(FALSE, None, menu->frame);*/
1001
1002         if (e->xbutton.button == 1) {
1003             entry = menu_find_entry(menu, e->xbutton.window);
1004             if (entry) {
1005                 int junk;
1006                 Window wjunk;
1007                 guint ujunk, b, w, h;
1008                 XGetGeometry(ob_display, e->xbutton.window,
1009                              &wjunk, &junk, &junk, &w, &h, &b, &ujunk);
1010                 if (e->xbutton.x >= (signed)-b &&
1011                     e->xbutton.y >= (signed)-b &&
1012                     e->xbutton.x < (signed)(w+b) &&
1013                     e->xbutton.y < (signed)(h+b)) {
1014                     menu_entry_fire(entry);
1015                 }
1016             }
1017         }
1018         
1019         break;
1020     case EnterNotify:
1021     case LeaveNotify:
1022         g_message("enter/leave");
1023         entry = menu_find_entry(menu, e->xcrossing.window);
1024         if (entry) {
1025             if (menu->mouseover)
1026                 menu->mouseover(entry, e->type == EnterNotify);
1027             else
1028                 menu_control_mouseover(entry, e->type == EnterNotify);
1029             
1030             menu_entry_render(entry);
1031         }
1032         break;
1033     }
1034 }
1035
1036 void event_add_fd_handler(event_fd_handler *h) {
1037   g_datalist_id_set_data(&fd_handler_list, h->fd, h);
1038   FD_SET(h->fd, &allset);
1039   max_fd = MAX(max_fd, h->fd);
1040 }
1041
1042 static void find_max_fd_foreach(GQuark n, gpointer data, gpointer max)
1043 {
1044   *((unsigned int *)max) = MAX(*((unsigned int *)max), n);
1045 }
1046
1047 static void find_max_fd()
1048
1049   int tmpmax = -1;
1050   g_datalist_foreach(&fd_handler_list, find_max_fd_foreach, (gpointer)&tmpmax);
1051   max_fd = MAX(x_fd, tmpmax);
1052 #ifdef USE_SM
1053   max_fd = MAX(ice_fd, tmpmax);
1054 #endif
1055 }
1056
1057 void event_remove_fd(int n)
1058 {
1059   FD_CLR(n, &allset);
1060   g_datalist_id_remove_data(&fd_handler_list, (GQuark)n);
1061   find_max_fd();
1062 }
1063
1064 static void fd_event_handle_foreach(GQuark n, gpointer data, gpointer user_data)
1065 {
1066     if (FD_ISSET( (int)n, &selset)) {
1067         event_fd_handler *h = (event_fd_handler *)data;
1068         g_assert(h->fd == (int)n);
1069         h->handler(h->fd, h->data);
1070     }
1071 }
1072
1073 static void fd_event_handle()
1074 {
1075     g_datalist_foreach(&fd_handler_list, fd_event_handle_foreach, NULL);
1076 }
1077
1078 static void event_handle_dock(Dock *s, XEvent *e)
1079 {
1080     switch (e->type) {
1081     case ButtonPress:
1082         stacking_raise(DOCK_AS_WINDOW(s));
1083         break;
1084     case EnterNotify:
1085         dock_hide(FALSE);
1086         break;
1087     case LeaveNotify:
1088         dock_hide(TRUE);
1089         break;
1090     }
1091 }
1092
1093 static void event_handle_dockapp(DockApp *app, XEvent *e)
1094 {
1095     switch (e->type) {
1096     case MotionNotify:
1097         dock_app_drag(app, &e->xmotion);
1098         break;
1099     case UnmapNotify:
1100         if (app->ignore_unmaps) {
1101             app->ignore_unmaps--;
1102             break;
1103         }
1104         dock_remove(app, TRUE);
1105         break;
1106     case DestroyNotify:
1107         dock_remove(app, FALSE);
1108         break;
1109     case ReparentNotify:
1110         dock_remove(app, FALSE);
1111         break;
1112     case ConfigureNotify:
1113         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1114         break;
1115     }
1116 }