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