10 #include "extensions.h"
16 #include <X11/keysym.h>
17 #include <X11/Xatom.h>
18 #ifdef HAVE_SYS_SELECT_H
19 # include <sys/select.h>
22 static void event_process(XEvent
*e
);
23 static void event_handle_root(XEvent
*e
);
24 static void event_handle_client(Client
*c
, XEvent
*e
);
26 Time event_lasttime
= 0;
28 /*! The value of the mask for the NumLock modifier */
29 unsigned int NumLockMask
;
30 /*! The value of the mask for the ScrollLock modifier */
31 unsigned int ScrollLockMask
;
32 /*! The key codes for the modifier keys */
33 static XModifierKeymap
*modmap
;
34 /*! Table of the constant modifier masks */
35 static const int mask_table
[] = {
36 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
37 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
39 static int mask_table_size
;
43 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
45 /* get lock masks that are defined by the display (not constant) */
46 modmap
= XGetModifierMapping(ob_display
);
48 if (modmap
&& modmap
->max_keypermod
> 0) {
50 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
51 /* get the values of the keyboard lock modifiers
52 Note: Caps lock is not retrieved the same way as Scroll and Num
53 lock since it doesn't need to be. */
54 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
55 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
58 for (cnt
= 0; cnt
< size
; ++cnt
) {
59 if (! modmap
->modifiermap
[cnt
]) continue;
61 if (num_lock
== modmap
->modifiermap
[cnt
])
62 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
63 if (scroll_lock
== modmap
->modifiermap
[cnt
])
64 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
71 XFreeModifiermap(modmap
);
83 There are slightly different event retrieval semantics here for
84 local (or high bandwidth) versus remote (or low bandwidth)
85 connections to the display/Xserver.
88 if (!XPending(ob_display
))
92 This XSync allows for far more compression of events, which
93 makes things like Motion events perform far far better. Since
94 it also means network traffic for every event instead of every
95 X events (where X is the number retrieved at a time), it
96 probably should not be used for setups where Openbox is
97 running on a remote/low bandwidth display/Xserver.
99 XSync(ob_display
, FALSE
);
100 if (!XEventsQueued(ob_display
, QueuedAlready
))
103 XNextEvent(ob_display
, &e
);
108 timer_dispatch((GTimeVal
**)&wait
);
109 x_fd
= ConnectionNumber(ob_display
);
111 FD_SET(x_fd
, &selset
);
112 select(x_fd
+ 1, &selset
, NULL
, NULL
, wait
);
115 void event_process(XEvent
*e
)
126 window
= e
->xmap
.window
;
129 window
= e
->xunmap
.window
;
132 window
= e
->xdestroywindow
.window
;
134 case ConfigureRequest
:
135 window
= e
->xconfigurerequest
.window
;
139 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
140 switch (((XkbAnyEvent
*)&e
)->xkb_type
) {
142 window
= ((XkbBellNotifyEvent
*)&e
)->window
;
148 window
= e
->xany
.window
;
151 client
= g_hash_table_lookup(client_map
, &window
);
153 /* grab the lasttime and hack up the state */
157 event_lasttime
= e
->xbutton
.time
;
158 e
->xbutton
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
159 /* kill off the Button1Mask etc, only want the modifiers */
160 e
->xbutton
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
161 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
164 event_lasttime
= e
->xkey
.time
;
165 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
166 /* kill off the Button1Mask etc, only want the modifiers */
167 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
168 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
169 /* add to the state the mask of the modifier being pressed, if it is
170 a modifier key being pressed (this is a little ugly..) */
171 /* I'm commenting this out cuz i don't want "C-Control_L" being returned. */
172 /* kp = modmap->modifiermap;*/
173 /* for (i = 0; i < mask_table_size; ++i) {*/
174 /* for (k = 0; k < modmap->max_keypermod; ++k) {*/
175 /* if (*kp == e->xkey.keycode) {*/ /* found the keycode */
176 /* add the mask for it */
177 /* e->xkey.state |= mask_table[i];*/
178 /* cause the first loop to break; */
179 /* i = mask_table_size;*/
180 /* break;*/ /* get outta here! */
188 event_lasttime
= e
->xkey
.time
;
189 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
190 /* kill off the Button1Mask etc, only want the modifiers */
191 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
192 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
193 /* remove from the state the mask of the modifier being released, if
194 it is a modifier key being released (this is a little ugly..) */
195 kp
= modmap
->modifiermap
;
196 for (i
= 0; i
< mask_table_size
; ++i
) {
197 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
198 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
199 /* remove the mask for it */
200 e
->xkey
.state
&= ~mask_table
[i
];
201 /* cause the first loop to break; */
203 break; /* get outta here! */
210 event_lasttime
= e
->xmotion
.time
;
211 e
->xmotion
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
212 /* kill off the Button1Mask etc, only want the modifiers */
213 e
->xmotion
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
214 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
215 /* compress events */
216 while (XCheckTypedWindowEvent(ob_display
, window
, e
->type
, &ce
)) {
217 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
218 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
222 event_lasttime
= e
->xproperty
.time
;
225 g_message("FocusIn on %lx mode %d detail %d", window
,
226 e
->xfocus
.mode
, e
->xfocus
.detail
);
227 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
228 because of RevertToPointerRoot. If the focus ends up reverting to
229 pointer root on a workspace change, then the FocusIn event that we
230 want will be of type NotifyAncestor. This situation does not occur
231 for FocusOut, so it is safely ignored there.
233 if (e
->xfocus
.detail
== NotifyInferior
||
234 e
->xfocus
.detail
> NotifyNonlinearVirtual
||
236 /* says a client was not found for the event (or a valid FocusIn
239 e
->xfocus
.window
= None
;
243 g_message("FocusIn on %lx", window
);
246 g_message("FocusOut on %lx mode %d detail %d", window
,
247 e
->xfocus
.mode
, e
->xfocus
.detail
);
248 if (e
->xfocus
.mode
== NotifyGrab
||
249 e
->xfocus
.detail
== NotifyInferior
||
250 e
->xfocus
.detail
== NotifyAncestor
||
251 e
->xfocus
.detail
> NotifyNonlinearVirtual
) return;
253 g_message("FocusOut on %lx", window
);
254 /* Try process a FocusIn first, and if a legit one isn't found, then
255 do the fallback shiznit. */
258 gboolean isfo
= FALSE
;
260 if (XCheckTypedEvent(ob_display
, FocusIn
, &fi
)) {
263 /* when we have gotten a fi/fo pair, then see if there are any
264 more fo's coming. if there are, then don't fallback just yet
266 if ((isfo
= XCheckTypedEvent(ob_display
, FocusOut
, &fo
)))
267 XPutBackEvent(ob_display
, &fo
);
269 /* secret magic way of event_process telling us that no client
270 was found for the FocusIn event. ^_^ */
271 if (!isfo
&& fi
.xfocus
.window
== None
)
272 focus_fallback(FALSE
);
273 if (fi
.xfocus
.window
== e
->xfocus
.window
)
276 focus_fallback(FALSE
);
281 event_lasttime
= e
->xcrossing
.time
;
282 /* NotifyUngrab occurs when a mouse button is released and the event is
283 caused, like when lowering a window */
284 if (e
->xcrossing
.mode
== NotifyGrab
) return;
287 event_lasttime
= CurrentTime
;
291 /* deal with it in the kernel */
293 event_handle_client(client
, e
);
294 else if (window
== ob_root
)
295 event_handle_root(e
);
296 else if (e
->type
== MapRequest
)
297 client_manage(window
);
298 else if (e
->type
== ConfigureRequest
) {
299 /* unhandled configure requests must be used to configure the
303 xwc
.x
= e
->xconfigurerequest
.x
;
304 xwc
.y
= e
->xconfigurerequest
.y
;
305 xwc
.width
= e
->xconfigurerequest
.width
;
306 xwc
.height
= e
->xconfigurerequest
.height
;
307 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
308 xwc
.sibling
= e
->xconfigurerequest
.above
;
309 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
311 /* we are not to be held responsible if someone sends us an
313 xerror_set_ignore(TRUE
);
314 XConfigureWindow(ob_display
, window
,
315 e
->xconfigurerequest
.value_mask
, &xwc
);
316 xerror_set_ignore(FALSE
);
319 /* dispatch the event to registered handlers */
320 dispatch_x(e
, client
);
323 static void event_handle_root(XEvent
*e
)
329 if (e
->xclient
.format
!= 32) break;
331 msgtype
= e
->xclient
.message_type
;
332 if (msgtype
== prop_atoms
.net_current_desktop
) {
333 unsigned int d
= e
->xclient
.data
.l
[0];
334 if (d
< screen_num_desktops
)
335 screen_set_desktop(d
);
336 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
337 unsigned int d
= e
->xclient
.data
.l
[0];
339 screen_set_num_desktops(d
);
340 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
341 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
345 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
346 screen_update_desktop_names();
347 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
348 screen_update_layout();
353 static void event_handle_client(Client
*client
, XEvent
*e
)
361 focus_set_client(client
);
363 g_message("Focus%s on client for %lx", (e
->type
==FocusIn
?"In":"Out"),
365 /* focus state can affect the stacking layer */
366 client_calc_layer(client
);
367 engine_frame_adjust_focus(client
->frame
);
370 if (client_normal(client
)) {
371 if (ob_state
== State_Starting
) {
372 /* move it to the top of the focus order */
373 guint desktop
= client
->desktop
;
374 if (desktop
== DESKTOP_ALL
) desktop
= screen_desktop
;
375 focus_order
[desktop
] = g_list_remove(focus_order
[desktop
],
377 focus_order
[desktop
] = g_list_prepend(focus_order
[desktop
],
379 } else if (focus_follow
)
380 client_focus(client
);
383 case ConfigureRequest
:
385 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
386 ConfigureRequest
, &ce
)) {
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
;
406 if (i
) g_message("Compressed %d Configures", i
);
408 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
409 if (client
->iconic
|| client
->shaded
) return;
411 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
412 client
->border_width
= e
->xconfigurerequest
.border_width
;
414 /* resize, then move, as specified in the EWMH section 7.7 */
415 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
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
;
429 switch (client
->gravity
) {
430 case NorthEastGravity
:
432 corner
= Corner_TopRight
;
434 case SouthWestGravity
:
436 corner
= Corner_BottomLeft
;
438 case SouthEastGravity
:
439 corner
= Corner_BottomRight
;
441 default: /* NorthWest, Static, etc */
442 corner
= Corner_TopLeft
;
445 client_configure(client
, corner
, x
, y
, w
, h
, FALSE
, FALSE
);
448 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
449 switch (e
->xconfigurerequest
.detail
) {
452 stacking_lower(client
);
458 stacking_raise(client
);
464 if (client
->ignore_unmaps
) {
465 client
->ignore_unmaps
--;
468 client_unmanage(client
);
471 client_unmanage(client
);
474 /* this is when the client is first taken captive in the frame */
475 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
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.
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
);
488 client_unmanage(client
);
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 */
500 client_shade(client
, FALSE
);
501 client_focus(client
);
502 stacking_raise(client
);
505 /* validate cuz we query stuff off the client here */
506 if (!client_validate(client
)) break;
508 if (e
->xclient
.format
!= 32) return;
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
);
522 e
->xclient
= ce
.xclient
;
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
);
536 e
->xclient
= ce
.xclient
;
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],
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],
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
);
560 client_iconify(client
, FALSE
, TRUE
);
561 else if (!client
->frame
->visible
)
562 /* if its not visible for other reasons, then don't mess
566 client_shade(client
, FALSE
);
567 client_focus(client
);
568 stacking_raise(client
);
572 /* validate cuz we query stuff off the client here */
573 if (!client_validate(client
)) break;
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
);
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
);
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
);
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
);
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
);
622 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
623 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
624 engine_frame_adjust_shape(client
->frame
);