]> Dogcows Code - chaz/openbox/blob - openbox/event.c
comment why NotifyAncestor is not ignored
[chaz/openbox] / openbox / event.c
1 #include "openbox.h"
2 #include "client.h"
3 #include "config.h"
4 #include "xerror.h"
5 #include "prop.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 /* grab the lasttime and hack up the state */
153 switch (e->type) {
154 case ButtonPress:
155 case ButtonRelease:
156 event_lasttime = e->xbutton.time;
157 e->xbutton.state &= ~(LockMask | NumLockMask | ScrollLockMask);
158 /* kill off the Button1Mask etc, only want the modifiers */
159 e->xbutton.state &= (ControlMask | ShiftMask | Mod1Mask |
160 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
161 break;
162 case KeyPress:
163 event_lasttime = e->xkey.time;
164 e->xkey.state &= ~(LockMask | NumLockMask | ScrollLockMask);
165 /* kill off the Button1Mask etc, only want the modifiers */
166 e->xkey.state &= (ControlMask | ShiftMask | Mod1Mask |
167 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
168 /* add to the state the mask of the modifier being pressed, if it is
169 a modifier key being pressed (this is a little ugly..) */
170 /* I'm commenting this out cuz i don't want "C-Control_L" being returned. */
171 /* kp = modmap->modifiermap;*/
172 /* for (i = 0; i < mask_table_size; ++i) {*/
173 /* for (k = 0; k < modmap->max_keypermod; ++k) {*/
174 /* if (*kp == e->xkey.keycode) {*/ /* found the keycode */
175 /* add the mask for it */
176 /* e->xkey.state |= mask_table[i];*/
177 /* cause the first loop to break; */
178 /* i = mask_table_size;*/
179 /* break;*/ /* get outta here! */
180 /* }*/
181 /* ++kp;*/
182 /* }*/
183 /* }*/
184
185 break;
186 case KeyRelease:
187 event_lasttime = e->xkey.time;
188 e->xkey.state &= ~(LockMask | NumLockMask | ScrollLockMask);
189 /* kill off the Button1Mask etc, only want the modifiers */
190 e->xkey.state &= (ControlMask | ShiftMask | Mod1Mask |
191 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
192 /* remove from the state the mask of the modifier being released, if
193 it is a modifier key being released (this is a little ugly..) */
194 kp = modmap->modifiermap;
195 for (i = 0; i < mask_table_size; ++i) {
196 for (k = 0; k < modmap->max_keypermod; ++k) {
197 if (*kp == e->xkey.keycode) { /* found the keycode */
198 /* remove the mask for it */
199 e->xkey.state &= ~mask_table[i];
200 /* cause the first loop to break; */
201 i = mask_table_size;
202 break; /* get outta here! */
203 }
204 ++kp;
205 }
206 }
207 break;
208 case MotionNotify:
209 event_lasttime = e->xmotion.time;
210 e->xmotion.state &= ~(LockMask | NumLockMask | ScrollLockMask);
211 /* kill off the Button1Mask etc, only want the modifiers */
212 e->xmotion.state &= (ControlMask | ShiftMask | Mod1Mask |
213 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
214 /* compress events */
215 while (XCheckTypedWindowEvent(ob_display, window, e->type, &ce)) {
216 e->xmotion.x_root = ce.xmotion.x_root;
217 e->xmotion.y_root = ce.xmotion.y_root;
218 }
219 break;
220 case PropertyNotify:
221 event_lasttime = e->xproperty.time;
222 break;
223 case FocusIn:
224 g_message("FocusIn on %lx mode %d detail %d", window,
225 e->xfocus.mode, e->xfocus.detail);
226 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
227 because of RevertToPointerRoot. If the focus ends up reverting to
228 pointer root on a workspace change, then the FocusIn event that we
229 want will be of type NotifyAncestor. This situation does not occur
230 for FocusOut, so it is safely ignored there.
231 */
232 if (e->xfocus.detail == NotifyInferior ||
233 e->xfocus.detail > NotifyNonlinearVirtual) return;
234 g_message("FocusIn on %lx", window);
235 break;
236 case FocusOut:
237 g_message("FocusOut on %lx mode %d detail %d", window,
238 e->xfocus.mode, e->xfocus.detail);
239 if (e->xfocus.mode == NotifyGrab ||
240 e->xfocus.detail == NotifyInferior ||
241 e->xfocus.detail == NotifyAncestor ||
242 e->xfocus.detail > NotifyNonlinearVirtual) return;
243
244 g_message("FocusOut on %lx", window);
245 /* FocusOut events just make us look for FocusIn events. They
246 are mostly ignored otherwise. */
247 {
248 XEvent fi;
249 if (XCheckTypedEvent(ob_display, FocusIn, &fi)) {
250 event_process(&fi);
251
252 /* secret magic way of event_process telling us that no client
253 was found for the FocusIn event.
254
255 it should be noted!! that focus events of invalud types
256 (the ones that cause a return in the FocusIn case above)
257 will not cause this focus_fallback to be called. it will
258 be assumed that focus is going someplace sane still, or
259 there are more focus events coming to fix up the situation.
260 this may not be perfect.. but its working! and focus events
261 are too much headache to take that for granted. ktnx. ^_^
262 */
263 if (fi.xfocus.window == None)
264 focus_fallback(FALSE);
265 if (fi.xfocus.window == e->xfocus.window)
266 return;
267 } else
268 focus_fallback(FALSE);
269 }
270 break;
271 case EnterNotify:
272 case LeaveNotify:
273 event_lasttime = e->xcrossing.time;
274 /* NotifyUngrab occurs when a mouse button is released and the event is
275 caused, like when lowering a window */
276 if (e->xcrossing.mode == NotifyGrab) return;
277 break;
278 default:
279 event_lasttime = CurrentTime;
280 break;
281 }
282
283 client = g_hash_table_lookup(client_map, &window);
284
285 /* deal with it in the kernel */
286 if (client)
287 event_handle_client(client, e);
288 else if (window == ob_root)
289 event_handle_root(e);
290 else if (e->type == MapRequest)
291 client_manage(window);
292 else if (e->type == FocusIn)
293 e->xfocus.window = None; /* says a client was found for the event! */
294 else if (e->type == ConfigureRequest) {
295 /* unhandled configure requests must be used to configure the
296 window directly */
297 XWindowChanges xwc;
298
299 xwc.x = e->xconfigurerequest.x;
300 xwc.y = e->xconfigurerequest.y;
301 xwc.width = e->xconfigurerequest.width;
302 xwc.height = e->xconfigurerequest.height;
303 xwc.border_width = e->xconfigurerequest.border_width;
304 xwc.sibling = e->xconfigurerequest.above;
305 xwc.stack_mode = e->xconfigurerequest.detail;
306
307 /* we are not to be held responsible if someone sends us an
308 invalid request! */
309 xerror_set_ignore(TRUE);
310 XConfigureWindow(ob_display, window,
311 e->xconfigurerequest.value_mask, &xwc);
312 xerror_set_ignore(FALSE);
313 }
314
315 /* dispatch the event to registered handlers */
316 dispatch_x(e, client);
317 }
318
319 static void event_handle_root(XEvent *e)
320 {
321 Atom msgtype;
322
323 switch(e->type) {
324 case ClientMessage:
325 if (e->xclient.format != 32) break;
326
327 msgtype = e->xclient.message_type;
328 if (msgtype == prop_atoms.net_current_desktop) {
329 unsigned int d = e->xclient.data.l[0];
330 if (d < screen_num_desktops)
331 screen_set_desktop(d);
332 } else if (msgtype == prop_atoms.net_number_of_desktops) {
333 unsigned int d = e->xclient.data.l[0];
334 if (d > 0)
335 screen_set_num_desktops(d);
336 } else if (msgtype == prop_atoms.net_showing_desktop) {
337 screen_show_desktop(e->xclient.data.l[0] != 0);
338 }
339 break;
340 case PropertyNotify:
341 if (e->xproperty.atom == prop_atoms.net_desktop_names)
342 screen_update_desktop_names();
343 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
344 screen_update_layout();
345 break;
346 }
347 }
348
349 static void event_handle_client(Client *client, XEvent *e)
350 {
351 XEvent ce;
352 Atom msgtype;
353 int i=0;
354 ConfigValue focus_follow;
355
356 switch (e->type) {
357 case FocusIn:
358 focus_set_client(client);
359 case FocusOut:
360 g_message("Focus%s on client for %lx", (e->type==FocusIn?"In":"Out"),
361 client->window);
362 /* focus state can affect the stacking layer */
363 client_calc_layer(client);
364 engine_frame_adjust_focus(client->frame);
365 break;
366 case EnterNotify:
367 if (client_normal(client)) {
368 if (ob_state == State_Starting) {
369 /* move it to the top of the focus order */
370 guint desktop = client->desktop;
371 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
372 focus_order[desktop] = g_list_remove(focus_order[desktop],
373 client);
374 focus_order[desktop] = g_list_prepend(focus_order[desktop],
375 client);
376 } else {
377 if (!config_get("focusFollowsMouse",Config_Bool,&focus_follow))
378 g_assert_not_reached();
379 if (focus_follow.bool)
380 client_focus(client);
381 }
382 }
383 break;
384 case ConfigureRequest:
385 /* compress these */
386 while (XCheckTypedWindowEvent(ob_display, client->window,
387 ConfigureRequest, &ce)) {
388 ++i;
389 /* XXX if this causes bad things.. we can compress config req's
390 with the same mask. */
391 e->xconfigurerequest.value_mask |=
392 ce.xconfigurerequest.value_mask;
393 if (ce.xconfigurerequest.value_mask & CWX)
394 e->xconfigurerequest.x = ce.xconfigurerequest.x;
395 if (ce.xconfigurerequest.value_mask & CWY)
396 e->xconfigurerequest.y = ce.xconfigurerequest.y;
397 if (ce.xconfigurerequest.value_mask & CWWidth)
398 e->xconfigurerequest.width = ce.xconfigurerequest.width;
399 if (ce.xconfigurerequest.value_mask & CWHeight)
400 e->xconfigurerequest.height = ce.xconfigurerequest.height;
401 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
402 e->xconfigurerequest.border_width =
403 ce.xconfigurerequest.border_width;
404 if (ce.xconfigurerequest.value_mask & CWStackMode)
405 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
406 }
407 if (i) g_message("Compressed %d Configures", i);
408
409 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
410 if (client->iconic || client->shaded) return;
411
412 if (e->xconfigurerequest.value_mask & CWBorderWidth)
413 client->border_width = e->xconfigurerequest.border_width;
414
415 /* resize, then move, as specified in the EWMH section 7.7 */
416 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
417 CWX | CWY)) {
418 int x, y, w, h;
419 Corner corner;
420
421 x = (e->xconfigurerequest.value_mask & CWX) ?
422 e->xconfigurerequest.x : client->area.x;
423 y = (e->xconfigurerequest.value_mask & CWY) ?
424 e->xconfigurerequest.y : client->area.y;
425 w = (e->xconfigurerequest.value_mask & CWWidth) ?
426 e->xconfigurerequest.width : client->area.width;
427 h = (e->xconfigurerequest.value_mask & CWHeight) ?
428 e->xconfigurerequest.height : client->area.height;
429
430 switch (client->gravity) {
431 case NorthEastGravity:
432 case EastGravity:
433 corner = Corner_TopRight;
434 break;
435 case SouthWestGravity:
436 case SouthGravity:
437 corner = Corner_BottomLeft;
438 break;
439 case SouthEastGravity:
440 corner = Corner_BottomRight;
441 break;
442 default: /* NorthWest, Static, etc */
443 corner = Corner_TopLeft;
444 }
445
446 client_configure(client, corner, x, y, w, h, FALSE, FALSE);
447 }
448
449 if (e->xconfigurerequest.value_mask & CWStackMode) {
450 switch (e->xconfigurerequest.detail) {
451 case Below:
452 case BottomIf:
453 stacking_lower(client);
454 break;
455
456 case Above:
457 case TopIf:
458 default:
459 stacking_raise(client);
460 break;
461 }
462 }
463 break;
464 case UnmapNotify:
465 if (client->ignore_unmaps) {
466 client->ignore_unmaps--;
467 break;
468 }
469 client_unmanage(client);
470 break;
471 case DestroyNotify:
472 client_unmanage(client);
473 break;
474 case ReparentNotify:
475 /* this is when the client is first taken captive in the frame */
476 if (e->xreparent.parent == client->frame->plate) break;
477
478 /*
479 This event is quite rare and is usually handled in unmapHandler.
480 However, if the window is unmapped when the reparent event occurs,
481 the window manager never sees it because an unmap event is not sent
482 to an already unmapped window.
483 */
484
485 /* we don't want the reparent event, put it back on the stack for the
486 X server to deal with after we unmanage the window */
487 XPutBackEvent(ob_display, e);
488
489 client_unmanage(client);
490 break;
491 case MapRequest:
492 if (!client->iconic) break; /* this normally doesn't happen, but if it
493 does, we don't want it! */
494 if (screen_showing_desktop)
495 screen_show_desktop(FALSE);
496 client_iconify(client, FALSE, TRUE);
497 if (!client->frame->visible)
498 /* if its not visible still, then don't mess with it */
499 break;
500 if (client->shaded)
501 client_shade(client, FALSE);
502 client_focus(client);
503 stacking_raise(client);
504 break;
505 case ClientMessage:
506 /* validate cuz we query stuff off the client here */
507 if (!client_validate(client)) break;
508
509 if (e->xclient.format != 32) return;
510
511 msgtype = e->xclient.message_type;
512 if (msgtype == prop_atoms.wm_change_state) {
513 /* compress changes into a single change */
514 while (XCheckTypedWindowEvent(ob_display, e->type,
515 client->window, &ce)) {
516 /* XXX: it would be nice to compress ALL messages of a
517 type, not just messages in a row without other
518 message types between. */
519 if (ce.xclient.message_type != msgtype) {
520 XPutBackEvent(ob_display, &ce);
521 break;
522 }
523 e->xclient = ce.xclient;
524 }
525 client_set_wm_state(client, e->xclient.data.l[0]);
526 } else if (msgtype == prop_atoms.net_wm_desktop) {
527 /* compress changes into a single change */
528 while (XCheckTypedWindowEvent(ob_display, e->type,
529 client->window, &ce)) {
530 /* XXX: it would be nice to compress ALL messages of a
531 type, not just messages in a row without other
532 message types between. */
533 if (ce.xclient.message_type != msgtype) {
534 XPutBackEvent(ob_display, &ce);
535 break;
536 }
537 e->xclient = ce.xclient;
538 }
539 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
540 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
541 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
542 FALSE);
543 } else if (msgtype == prop_atoms.net_wm_state) {
544 /* can't compress these */
545 g_message("net_wm_state %s %ld %ld for 0x%lx",
546 (e->xclient.data.l[0] == 0 ? "Remove" :
547 e->xclient.data.l[0] == 1 ? "Add" :
548 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
549 e->xclient.data.l[1], e->xclient.data.l[2],
550 client->window);
551 client_set_state(client, e->xclient.data.l[0],
552 e->xclient.data.l[1], e->xclient.data.l[2]);
553 } else if (msgtype == prop_atoms.net_close_window) {
554 g_message("net_close_window for 0x%lx", client->window);
555 client_close(client);
556 } else if (msgtype == prop_atoms.net_active_window) {
557 g_message("net_active_window for 0x%lx", client->window);
558 if (screen_showing_desktop)
559 screen_show_desktop(FALSE);
560 if (client->iconic)
561 client_iconify(client, FALSE, TRUE);
562 else if (!client->frame->visible)
563 /* if its not visible for other reasons, then don't mess
564 with it */
565 break;
566 if (client->shaded)
567 client_shade(client, FALSE);
568 client_focus(client);
569 stacking_raise(client);
570 }
571 break;
572 case PropertyNotify:
573 /* validate cuz we query stuff off the client here */
574 if (!client_validate(client)) break;
575
576 /* compress changes to a single property into a single change */
577 while (XCheckTypedWindowEvent(ob_display, e->type,
578 client->window, &ce)) {
579 /* XXX: it would be nice to compress ALL changes to a property,
580 not just changes in a row without other props between. */
581 if (ce.xproperty.atom != e->xproperty.atom) {
582 XPutBackEvent(ob_display, &ce);
583 break;
584 }
585 }
586
587 msgtype = e->xproperty.atom;
588 if (msgtype == XA_WM_NORMAL_HINTS) {
589 client_update_normal_hints(client);
590 /* normal hints can make a window non-resizable */
591 client_setup_decor_and_functions(client);
592 }
593 else if (msgtype == XA_WM_HINTS)
594 client_update_wmhints(client);
595 else if (msgtype == XA_WM_TRANSIENT_FOR) {
596 client_update_transient_for(client);
597 client_get_type(client);
598 /* type may have changed, so update the layer */
599 client_calc_layer(client);
600 client_setup_decor_and_functions(client);
601 }
602 else if (msgtype == prop_atoms.net_wm_name ||
603 msgtype == prop_atoms.wm_name)
604 client_update_title(client);
605 else if (msgtype == prop_atoms.net_wm_icon_name ||
606 msgtype == prop_atoms.wm_icon_name)
607 client_update_icon_title(client);
608 else if (msgtype == prop_atoms.wm_class)
609 client_update_class(client);
610 else if (msgtype == prop_atoms.wm_protocols) {
611 client_update_protocols(client);
612 client_setup_decor_and_functions(client);
613 }
614 else if (msgtype == prop_atoms.net_wm_strut)
615 client_update_strut(client);
616 else if (msgtype == prop_atoms.net_wm_icon)
617 client_update_icons(client);
618 else if (msgtype == prop_atoms.kwm_win_icon)
619 client_update_kwm_icon(client);
620 default:
621 ;
622 #ifdef SHAPE
623 if (extensions_shape && e->type == extensions_shape_event_basep) {
624 client->shaped = ((XShapeEvent*)e)->shaped;
625 engine_frame_adjust_shape(client->frame);
626 }
627 #endif
628 }
629 }
This page took 0.0664979999999999 seconds and 5 git commands to generate.