10 #include "extensions.h"
15 #include <X11/keysym.h>
16 #include <X11/Xatom.h>
18 static void event_process(XEvent
*e
);
19 static void event_handle_root(XEvent
*e
);
20 static void event_handle_client(Client
*c
, XEvent
*e
);
22 Time event_lasttime
= 0;
24 /*! A list of all possible combinations of keyboard lock masks */
25 static unsigned int mask_list
[8];
26 /*! The value of the mask for the NumLock modifier */
27 static unsigned int NumLockMask
;
28 /*! The value of the mask for the ScrollLock modifier */
29 static unsigned int ScrollLockMask
;
30 /*! The key codes for the modifier keys */
31 static XModifierKeymap
*modmap
;
32 /*! Table of the constant modifier masks */
33 static const int mask_table
[] = {
34 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
35 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
37 static int mask_table_size
;
41 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
43 /* get lock masks that are defined by the display (not constant) */
44 modmap
= XGetModifierMapping(ob_display
);
46 if (modmap
&& modmap
->max_keypermod
> 0) {
48 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
49 /* get the values of the keyboard lock modifiers
50 Note: Caps lock is not retrieved the same way as Scroll and Num
51 lock since it doesn't need to be. */
52 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
53 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
56 for (cnt
= 0; cnt
< size
; ++cnt
) {
57 if (! modmap
->modifiermap
[cnt
]) continue;
59 if (num_lock
== modmap
->modifiermap
[cnt
])
60 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
61 if (scroll_lock
== modmap
->modifiermap
[cnt
])
62 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
67 mask_list
[1] = LockMask
;
68 mask_list
[2] = NumLockMask
;
69 mask_list
[3] = LockMask
| NumLockMask
;
70 mask_list
[4] = ScrollLockMask
;
71 mask_list
[5] = ScrollLockMask
| LockMask
;
72 mask_list
[6] = ScrollLockMask
| NumLockMask
;
73 mask_list
[7] = ScrollLockMask
| LockMask
| NumLockMask
;
78 XFreeModifiermap(modmap
);
90 There are slightly different event retrieval semantics here for
91 local (or high bandwidth) versus remote (or low bandwidth)
92 connections to the display/Xserver.
95 if (!XPending(ob_display
))
99 This XSync allows for far more compression of events, which
100 makes things like Motion events perform far far better. Since
101 it also means network traffic for every event instead of every
102 X events (where X is the number retrieved at a time), it
103 probably should not be used for setups where Openbox is
104 running on a remote/low bandwidth display/Xserver.
106 XSync(ob_display
, FALSE
);
107 if (!XEventsQueued(ob_display
, QueuedAlready
))
110 XNextEvent(ob_display
, &e
);
115 timer_dispatch((GTimeVal
**)&wait
);
116 x_fd
= ConnectionNumber(ob_display
);
118 FD_SET(x_fd
, &selset
);
119 select(x_fd
+ 1, &selset
, NULL
, NULL
, wait
);
122 void event_process(XEvent
*e
)
133 window
= e
->xunmap
.window
;
136 window
= e
->xdestroywindow
.window
;
138 case ConfigureRequest
:
139 window
= e
->xconfigurerequest
.window
;
143 if (e
->type
== extensions_xkb_event_basep
) {
144 switch (((XkbAnyEvent
*)&e
)->xkb_type
) {
146 window
= ((XkbBellNotifyEvent
*)&e
)->window
;
151 window
= e
->xany
.window
;
154 /* grab the lasttime and hack up the state */
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
);
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! */
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; */
204 break; /* get outta here! */
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
;
223 event_lasttime
= e
->xproperty
.time
;
227 if (e
->xfocus
.mode
== NotifyGrab
)
228 /*|| e.xfocus.mode == NotifyUngrab ||*/
230 /* From Metacity, from WindowMaker, ignore all funky pointer
231 root events. Its commented out cuz I don't think we need this
232 at all. If problems arise we can look into it */
233 /*e.xfocus.detail > NotifyNonlinearVirtual) */
234 return; /* skip me! */
235 if (e
->type
== FocusOut
) {
236 /* FocusOut events just make us look for FocusIn events. They
237 are mostly ignored otherwise. */
239 if (XCheckTypedEvent(ob_display
, FocusIn
, &fi
)) {
241 /* dont unfocus the window we just focused! */
242 if (fi
.xfocus
.window
== e
->xfocus
.window
)
249 event_lasttime
= e
->xcrossing
.time
;
253 client
= g_hash_table_lookup(client_map
, (gpointer
)window
);
256 event_handle_client(client
, e
);
257 } else if (window
== ob_root
)
258 event_handle_root(e
);
259 else if (e
->type
== ConfigureRequest
) {
260 /* unhandled configure requests must be used to configure the
264 xwc
.x
= e
->xconfigurerequest
.x
;
265 xwc
.y
= e
->xconfigurerequest
.y
;
266 xwc
.width
= e
->xconfigurerequest
.width
;
267 xwc
.height
= e
->xconfigurerequest
.height
;
268 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
269 xwc
.sibling
= e
->xconfigurerequest
.above
;
270 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
272 g_message("Proxying configure event for 0x%lx", window
);
274 /* we are not to be held responsible if someone sends us an
276 xerror_set_ignore(TRUE
);
277 XConfigureWindow(ob_display
, window
,
278 e
->xconfigurerequest
.value_mask
, &xwc
);
279 xerror_set_ignore(FALSE
);
282 /* dispatch Crossing, Pointer and Key events to the hooks */
285 if (client
!= NULL
) engine_mouse_enter(client
->frame
, window
);
286 /*HOOKFIRECLIENT(pointerenter, client);XXX*/
289 if (client
!= NULL
) engine_mouse_leave(client
->frame
, window
);
290 /*HOOKFIRECLIENT(pointerleave, client);XXX*/
294 engine_mouse_press(client
->frame
, window
,
295 e
->xbutton
.x
, e
->xbutton
.y
);
296 /*pointer_event(e, client);XXX*/
300 engine_mouse_release(client
->frame
, window
,
301 e
->xbutton
.x
, e
->xbutton
.y
);
302 /*pointer_event(e, client);XXX*/
305 /*pointer_event(e, client);XXX*/
309 /*keyboard_event(&e->xkey);XXX*/
313 if (e
->type
== extensions_xkb_event_basep
) {
314 switch (((XkbAnyEvent
*)&e
)->xkb_type
) {
316 /*HOOKFIRECLIENT(bell, client);XXX*/
323 static void event_handle_root(XEvent
*e
)
329 g_message("MapRequest on root");
330 client_manage(e
->xmap
.window
);
333 if (e
->xclient
.format
!= 32) break;
335 msgtype
= e
->xclient
.message_type
;
336 if (msgtype
== prop_atoms
.net_current_desktop
) {
337 unsigned int d
= e
->xclient
.data
.l
[0];
338 if (d
<= screen_num_desktops
)
339 screen_set_desktop(d
);
340 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
341 unsigned int d
= e
->xclient
.data
.l
[0];
343 screen_set_num_desktops(d
);
344 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
345 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
349 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
350 screen_update_desktop_names();
351 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
352 screen_update_layout();
357 static void event_handle_client(Client
*client
, XEvent
*e
)
364 client
->focused
= TRUE
;
365 engine_frame_adjust_focus(client
->frame
);
367 /* focus state can affect the stacking layer */
368 client_calc_layer(client
);
370 focus_set_client(client
);
373 client
->focused
= FALSE
;
374 engine_frame_adjust_focus(client
->frame
);
376 /* focus state can affect the stacking layer */
377 client_calc_layer(client
);
379 if (focus_client
== client
)
380 focus_set_client(NULL
);
382 case ConfigureRequest
:
383 g_message("ConfigureRequest for window %lx", client
->window
);
385 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
386 ConfigureRequest
, &ce
)) {
387 /* XXX if this causes bad things.. we can compress config req's
388 with the same mask. */
389 e
->xconfigurerequest
.value_mask
|=
390 ce
.xconfigurerequest
.value_mask
;
391 if (ce
.xconfigurerequest
.value_mask
& CWX
)
392 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
393 if (ce
.xconfigurerequest
.value_mask
& CWY
)
394 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
395 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
396 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
397 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
398 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
399 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
400 e
->xconfigurerequest
.border_width
=
401 ce
.xconfigurerequest
.border_width
;
402 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
403 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
406 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
407 if (client
->iconic
|| client
->shaded
) return;
409 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
410 client
->border_width
= e
->xconfigurerequest
.border_width
;
412 /* resize, then move, as specified in the EWMH section 7.7 */
413 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
418 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
419 e
->xconfigurerequest
.x
: client
->area
.x
;
420 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
421 e
->xconfigurerequest
.y
: client
->area
.y
;
422 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
423 e
->xconfigurerequest
.width
: client
->area
.width
;
424 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
425 e
->xconfigurerequest
.height
: client
->area
.height
;
427 switch (client
->gravity
) {
428 case NorthEastGravity
:
430 corner
= Corner_TopRight
;
432 case SouthWestGravity
:
434 corner
= Corner_BottomLeft
;
436 case SouthEastGravity
:
437 corner
= Corner_BottomRight
;
439 default: /* NorthWest, Static, etc */
440 corner
= Corner_TopLeft
;
443 client_configure(client
, corner
, x
, y
, w
, h
, FALSE
, FALSE
);
446 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
447 switch (e
->xconfigurerequest
.detail
) {
450 stacking_lower(client
);
456 stacking_raise(client
);
462 if (client
->ignore_unmaps
) {
463 client
->ignore_unmaps
--;
466 g_message("UnmapNotify for %lx", client
->window
);
467 client_unmanage(client
);
470 g_message("DestroyNotify for %lx", client
->window
);
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 /* we shouldn't be able to get this unless we're iconic */
492 g_assert(client
->iconic
);
494 /*HOOKFIRECLIENT(requestactivate, client);XXX*/
497 /* validate cuz we query stuff off the client here */
498 if (!client_validate(client
)) break;
500 if (e
->xclient
.format
!= 32) return;
502 msgtype
= e
->xclient
.message_type
;
503 if (msgtype
== prop_atoms
.wm_change_state
) {
504 /* compress changes into a single change */
505 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
506 client
->window
, &ce
)) {
507 /* XXX: it would be nice to compress ALL messages of a
508 type, not just messages in a row without other
509 message types between. */
510 if (ce
.xclient
.message_type
!= msgtype
) {
511 XPutBackEvent(ob_display
, &ce
);
514 e
->xclient
= ce
.xclient
;
516 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
517 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
518 /* compress changes into a single change */
519 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
520 client
->window
, &ce
)) {
521 /* XXX: it would be nice to compress ALL messages of a
522 type, not just messages in a row without other
523 message types between. */
524 if (ce
.xclient
.message_type
!= msgtype
) {
525 XPutBackEvent(ob_display
, &ce
);
528 e
->xclient
= ce
.xclient
;
530 client_set_desktop(client
, e
->xclient
.data
.l
[0]);
531 } else if (msgtype
== prop_atoms
.net_wm_state
) {
532 /* can't compress these */
533 g_message("net_wm_state %s %ld %ld for 0x%lx\n",
534 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
535 e
->xclient
.data
.l
[0] == 1 ? "Add" :
536 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
537 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
539 client_set_state(client
, e
->xclient
.data
.l
[0],
540 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
541 } else if (msgtype
== prop_atoms
.net_close_window
) {
542 g_message("net_close_window for 0x%lx\n", client
->window
);
543 client_close(client
);
544 } else if (msgtype
== prop_atoms
.net_active_window
) {
545 g_message("net_active_window for 0x%lx\n", client
->window
);
546 if (screen_showing_desktop
)
547 screen_show_desktop(FALSE
);
549 client_iconify(client
, FALSE
, TRUE
);
550 else if (!client
->frame
->visible
)
551 /* if its not visible for other reasons, then don't mess
554 /*HOOKFIRECLIENT(requestactivate, client);XXX*/
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
);