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