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