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