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 /* grab the lasttime and hack up the state */
155 event_lasttime
= e
->xbutton
.time
;
156 e
->xbutton
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
157 /* kill off the Button1Mask etc, only want the modifiers */
158 e
->xbutton
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
159 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
162 event_lasttime
= e
->xkey
.time
;
163 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
164 /* kill off the Button1Mask etc, only want the modifiers */
165 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
166 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
167 /* add to the state the mask of the modifier being pressed, if it is
168 a modifier key being pressed (this is a little ugly..) */
169 /* I'm commenting this out cuz i don't want "C-Control_L" being returned. */
170 /* kp = modmap->modifiermap;*/
171 /* for (i = 0; i < mask_table_size; ++i) {*/
172 /* for (k = 0; k < modmap->max_keypermod; ++k) {*/
173 /* if (*kp == e->xkey.keycode) {*/ /* found the keycode */
174 /* add the mask for it */
175 /* e->xkey.state |= mask_table[i];*/
176 /* cause the first loop to break; */
177 /* i = mask_table_size;*/
178 /* break;*/ /* get outta here! */
186 event_lasttime
= e
->xkey
.time
;
187 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
188 /* kill off the Button1Mask etc, only want the modifiers */
189 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
190 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
191 /* remove from the state the mask of the modifier being released, if
192 it is a modifier key being released (this is a little ugly..) */
193 kp
= modmap
->modifiermap
;
194 for (i
= 0; i
< mask_table_size
; ++i
) {
195 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
196 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
197 /* remove the mask for it */
198 e
->xkey
.state
&= ~mask_table
[i
];
199 /* cause the first loop to break; */
201 break; /* get outta here! */
208 event_lasttime
= e
->xmotion
.time
;
209 e
->xmotion
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
210 /* kill off the Button1Mask etc, only want the modifiers */
211 e
->xmotion
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
212 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
213 /* compress events */
214 while (XCheckTypedWindowEvent(ob_display
, window
, e
->type
, &ce
)) {
215 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
216 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
220 event_lasttime
= e
->xproperty
.time
;
223 if (e
->xfocus
.mode
== NotifyGrab
||
224 !(e
->xfocus
.detail
== NotifyNonlinearVirtual
||
225 e
->xfocus
.detail
== NotifyNonlinear
))
229 if (e
->xfocus
.mode
== NotifyGrab
||
230 !(e
->xfocus
.detail
== NotifyNonlinearVirtual
||
231 e
->xfocus
.detail
== NotifyNonlinear
))
234 /* FocusOut events just make us look for FocusIn events. They
235 are mostly ignored otherwise. */
238 if (XCheckTypedEvent(ob_display
, FocusIn
, &fi
)) {
241 if (fi
.xfocus
.window
== e
->xfocus
.window
)
248 event_lasttime
= e
->xcrossing
.time
;
249 /* NotifyUngrab occurs when a mouse button is released and the event is
250 caused, like when lowering a window */
251 if (e
->xcrossing
.mode
== NotifyGrab
) return;
255 client
= g_hash_table_lookup(client_map
, &window
);
257 /* deal with it in the kernel */
259 event_handle_client(client
, e
);
260 else if (window
== ob_root
)
261 event_handle_root(e
);
262 else if (e
->type
== MapRequest
)
263 client_manage(window
);
264 else if (e
->type
== ConfigureRequest
) {
265 /* unhandled configure requests must be used to configure the
269 xwc
.x
= e
->xconfigurerequest
.x
;
270 xwc
.y
= e
->xconfigurerequest
.y
;
271 xwc
.width
= e
->xconfigurerequest
.width
;
272 xwc
.height
= e
->xconfigurerequest
.height
;
273 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
274 xwc
.sibling
= e
->xconfigurerequest
.above
;
275 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
277 /* we are not to be held responsible if someone sends us an
279 xerror_set_ignore(TRUE
);
280 XConfigureWindow(ob_display
, window
,
281 e
->xconfigurerequest
.value_mask
, &xwc
);
282 xerror_set_ignore(FALSE
);
285 /* dispatch the event to registered handlers */
286 dispatch_x(e
, client
);
289 static void event_handle_root(XEvent
*e
)
295 if (e
->xclient
.format
!= 32) break;
297 msgtype
= e
->xclient
.message_type
;
298 if (msgtype
== prop_atoms
.net_current_desktop
) {
299 unsigned int d
= e
->xclient
.data
.l
[0];
300 if (d
< screen_num_desktops
)
301 screen_set_desktop(d
);
302 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
303 unsigned int d
= e
->xclient
.data
.l
[0];
305 screen_set_num_desktops(d
);
306 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
307 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
311 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
312 screen_update_desktop_names();
313 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
314 screen_update_layout();
319 static void event_handle_client(Client
*client
, XEvent
*e
)
328 client_set_focused(client
, e
->type
== FocusIn
);
330 case ConfigureRequest
:
332 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
333 ConfigureRequest
, &ce
)) {
335 /* XXX if this causes bad things.. we can compress config req's
336 with the same mask. */
337 e
->xconfigurerequest
.value_mask
|=
338 ce
.xconfigurerequest
.value_mask
;
339 if (ce
.xconfigurerequest
.value_mask
& CWX
)
340 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
341 if (ce
.xconfigurerequest
.value_mask
& CWY
)
342 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
343 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
344 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
345 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
346 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
347 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
348 e
->xconfigurerequest
.border_width
=
349 ce
.xconfigurerequest
.border_width
;
350 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
351 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
353 if (i
) g_message("Compressed %d Configures", i
);
355 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
356 if (client
->iconic
|| client
->shaded
) return;
358 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
359 client
->border_width
= e
->xconfigurerequest
.border_width
;
361 /* resize, then move, as specified in the EWMH section 7.7 */
362 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
367 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
368 e
->xconfigurerequest
.x
: client
->area
.x
;
369 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
370 e
->xconfigurerequest
.y
: client
->area
.y
;
371 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
372 e
->xconfigurerequest
.width
: client
->area
.width
;
373 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
374 e
->xconfigurerequest
.height
: client
->area
.height
;
376 switch (client
->gravity
) {
377 case NorthEastGravity
:
379 corner
= Corner_TopRight
;
381 case SouthWestGravity
:
383 corner
= Corner_BottomLeft
;
385 case SouthEastGravity
:
386 corner
= Corner_BottomRight
;
388 default: /* NorthWest, Static, etc */
389 corner
= Corner_TopLeft
;
392 client_configure(client
, corner
, x
, y
, w
, h
, FALSE
, FALSE
);
395 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
396 switch (e
->xconfigurerequest
.detail
) {
399 stacking_lower(client
);
405 stacking_raise(client
);
411 if (client
->ignore_unmaps
) {
412 client
->ignore_unmaps
--;
415 client_unmanage(client
);
418 client_unmanage(client
);
421 /* this is when the client is first taken captive in the frame */
422 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
425 This event is quite rare and is usually handled in unmapHandler.
426 However, if the window is unmapped when the reparent event occurs,
427 the window manager never sees it because an unmap event is not sent
428 to an already unmapped window.
431 /* we don't want the reparent event, put it back on the stack for the
432 X server to deal with after we unmanage the window */
433 XPutBackEvent(ob_display
, e
);
435 client_unmanage(client
);
438 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
439 does, we don't want it! */
440 if (screen_showing_desktop
)
441 screen_show_desktop(FALSE
);
442 client_iconify(client
, FALSE
, TRUE
);
443 if (!client
->frame
->visible
)
444 /* if its not visible still, then don't mess with it */
447 client_shade(client
, FALSE
);
448 client_focus(client
);
449 stacking_raise(client
);
452 /* validate cuz we query stuff off the client here */
453 if (!client_validate(client
)) break;
455 if (e
->xclient
.format
!= 32) return;
457 msgtype
= e
->xclient
.message_type
;
458 if (msgtype
== prop_atoms
.wm_change_state
) {
459 /* compress changes into a single change */
460 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
461 client
->window
, &ce
)) {
462 /* XXX: it would be nice to compress ALL messages of a
463 type, not just messages in a row without other
464 message types between. */
465 if (ce
.xclient
.message_type
!= msgtype
) {
466 XPutBackEvent(ob_display
, &ce
);
469 e
->xclient
= ce
.xclient
;
471 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
472 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
473 /* compress changes into a single change */
474 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
475 client
->window
, &ce
)) {
476 /* XXX: it would be nice to compress ALL messages of a
477 type, not just messages in a row without other
478 message types between. */
479 if (ce
.xclient
.message_type
!= msgtype
) {
480 XPutBackEvent(ob_display
, &ce
);
483 e
->xclient
= ce
.xclient
;
485 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
486 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
487 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0]);
488 } else if (msgtype
== prop_atoms
.net_wm_state
) {
489 /* can't compress these */
490 g_message("net_wm_state %s %ld %ld for 0x%lx",
491 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
492 e
->xclient
.data
.l
[0] == 1 ? "Add" :
493 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
494 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
496 client_set_state(client
, e
->xclient
.data
.l
[0],
497 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
498 } else if (msgtype
== prop_atoms
.net_close_window
) {
499 g_message("net_close_window for 0x%lx", client
->window
);
500 client_close(client
);
501 } else if (msgtype
== prop_atoms
.net_active_window
) {
502 g_message("net_active_window for 0x%lx", client
->window
);
503 if (screen_showing_desktop
)
504 screen_show_desktop(FALSE
);
506 client_iconify(client
, FALSE
, TRUE
);
507 else if (!client
->frame
->visible
)
508 /* if its not visible for other reasons, then don't mess
512 client_shade(client
, FALSE
);
513 client_focus(client
);
514 stacking_raise(client
);
518 /* validate cuz we query stuff off the client here */
519 if (!client_validate(client
)) break;
521 /* compress changes to a single property into a single change */
522 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
523 client
->window
, &ce
)) {
524 /* XXX: it would be nice to compress ALL changes to a property,
525 not just changes in a row without other props between. */
526 if (ce
.xproperty
.atom
!= e
->xproperty
.atom
) {
527 XPutBackEvent(ob_display
, &ce
);
532 msgtype
= e
->xproperty
.atom
;
533 if (msgtype
== XA_WM_NORMAL_HINTS
) {
534 client_update_normal_hints(client
);
535 /* normal hints can make a window non-resizable */
536 client_setup_decor_and_functions(client
);
538 else if (msgtype
== XA_WM_HINTS
)
539 client_update_wmhints(client
);
540 else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
541 client_update_transient_for(client
);
542 client_get_type(client
);
543 /* type may have changed, so update the layer */
544 client_calc_layer(client
);
545 client_setup_decor_and_functions(client
);
547 else if (msgtype
== prop_atoms
.net_wm_name
||
548 msgtype
== prop_atoms
.wm_name
)
549 client_update_title(client
);
550 else if (msgtype
== prop_atoms
.net_wm_icon_name
||
551 msgtype
== prop_atoms
.wm_icon_name
)
552 client_update_icon_title(client
);
553 else if (msgtype
== prop_atoms
.wm_class
)
554 client_update_class(client
);
555 else if (msgtype
== prop_atoms
.wm_protocols
) {
556 client_update_protocols(client
);
557 client_setup_decor_and_functions(client
);
559 else if (msgtype
== prop_atoms
.net_wm_strut
)
560 client_update_strut(client
);
561 else if (msgtype
== prop_atoms
.net_wm_icon
)
562 client_update_icons(client
);
563 else if (msgtype
== prop_atoms
.kwm_win_icon
)
564 client_update_kwm_icon(client
);
568 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
569 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
570 engine_frame_adjust_shape(client
->frame
);