11 #include "extensions.h"
17 #include <X11/keysym.h>
18 #include <X11/Xatom.h>
19 #ifdef HAVE_SYS_SELECT_H
20 # include <sys/select.h>
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);
27 Time event_lasttime = 0;
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
40 static int mask_table_size;
44 mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
46 /* get lock masks that are defined by the display (not constant) */
47 modmap = XGetModifierMapping(ob_display);
49 if (modmap && modmap->max_keypermod > 0) {
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,
59 for (cnt = 0; cnt < size; ++cnt) {
60 if (! modmap->modifiermap[cnt]) continue;
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];
72 XFreeModifiermap(modmap);
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.
89 if (!XPending(ob_display))
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.
100 XSync(ob_display, FALSE);
101 if (!XEventsQueued(ob_display, QueuedAlready))
104 XNextEvent(ob_display, &e);
109 timer_dispatch((GTimeVal**)&wait);
110 x_fd = ConnectionNumber(ob_display);
112 FD_SET(x_fd, &selset);
113 select(x_fd + 1, &selset, NULL, NULL, wait);
116 void event_process(XEvent *e)
127 window = e->xmap.window;
130 window = e->xunmap.window;
133 window = e->xdestroywindow.window;
135 case ConfigureRequest:
136 window = e->xconfigurerequest.window;
140 if (extensions_xkb && e->type == extensions_xkb_event_basep) {
141 switch (((XkbAnyEvent*)&e)->xkb_type) {
143 window = ((XkbBellNotifyEvent*)&e)->window;
149 window = e->xany.window;
152 /* grab the lasttime and hack up the state */
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);
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! */
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; */
202 break; /* get outta here! */
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;
221 event_lasttime = e->xproperty.time;
224 g_message("FocusIn on %lx mode %d detail %d", window,
225 e->xfocus.mode, e->xfocus.detail);
226 if (e->xfocus.detail == NotifyInferior ||
227 e->xfocus.detail == NotifyAncestor ||
228 e->xfocus.detail > NotifyNonlinearVirtual) return;
229 g_message("FocusIn on %lx", window);
232 g_message("FocusOut on %lx mode %d detail %d", window,
233 e->xfocus.mode, e->xfocus.detail);
234 if (e->xfocus.detail == NotifyInferior ||
235 e->xfocus.detail == NotifyAncestor ||
236 e->xfocus.detail > NotifyNonlinearVirtual) return;
238 g_message("FocusOut on %lx", window);
239 /* FocusOut events just make us look for FocusIn events. They
240 are mostly ignored otherwise. */
243 if (XCheckTypedEvent(ob_display, FocusIn, &fi)) {
246 /* secret magic way of event_process telling us that no client
247 was found for the FocusIn event */
248 if (fi.xfocus.window != None)
249 focus_fallback(FALSE);
250 if (fi.xfocus.window == e->xfocus.window)
253 focus_fallback(FALSE);
258 event_lasttime = e->xcrossing.time;
259 /* NotifyUngrab occurs when a mouse button is released and the event is
260 caused, like when lowering a window */
261 if (e->xcrossing.mode == NotifyGrab) return;
264 event_lasttime = CurrentTime;
268 client = g_hash_table_lookup(client_map, &window);
270 /* deal with it in the kernel */
272 event_handle_client(client, e);
273 else if (window == ob_root)
274 event_handle_root(e);
275 else if (e->type == MapRequest)
276 client_manage(window);
277 else if (e->type == ConfigureRequest) {
278 /* unhandled configure requests must be used to configure the
282 xwc.x = e->xconfigurerequest.x;
283 xwc.y = e->xconfigurerequest.y;
284 xwc.width = e->xconfigurerequest.width;
285 xwc.height = e->xconfigurerequest.height;
286 xwc.border_width = e->xconfigurerequest.border_width;
287 xwc.sibling = e->xconfigurerequest.above;
288 xwc.stack_mode = e->xconfigurerequest.detail;
290 /* we are not to be held responsible if someone sends us an
292 xerror_set_ignore(TRUE);
293 XConfigureWindow(ob_display, window,
294 e->xconfigurerequest.value_mask, &xwc);
295 xerror_set_ignore(FALSE);
298 /* dispatch the event to registered handlers */
299 dispatch_x(e, client);
302 static void event_handle_root(XEvent *e)
308 if (e->xclient.format != 32) break;
310 msgtype = e->xclient.message_type;
311 if (msgtype == prop_atoms.net_current_desktop) {
312 unsigned int d = e->xclient.data.l[0];
313 if (d < screen_num_desktops)
314 screen_set_desktop(d);
315 } else if (msgtype == prop_atoms.net_number_of_desktops) {
316 unsigned int d = e->xclient.data.l[0];
318 screen_set_num_desktops(d);
319 } else if (msgtype == prop_atoms.net_showing_desktop) {
320 screen_show_desktop(e->xclient.data.l[0] != 0);
324 if (e->xproperty.atom == prop_atoms.net_desktop_names)
325 screen_update_desktop_names();
326 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
327 screen_update_layout();
332 static void event_handle_client(Client *client, XEvent *e)
337 ConfigValue focus_follow;
341 focus_set_client(client);
343 g_message("Focus%s on client for %lx", (e->type==FocusIn?"In":"Out"),
345 /* focus state can affect the stacking layer */
346 client_calc_layer(client);
347 engine_frame_adjust_focus(client->frame);
349 e->xfocus.window = None; /* says a client was found for the event! */
352 if (client_normal(client)) {
353 if (ob_state == State_Starting) {
354 /* move it to the top of the focus order */
355 guint desktop = client->desktop;
356 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
357 focus_order[desktop] = g_list_remove(focus_order[desktop],
359 focus_order[desktop] = g_list_prepend(focus_order[desktop],
362 if (!config_get("focusFollowsMouse",Config_Bool,&focus_follow))
363 g_assert_not_reached();
364 if (focus_follow.bool)
365 client_focus(client);
369 case ConfigureRequest:
371 while (XCheckTypedWindowEvent(ob_display, client->window,
372 ConfigureRequest, &ce)) {
374 /* XXX if this causes bad things.. we can compress config req's
375 with the same mask. */
376 e->xconfigurerequest.value_mask |=
377 ce.xconfigurerequest.value_mask;
378 if (ce.xconfigurerequest.value_mask & CWX)
379 e->xconfigurerequest.x = ce.xconfigurerequest.x;
380 if (ce.xconfigurerequest.value_mask & CWY)
381 e->xconfigurerequest.y = ce.xconfigurerequest.y;
382 if (ce.xconfigurerequest.value_mask & CWWidth)
383 e->xconfigurerequest.width = ce.xconfigurerequest.width;
384 if (ce.xconfigurerequest.value_mask & CWHeight)
385 e->xconfigurerequest.height = ce.xconfigurerequest.height;
386 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
387 e->xconfigurerequest.border_width =
388 ce.xconfigurerequest.border_width;
389 if (ce.xconfigurerequest.value_mask & CWStackMode)
390 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
392 if (i) g_message("Compressed %d Configures", i);
394 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
395 if (client->iconic || client->shaded) return;
397 if (e->xconfigurerequest.value_mask & CWBorderWidth)
398 client->border_width = e->xconfigurerequest.border_width;
400 /* resize, then move, as specified in the EWMH section 7.7 */
401 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
406 x = (e->xconfigurerequest.value_mask & CWX) ?
407 e->xconfigurerequest.x : client->area.x;
408 y = (e->xconfigurerequest.value_mask & CWY) ?
409 e->xconfigurerequest.y : client->area.y;
410 w = (e->xconfigurerequest.value_mask & CWWidth) ?
411 e->xconfigurerequest.width : client->area.width;
412 h = (e->xconfigurerequest.value_mask & CWHeight) ?
413 e->xconfigurerequest.height : client->area.height;
415 switch (client->gravity) {
416 case NorthEastGravity:
418 corner = Corner_TopRight;
420 case SouthWestGravity:
422 corner = Corner_BottomLeft;
424 case SouthEastGravity:
425 corner = Corner_BottomRight;
427 default: /* NorthWest, Static, etc */
428 corner = Corner_TopLeft;
431 client_configure(client, corner, x, y, w, h, FALSE, FALSE);
434 if (e->xconfigurerequest.value_mask & CWStackMode) {
435 switch (e->xconfigurerequest.detail) {
438 stacking_lower(client);
444 stacking_raise(client);
450 if (client->ignore_unmaps) {
451 client->ignore_unmaps--;
454 client_unmanage(client);
457 client_unmanage(client);
460 /* this is when the client is first taken captive in the frame */
461 if (e->xreparent.parent == client->frame->plate) break;
464 This event is quite rare and is usually handled in unmapHandler.
465 However, if the window is unmapped when the reparent event occurs,
466 the window manager never sees it because an unmap event is not sent
467 to an already unmapped window.
470 /* we don't want the reparent event, put it back on the stack for the
471 X server to deal with after we unmanage the window */
472 XPutBackEvent(ob_display, e);
474 client_unmanage(client);
477 if (!client->iconic) break; /* this normally doesn't happen, but if it
478 does, we don't want it! */
479 if (screen_showing_desktop)
480 screen_show_desktop(FALSE);
481 client_iconify(client, FALSE, TRUE);
482 if (!client->frame->visible)
483 /* if its not visible still, then don't mess with it */
486 client_shade(client, FALSE);
487 client_focus(client);
488 stacking_raise(client);
491 /* validate cuz we query stuff off the client here */
492 if (!client_validate(client)) break;
494 if (e->xclient.format != 32) return;
496 msgtype = e->xclient.message_type;
497 if (msgtype == prop_atoms.wm_change_state) {
498 /* compress changes into a single change */
499 while (XCheckTypedWindowEvent(ob_display, e->type,
500 client->window, &ce)) {
501 /* XXX: it would be nice to compress ALL messages of a
502 type, not just messages in a row without other
503 message types between. */
504 if (ce.xclient.message_type != msgtype) {
505 XPutBackEvent(ob_display, &ce);
508 e->xclient = ce.xclient;
510 client_set_wm_state(client, e->xclient.data.l[0]);
511 } else if (msgtype == prop_atoms.net_wm_desktop) {
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);
522 e->xclient = ce.xclient;
524 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
525 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
526 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
528 } else if (msgtype == prop_atoms.net_wm_state) {
529 /* can't compress these */
530 g_message("net_wm_state %s %ld %ld for 0x%lx",
531 (e->xclient.data.l[0] == 0 ? "Remove" :
532 e->xclient.data.l[0] == 1 ? "Add" :
533 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
534 e->xclient.data.l[1], e->xclient.data.l[2],
536 client_set_state(client, e->xclient.data.l[0],
537 e->xclient.data.l[1], e->xclient.data.l[2]);
538 } else if (msgtype == prop_atoms.net_close_window) {
539 g_message("net_close_window for 0x%lx", client->window);
540 client_close(client);
541 } else if (msgtype == prop_atoms.net_active_window) {
542 g_message("net_active_window for 0x%lx", client->window);
543 if (screen_showing_desktop)
544 screen_show_desktop(FALSE);
546 client_iconify(client, FALSE, TRUE);
547 else if (!client->frame->visible)
548 /* if its not visible for other reasons, then don't mess
552 client_shade(client, FALSE);
553 client_focus(client);
554 stacking_raise(client);
558 /* validate cuz we query stuff off the client here */
559 if (!client_validate(client)) break;
561 /* compress changes to a single property into a single change */
562 while (XCheckTypedWindowEvent(ob_display, e->type,
563 client->window, &ce)) {
564 /* XXX: it would be nice to compress ALL changes to a property,
565 not just changes in a row without other props between. */
566 if (ce.xproperty.atom != e->xproperty.atom) {
567 XPutBackEvent(ob_display, &ce);
572 msgtype = e->xproperty.atom;
573 if (msgtype == XA_WM_NORMAL_HINTS) {
574 client_update_normal_hints(client);
575 /* normal hints can make a window non-resizable */
576 client_setup_decor_and_functions(client);
578 else if (msgtype == XA_WM_HINTS)
579 client_update_wmhints(client);
580 else if (msgtype == XA_WM_TRANSIENT_FOR) {
581 client_update_transient_for(client);
582 client_get_type(client);
583 /* type may have changed, so update the layer */
584 client_calc_layer(client);
585 client_setup_decor_and_functions(client);
587 else if (msgtype == prop_atoms.net_wm_name ||
588 msgtype == prop_atoms.wm_name)
589 client_update_title(client);
590 else if (msgtype == prop_atoms.net_wm_icon_name ||
591 msgtype == prop_atoms.wm_icon_name)
592 client_update_icon_title(client);
593 else if (msgtype == prop_atoms.wm_class)
594 client_update_class(client);
595 else if (msgtype == prop_atoms.wm_protocols) {
596 client_update_protocols(client);
597 client_setup_decor_and_functions(client);
599 else if (msgtype == prop_atoms.net_wm_strut)
600 client_update_strut(client);
601 else if (msgtype == prop_atoms.net_wm_icon)
602 client_update_icons(client);
603 else if (msgtype == prop_atoms.kwm_win_icon)
604 client_update_kwm_icon(client);
608 if (extensions_shape && e->type == extensions_shape_event_basep) {
609 client->shaped = ((XShapeEvent*)e)->shaped;
610 engine_frame_adjust_shape(client->frame);