]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/event.c
no \n in g_messages
[mikachu/openbox.git] / openbox / event.c
1 #include "openbox.h"
2 #include "client.h"
3 #include "xerror.h"
4 #include "prop.h"
5 #include "screen.h"
6 #include "frame.h"
7 #include "engine.h"
8 #include "focus.h"
9 #include "stacking.h"
10 #include "extensions.h"
11 #include "timer.h"
12 #include "engine.h"
13 #include "dispatch.h"
14
15 #include <X11/Xlib.h>
16 #include <X11/keysym.h>
17 #include <X11/Xatom.h>
18
19 static void event_process(XEvent *e);
20 static void event_handle_root(XEvent *e);
21 static void event_handle_client(Client *c, XEvent *e);
22
23 Time event_lasttime = 0;
24
25 /*! The value of the mask for the NumLock modifier */
26 unsigned int NumLockMask;
27 /*! The value of the mask for the ScrollLock modifier */
28 unsigned int ScrollLockMask;
29 /*! The key codes for the modifier keys */
30 static XModifierKeymap *modmap;
31 /*! Table of the constant modifier masks */
32 static const int mask_table[] = {
33     ShiftMask, LockMask, ControlMask, Mod1Mask,
34     Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
35 };
36 static int mask_table_size;
37
38 void event_startup()
39 {
40     mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
41      
42     /* get lock masks that are defined by the display (not constant) */
43     modmap = XGetModifierMapping(ob_display);
44     g_assert(modmap);
45     if (modmap && modmap->max_keypermod > 0) {
46         size_t cnt;
47         const size_t size = mask_table_size * modmap->max_keypermod;
48         /* get the values of the keyboard lock modifiers
49            Note: Caps lock is not retrieved the same way as Scroll and Num
50            lock since it doesn't need to be. */
51         const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
52         const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
53                                                      XK_Scroll_Lock);
54           
55         for (cnt = 0; cnt < size; ++cnt) {
56             if (! modmap->modifiermap[cnt]) continue;
57                
58             if (num_lock == modmap->modifiermap[cnt])
59                 NumLockMask = mask_table[cnt / modmap->max_keypermod];
60             if (scroll_lock == modmap->modifiermap[cnt])
61                 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
62         }
63     }
64 }
65
66 void event_shutdown()
67 {
68     XFreeModifiermap(modmap);
69 }
70
71 void event_loop()
72 {
73     fd_set selset;
74     XEvent e;
75     int x_fd;
76     struct timeval *wait;
77
78     while (TRUE) {
79         /*
80           There are slightly different event retrieval semantics here for
81           local (or high bandwidth) versus remote (or low bandwidth)
82           connections to the display/Xserver.
83         */
84         if (ob_remote) {
85             if (!XPending(ob_display))
86                 break;
87         } else {
88             /*
89               This XSync allows for far more compression of events, which
90               makes things like Motion events perform far far better. Since
91               it also means network traffic for every event instead of every
92               X events (where X is the number retrieved at a time), it
93               probably should not be used for setups where Openbox is
94               running on a remote/low bandwidth display/Xserver.
95             */
96             XSync(ob_display, FALSE);
97             if (!XEventsQueued(ob_display, QueuedAlready))
98                 break;
99         }
100         XNextEvent(ob_display, &e);
101
102         event_process(&e);
103     }
104      
105     timer_dispatch((GTimeVal**)&wait);
106     x_fd = ConnectionNumber(ob_display);
107     FD_ZERO(&selset);
108     FD_SET(x_fd, &selset);
109     select(x_fd + 1, &selset, NULL, NULL, wait);
110 }
111
112 void event_process(XEvent *e)
113 {
114     XEvent ce;
115     KeyCode *kp;
116     Window window;
117     int i, k;
118     Client *client;
119
120     /* pick a window */
121     switch (e->type) {
122     case UnmapNotify:
123         window = e->xunmap.window;
124         break;
125     case DestroyNotify:
126         window = e->xdestroywindow.window;
127         break;
128     case ConfigureRequest:
129         window = e->xconfigurerequest.window;
130         break;
131     default:
132         /* XKB events */
133         if (e->type == extensions_xkb_event_basep) {
134             switch (((XkbAnyEvent*)&e)->xkb_type) {
135             case XkbBellNotify:
136                 window = ((XkbBellNotifyEvent*)&e)->window;
137             default:
138                 window = None;
139             }
140         } else
141             window = e->xany.window;
142     }
143      
144     /* grab the lasttime and hack up the state */
145     switch (e->type) {
146     case ButtonPress:
147     case ButtonRelease:
148         event_lasttime = e->xbutton.time;
149         e->xbutton.state &= ~(LockMask | NumLockMask | ScrollLockMask);
150         /* kill off the Button1Mask etc, only want the modifiers */
151         e->xbutton.state &= (ControlMask | ShiftMask | Mod1Mask |
152                              Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
153         break;
154     case KeyPress:
155         event_lasttime = e->xkey.time;
156         e->xkey.state &= ~(LockMask | NumLockMask | ScrollLockMask);
157         /* kill off the Button1Mask etc, only want the modifiers */
158         e->xkey.state &= (ControlMask | ShiftMask | Mod1Mask |
159                           Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
160         /* add to the state the mask of the modifier being pressed, if it is
161            a modifier key being pressed (this is a little ugly..) */
162 /* I'm commenting this out cuz i don't want "C-Control_L" being returned. */
163 /*      kp = modmap->modifiermap;*/
164 /*      for (i = 0; i < mask_table_size; ++i) {*/
165 /*          for (k = 0; k < modmap->max_keypermod; ++k) {*/
166 /*              if (*kp == e->xkey.keycode) {*/ /* found the keycode */
167                     /* add the mask for it */
168 /*                  e->xkey.state |= mask_table[i];*/
169                     /* cause the first loop to break; */
170 /*                  i = mask_table_size;*/
171 /*                  break;*/ /* get outta here! */
172 /*              }*/
173 /*              ++kp;*/
174 /*          }*/
175 /*      }*/
176
177         break;
178     case KeyRelease:
179         event_lasttime = e->xkey.time;
180         e->xkey.state &= ~(LockMask | NumLockMask | ScrollLockMask);
181         /* kill off the Button1Mask etc, only want the modifiers */
182         e->xkey.state &= (ControlMask | ShiftMask | Mod1Mask |
183                           Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
184         /* remove from the state the mask of the modifier being released, if
185            it is a modifier key being released (this is a little ugly..) */
186         kp = modmap->modifiermap;
187         for (i = 0; i < mask_table_size; ++i) {
188             for (k = 0; k < modmap->max_keypermod; ++k) {
189                 if (*kp == e->xkey.keycode) { /* found the keycode */
190                     /* remove the mask for it */
191                     e->xkey.state &= ~mask_table[i];
192                     /* cause the first loop to break; */
193                     i = mask_table_size;
194                     break; /* get outta here! */
195                 }
196                 ++kp;
197             }
198         }
199         break;
200     case MotionNotify:
201         event_lasttime = e->xmotion.time;
202         e->xmotion.state &= ~(LockMask | NumLockMask | ScrollLockMask);
203         /* kill off the Button1Mask etc, only want the modifiers */
204         e->xmotion.state &= (ControlMask | ShiftMask | Mod1Mask |
205                              Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
206         /* compress events */
207         while (XCheckTypedWindowEvent(ob_display, window, e->type, &ce)) {
208             e->xmotion.x_root = ce.xmotion.x_root;
209             e->xmotion.y_root = ce.xmotion.y_root;
210         }
211         break;
212     case PropertyNotify:
213         event_lasttime = e->xproperty.time;
214         break;
215     case FocusIn:
216     case FocusOut:
217         if (e->xfocus.mode == NotifyGrab)
218             /*|| e.xfocus.mode == NotifyUngrab ||*/
219                
220             /* From Metacity, from WindowMaker, ignore all funky pointer
221                root events. Its commented out cuz I don't think we need this
222                at all. If problems arise we can look into it */
223             /*e.xfocus.detail > NotifyNonlinearVirtual) */
224             return; /* skip me! */
225         if (e->type == FocusOut) {
226             /* FocusOut events just make us look for FocusIn events. They
227                are mostly ignored otherwise. */
228             XEvent fi;
229             if (XCheckTypedEvent(ob_display, FocusIn, &fi)) {
230                 event_process(&fi);
231                 /* dont unfocus the window we just focused! */
232                 if (fi.xfocus.window == e->xfocus.window)
233                     return;
234             }
235         }
236         break;
237     case EnterNotify:
238     case LeaveNotify:
239         event_lasttime = e->xcrossing.time;
240         /* XXX this caused problems before... but i don't remember why. hah.
241            so back it is. if problems arise again, then try filtering on the
242            detail instead of the mode. */
243         if (e->xcrossing.mode != NotifyNormal) return;
244         break;
245     }
246
247     client = g_hash_table_lookup(client_map, (gpointer)window);
248
249     /* deal with it in the kernel */
250     if (client) {
251         event_handle_client(client, e);
252     } else if (window == ob_root)
253         event_handle_root(e);
254     else if (e->type == ConfigureRequest) {
255         /* unhandled configure requests must be used to configure the
256            window directly */
257         XWindowChanges xwc;
258                
259         xwc.x = e->xconfigurerequest.x;
260         xwc.y = e->xconfigurerequest.y;
261         xwc.width = e->xconfigurerequest.width;
262         xwc.height = e->xconfigurerequest.height;
263         xwc.border_width = e->xconfigurerequest.border_width;
264         xwc.sibling = e->xconfigurerequest.above;
265         xwc.stack_mode = e->xconfigurerequest.detail;
266        
267         g_message("Proxying configure event for 0x%lx", window);
268        
269         /* we are not to be held responsible if someone sends us an
270            invalid request! */
271         xerror_set_ignore(TRUE);
272         XConfigureWindow(ob_display, window,
273                          e->xconfigurerequest.value_mask, &xwc);
274         xerror_set_ignore(FALSE);
275     }
276
277     /* dispatch the event to registered handlers */
278     dispatch_x(e, client);
279 }
280
281 static void event_handle_root(XEvent *e)
282 {
283     Atom msgtype;
284      
285     switch(e->type) {
286     case MapRequest:
287         g_message("MapRequest on root");
288         client_manage(e->xmap.window);
289         break;
290     case ClientMessage:
291         if (e->xclient.format != 32) break;
292
293         msgtype = e->xclient.message_type;
294         if (msgtype == prop_atoms.net_current_desktop) {
295             unsigned int d = e->xclient.data.l[0];
296             if (d <= screen_num_desktops)
297                 screen_set_desktop(d);
298         } else if (msgtype == prop_atoms.net_number_of_desktops) {
299             unsigned int d = e->xclient.data.l[0];
300             if (d > 0)
301                 screen_set_num_desktops(d);
302         } else if (msgtype == prop_atoms.net_showing_desktop) {
303             screen_show_desktop(e->xclient.data.l[0] != 0);
304         }
305         break;
306     case PropertyNotify:
307         if (e->xproperty.atom == prop_atoms.net_desktop_names)
308             screen_update_desktop_names();
309         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
310             screen_update_layout();
311         break;
312     }
313 }
314
315 static void event_handle_client(Client *client, XEvent *e)
316 {
317     XEvent ce;
318     Atom msgtype;
319      
320     switch (e->type) {
321     case FocusIn:
322         client->focused = TRUE;
323         engine_frame_adjust_focus(client->frame);
324
325         /* focus state can affect the stacking layer */
326         client_calc_layer(client);
327
328         if (focus_client != client)
329             focus_set_client(client);
330         break;
331     case FocusOut:
332         client->focused = FALSE;
333         engine_frame_adjust_focus(client->frame);
334
335         /* focus state can affect the stacking layer */
336         client_calc_layer(client);
337
338         if (focus_client == client)
339             focus_set_client(NULL);
340         break;
341     case ConfigureRequest:
342         g_message("ConfigureRequest for window %lx", client->window);
343         /* compress these */
344         while (XCheckTypedWindowEvent(ob_display, client->window,
345                                       ConfigureRequest, &ce)) {
346             /* XXX if this causes bad things.. we can compress config req's
347                with the same mask. */
348             e->xconfigurerequest.value_mask |=
349                 ce.xconfigurerequest.value_mask;
350             if (ce.xconfigurerequest.value_mask & CWX)
351                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
352             if (ce.xconfigurerequest.value_mask & CWY)
353                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
354             if (ce.xconfigurerequest.value_mask & CWWidth)
355                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
356             if (ce.xconfigurerequest.value_mask & CWHeight)
357                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
358             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
359                 e->xconfigurerequest.border_width =
360                     ce.xconfigurerequest.border_width;
361             if (ce.xconfigurerequest.value_mask & CWStackMode)
362                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
363         }
364
365         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
366         if (client->iconic || client->shaded) return;
367
368         if (e->xconfigurerequest.value_mask & CWBorderWidth)
369             client->border_width = e->xconfigurerequest.border_width;
370
371         /* resize, then move, as specified in the EWMH section 7.7 */
372         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
373                                                CWX | CWY)) {
374             int x, y, w, h;
375             Corner corner;
376                
377             x = (e->xconfigurerequest.value_mask & CWX) ?
378                 e->xconfigurerequest.x : client->area.x;
379             y = (e->xconfigurerequest.value_mask & CWY) ?
380                 e->xconfigurerequest.y : client->area.y;
381             w = (e->xconfigurerequest.value_mask & CWWidth) ?
382                 e->xconfigurerequest.width : client->area.width;
383             h = (e->xconfigurerequest.value_mask & CWHeight) ?
384                 e->xconfigurerequest.height : client->area.height;
385                
386             switch (client->gravity) {
387             case NorthEastGravity:
388             case EastGravity:
389                 corner = Corner_TopRight;
390                 break;
391             case SouthWestGravity:
392             case SouthGravity:
393                 corner = Corner_BottomLeft;
394                 break;
395             case SouthEastGravity:
396                 corner = Corner_BottomRight;
397                 break;
398             default:     /* NorthWest, Static, etc */
399                 corner = Corner_TopLeft;
400             }
401
402             client_configure(client, corner, x, y, w, h, FALSE, FALSE);
403         }
404
405         if (e->xconfigurerequest.value_mask & CWStackMode) {
406             switch (e->xconfigurerequest.detail) {
407             case Below:
408             case BottomIf:
409                 stacking_lower(client);
410                 break;
411
412             case Above:
413             case TopIf:
414             default:
415                 stacking_raise(client);
416                 break;
417             }
418         }
419         break;
420     case UnmapNotify:
421         if (client->ignore_unmaps) {
422             client->ignore_unmaps--;
423             break;
424         }
425         g_message("UnmapNotify for %lx", client->window);
426         client_unmanage(client);
427         break;
428     case DestroyNotify:
429         g_message("DestroyNotify for %lx", client->window);
430         client_unmanage(client);
431         break;
432     case ReparentNotify:
433         /* this is when the client is first taken captive in the frame */
434         if (e->xreparent.parent == client->frame->plate) break;
435
436         /*
437           This event is quite rare and is usually handled in unmapHandler.
438           However, if the window is unmapped when the reparent event occurs,
439           the window manager never sees it because an unmap event is not sent
440           to an already unmapped window.
441         */
442
443         /* we don't want the reparent event, put it back on the stack for the
444            X server to deal with after we unmanage the window */
445         XPutBackEvent(ob_display, e);
446      
447         client_unmanage(client);
448         break;
449     case MapRequest:
450         /* we shouldn't be able to get this unless we're iconic */
451         g_assert(client->iconic);
452
453         if (screen_showing_desktop)
454             screen_show_desktop(FALSE);
455         client_iconify(client, FALSE, TRUE);
456         if (!client->frame->visible)
457             /* if its not visible still, then don't mess with it */
458             break;
459         if (client->shaded)
460             client_shade(client, FALSE);
461         client_focus(client);
462         stacking_raise(client);
463         break;
464     case ClientMessage:
465         /* validate cuz we query stuff off the client here */
466         if (!client_validate(client)) break;
467   
468         if (e->xclient.format != 32) return;
469
470         msgtype = e->xclient.message_type;
471         if (msgtype == prop_atoms.wm_change_state) {
472             /* compress changes into a single change */
473             while (XCheckTypedWindowEvent(ob_display, e->type,
474                                           client->window, &ce)) {
475                 /* XXX: it would be nice to compress ALL messages of a
476                    type, not just messages in a row without other
477                    message types between. */
478                 if (ce.xclient.message_type != msgtype) {
479                     XPutBackEvent(ob_display, &ce);
480                     break;
481                 }
482                 e->xclient = ce.xclient;
483             }
484             client_set_wm_state(client, e->xclient.data.l[0]);
485         } else if (msgtype == prop_atoms.net_wm_desktop) {
486             /* compress changes into a single change */
487             while (XCheckTypedWindowEvent(ob_display, e->type,
488                                           client->window, &ce)) {
489                 /* XXX: it would be nice to compress ALL messages of a
490                    type, not just messages in a row without other
491                    message types between. */
492                 if (ce.xclient.message_type != msgtype) {
493                     XPutBackEvent(ob_display, &ce);
494                     break;
495                 }
496                 e->xclient = ce.xclient;
497             }
498             client_set_desktop(client, e->xclient.data.l[0]);
499         } else if (msgtype == prop_atoms.net_wm_state) {
500             /* can't compress these */
501             g_message("net_wm_state %s %ld %ld for 0x%lx\n",
502                       (e->xclient.data.l[0] == 0 ? "Remove" :
503                        e->xclient.data.l[0] == 1 ? "Add" :
504                        e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
505                       e->xclient.data.l[1], e->xclient.data.l[2],
506                       client->window);
507             client_set_state(client, e->xclient.data.l[0],
508                              e->xclient.data.l[1], e->xclient.data.l[2]);
509         } else if (msgtype == prop_atoms.net_close_window) {
510             g_message("net_close_window for 0x%lx\n", client->window);
511             client_close(client);
512         } else if (msgtype == prop_atoms.net_active_window) {
513             g_message("net_active_window for 0x%lx\n", client->window);
514             if (screen_showing_desktop)
515                 screen_show_desktop(FALSE);
516             if (client->iconic)
517                 client_iconify(client, FALSE, TRUE);
518             else if (!client->frame->visible)
519                 /* if its not visible for other reasons, then don't mess
520                    with it */
521                 break;
522             if (client->shaded)
523                 client_shade(client, FALSE);
524             client_focus(client);
525             stacking_raise(client);
526         }
527         break;
528     case PropertyNotify:
529         /* validate cuz we query stuff off the client here */
530         if (!client_validate(client)) break;
531   
532         /* compress changes to a single property into a single change */
533         while (XCheckTypedWindowEvent(ob_display, e->type,
534                                       client->window, &ce)) {
535             /* XXX: it would be nice to compress ALL changes to a property,
536                not just changes in a row without other props between. */
537             if (ce.xproperty.atom != e->xproperty.atom) {
538                 XPutBackEvent(ob_display, &ce);
539                 break;
540             }
541         }
542
543         msgtype = e->xproperty.atom;
544         if (msgtype == XA_WM_NORMAL_HINTS) {
545             client_update_normal_hints(client);
546             /* normal hints can make a window non-resizable */
547             client_setup_decor_and_functions(client);
548         }
549         else if (msgtype == XA_WM_HINTS)
550             client_update_wmhints(client);
551         else if (msgtype == XA_WM_TRANSIENT_FOR) {
552             client_update_transient_for(client);
553             client_get_type(client);
554             /* type may have changed, so update the layer */
555             client_calc_layer(client);
556             client_setup_decor_and_functions(client);
557         }
558         else if (msgtype == prop_atoms.net_wm_name ||
559                  msgtype == prop_atoms.wm_name)
560             client_update_title(client);
561         else if (msgtype == prop_atoms.net_wm_icon_name ||
562                  msgtype == prop_atoms.wm_icon_name)
563             client_update_icon_title(client);
564         else if (msgtype == prop_atoms.wm_class)
565             client_update_class(client);
566         else if (msgtype == prop_atoms.wm_protocols) {
567             client_update_protocols(client);
568             client_setup_decor_and_functions(client);
569         }
570         else if (msgtype == prop_atoms.net_wm_strut)
571             client_update_strut(client);
572         else if (msgtype == prop_atoms.net_wm_icon)
573             client_update_icons(client);
574         else if (msgtype == prop_atoms.kwm_win_icon)
575             client_update_kwm_icon(client);
576     }
577 }