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 if (fi
.xfocus
.window
== e
->xfocus
.window
)
249 focus_set_client(NULL
);
254 event_lasttime
= e
->xcrossing
.time
;
255 /* NotifyUngrab occurs when a mouse button is released and the event is
256 caused, like when lowering a window */
257 if (e
->xcrossing
.mode
== NotifyGrab
) return;
260 event_lasttime
= CurrentTime
;
264 client
= g_hash_table_lookup(client_map
, &window
);
266 /* deal with it in the kernel */
268 event_handle_client(client
, e
);
269 else if (window
== ob_root
)
270 event_handle_root(e
);
271 else if (e
->type
== MapRequest
)
272 client_manage(window
);
273 else if (e
->type
== ConfigureRequest
) {
274 /* unhandled configure requests must be used to configure the
278 xwc
.x
= e
->xconfigurerequest
.x
;
279 xwc
.y
= e
->xconfigurerequest
.y
;
280 xwc
.width
= e
->xconfigurerequest
.width
;
281 xwc
.height
= e
->xconfigurerequest
.height
;
282 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
283 xwc
.sibling
= e
->xconfigurerequest
.above
;
284 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
286 /* we are not to be held responsible if someone sends us an
288 xerror_set_ignore(TRUE
);
289 XConfigureWindow(ob_display
, window
,
290 e
->xconfigurerequest
.value_mask
, &xwc
);
291 xerror_set_ignore(FALSE
);
294 /* dispatch the event to registered handlers */
295 dispatch_x(e
, client
);
298 static void event_handle_root(XEvent
*e
)
304 if (e
->xclient
.format
!= 32) break;
306 msgtype
= e
->xclient
.message_type
;
307 if (msgtype
== prop_atoms
.net_current_desktop
) {
308 unsigned int d
= e
->xclient
.data
.l
[0];
309 if (d
< screen_num_desktops
)
310 screen_set_desktop(d
);
311 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
312 unsigned int d
= e
->xclient
.data
.l
[0];
314 screen_set_num_desktops(d
);
315 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
316 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
320 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
321 screen_update_desktop_names();
322 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
323 screen_update_layout();
328 static void event_handle_client(Client
*client
, XEvent
*e
)
333 ConfigValue focus_follow
;
337 focus_set_client(client
);
339 g_message("Focus%s on client for %lx", (e
->type
==FocusIn
?"In":"Out"),
341 /* focus state can affect the stacking layer */
342 client_calc_layer(client
);
343 engine_frame_adjust_focus(client
->frame
);
346 if (ob_state
== State_Starting
) {
347 /* move it to the top of the focus order */
348 guint desktop
= client
->desktop
;
349 if (desktop
== DESKTOP_ALL
) desktop
= screen_desktop
;
350 focus_order
[desktop
] = g_list_remove(focus_order
[desktop
], client
);
351 focus_order
[desktop
] = g_list_prepend(focus_order
[desktop
],client
);
353 if (!config_get("focusFollowsMouse", Config_Bool
, &focus_follow
))
354 g_assert_not_reached();
355 if (focus_follow
.bool)
356 client_focus(client
);
359 case ConfigureRequest
:
361 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
362 ConfigureRequest
, &ce
)) {
364 /* XXX if this causes bad things.. we can compress config req's
365 with the same mask. */
366 e
->xconfigurerequest
.value_mask
|=
367 ce
.xconfigurerequest
.value_mask
;
368 if (ce
.xconfigurerequest
.value_mask
& CWX
)
369 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
370 if (ce
.xconfigurerequest
.value_mask
& CWY
)
371 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
372 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
373 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
374 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
375 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
376 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
377 e
->xconfigurerequest
.border_width
=
378 ce
.xconfigurerequest
.border_width
;
379 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
380 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
382 if (i
) g_message("Compressed %d Configures", i
);
384 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
385 if (client
->iconic
|| client
->shaded
) return;
387 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
388 client
->border_width
= e
->xconfigurerequest
.border_width
;
390 /* resize, then move, as specified in the EWMH section 7.7 */
391 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
396 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
397 e
->xconfigurerequest
.x
: client
->area
.x
;
398 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
399 e
->xconfigurerequest
.y
: client
->area
.y
;
400 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
401 e
->xconfigurerequest
.width
: client
->area
.width
;
402 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
403 e
->xconfigurerequest
.height
: client
->area
.height
;
405 switch (client
->gravity
) {
406 case NorthEastGravity
:
408 corner
= Corner_TopRight
;
410 case SouthWestGravity
:
412 corner
= Corner_BottomLeft
;
414 case SouthEastGravity
:
415 corner
= Corner_BottomRight
;
417 default: /* NorthWest, Static, etc */
418 corner
= Corner_TopLeft
;
421 client_configure(client
, corner
, x
, y
, w
, h
, FALSE
, FALSE
);
424 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
425 switch (e
->xconfigurerequest
.detail
) {
428 stacking_lower(client
);
434 stacking_raise(client
);
440 if (client
->ignore_unmaps
) {
441 client
->ignore_unmaps
--;
444 client_unmanage(client
);
447 client_unmanage(client
);
450 /* this is when the client is first taken captive in the frame */
451 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
454 This event is quite rare and is usually handled in unmapHandler.
455 However, if the window is unmapped when the reparent event occurs,
456 the window manager never sees it because an unmap event is not sent
457 to an already unmapped window.
460 /* we don't want the reparent event, put it back on the stack for the
461 X server to deal with after we unmanage the window */
462 XPutBackEvent(ob_display
, e
);
464 client_unmanage(client
);
467 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
468 does, we don't want it! */
469 if (screen_showing_desktop
)
470 screen_show_desktop(FALSE
);
471 client_iconify(client
, FALSE
, TRUE
);
472 if (!client
->frame
->visible
)
473 /* if its not visible still, then don't mess with it */
476 client_shade(client
, FALSE
);
477 client_focus(client
);
478 stacking_raise(client
);
481 /* validate cuz we query stuff off the client here */
482 if (!client_validate(client
)) break;
484 if (e
->xclient
.format
!= 32) return;
486 msgtype
= e
->xclient
.message_type
;
487 if (msgtype
== prop_atoms
.wm_change_state
) {
488 /* compress changes into a single change */
489 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
490 client
->window
, &ce
)) {
491 /* XXX: it would be nice to compress ALL messages of a
492 type, not just messages in a row without other
493 message types between. */
494 if (ce
.xclient
.message_type
!= msgtype
) {
495 XPutBackEvent(ob_display
, &ce
);
498 e
->xclient
= ce
.xclient
;
500 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
501 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
502 /* compress changes into a single change */
503 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
504 client
->window
, &ce
)) {
505 /* XXX: it would be nice to compress ALL messages of a
506 type, not just messages in a row without other
507 message types between. */
508 if (ce
.xclient
.message_type
!= msgtype
) {
509 XPutBackEvent(ob_display
, &ce
);
512 e
->xclient
= ce
.xclient
;
514 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
515 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
516 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0]);
517 } else if (msgtype
== prop_atoms
.net_wm_state
) {
518 /* can't compress these */
519 g_message("net_wm_state %s %ld %ld for 0x%lx",
520 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
521 e
->xclient
.data
.l
[0] == 1 ? "Add" :
522 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
523 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
525 client_set_state(client
, e
->xclient
.data
.l
[0],
526 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
527 } else if (msgtype
== prop_atoms
.net_close_window
) {
528 g_message("net_close_window for 0x%lx", client
->window
);
529 client_close(client
);
530 } else if (msgtype
== prop_atoms
.net_active_window
) {
531 g_message("net_active_window for 0x%lx", client
->window
);
532 if (screen_showing_desktop
)
533 screen_show_desktop(FALSE
);
535 client_iconify(client
, FALSE
, TRUE
);
536 else if (!client
->frame
->visible
)
537 /* if its not visible for other reasons, then don't mess
541 client_shade(client
, FALSE
);
542 client_focus(client
);
543 stacking_raise(client
);
547 /* validate cuz we query stuff off the client here */
548 if (!client_validate(client
)) break;
550 /* compress changes to a single property into a single change */
551 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
552 client
->window
, &ce
)) {
553 /* XXX: it would be nice to compress ALL changes to a property,
554 not just changes in a row without other props between. */
555 if (ce
.xproperty
.atom
!= e
->xproperty
.atom
) {
556 XPutBackEvent(ob_display
, &ce
);
561 msgtype
= e
->xproperty
.atom
;
562 if (msgtype
== XA_WM_NORMAL_HINTS
) {
563 client_update_normal_hints(client
);
564 /* normal hints can make a window non-resizable */
565 client_setup_decor_and_functions(client
);
567 else if (msgtype
== XA_WM_HINTS
)
568 client_update_wmhints(client
);
569 else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
570 client_update_transient_for(client
);
571 client_get_type(client
);
572 /* type may have changed, so update the layer */
573 client_calc_layer(client
);
574 client_setup_decor_and_functions(client
);
576 else if (msgtype
== prop_atoms
.net_wm_name
||
577 msgtype
== prop_atoms
.wm_name
)
578 client_update_title(client
);
579 else if (msgtype
== prop_atoms
.net_wm_icon_name
||
580 msgtype
== prop_atoms
.wm_icon_name
)
581 client_update_icon_title(client
);
582 else if (msgtype
== prop_atoms
.wm_class
)
583 client_update_class(client
);
584 else if (msgtype
== prop_atoms
.wm_protocols
) {
585 client_update_protocols(client
);
586 client_setup_decor_and_functions(client
);
588 else if (msgtype
== prop_atoms
.net_wm_strut
)
589 client_update_strut(client
);
590 else if (msgtype
== prop_atoms
.net_wm_icon
)
591 client_update_icons(client
);
592 else if (msgtype
== prop_atoms
.kwm_win_icon
)
593 client_update_kwm_icon(client
);
597 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
598 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
599 engine_frame_adjust_shape(client
->frame
);