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