13 #include "extensions.h"
18 #include <X11/keysym.h>
19 #include <X11/Xatom.h>
21 static void event_process(XEvent
*e
);
22 static void event_handle_root(XEvent
*e
);
23 static void event_handle_client(Client
*c
, XEvent
*e
);
25 Time event_lasttime
= 0;
27 /*! A list of all possible combinations of keyboard lock masks */
28 static unsigned int mask_list
[8];
29 /*! The value of the mask for the NumLock modifier */
30 static unsigned int NumLockMask
;
31 /*! The value of the mask for the ScrollLock modifier */
32 static 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
];
70 mask_list
[1] = LockMask
;
71 mask_list
[2] = NumLockMask
;
72 mask_list
[3] = LockMask
| NumLockMask
;
73 mask_list
[4] = ScrollLockMask
;
74 mask_list
[5] = ScrollLockMask
| LockMask
;
75 mask_list
[6] = ScrollLockMask
| NumLockMask
;
76 mask_list
[7] = ScrollLockMask
| LockMask
| NumLockMask
;
81 XFreeModifiermap(modmap
);
93 There are slightly different event retrieval semantics here for
94 local (or high bandwidth) versus remote (or low bandwidth)
95 connections to the display/Xserver.
98 if (!XPending(ob_display
))
102 This XSync allows for far more compression of events, which
103 makes things like Motion events perform far far better. Since
104 it also means network traffic for every event instead of every
105 X events (where X is the number retrieved at a time), it
106 probably should not be used for setups where Openbox is
107 running on a remote/low bandwidth display/Xserver.
109 XSync(ob_display
, FALSE
);
110 if (!XEventsQueued(ob_display
, QueuedAlready
))
113 XNextEvent(ob_display
, &e
);
118 timer_dispatch((GTimeVal
**)&wait
);
119 x_fd
= ConnectionNumber(ob_display
);
121 FD_SET(x_fd
, &selset
);
122 select(x_fd
+ 1, &selset
, NULL
, NULL
, wait
);
125 void event_process(XEvent
*e
)
136 window
= e
->xunmap
.window
;
139 window
= e
->xdestroywindow
.window
;
141 case ConfigureRequest
:
142 window
= e
->xconfigurerequest
.window
;
146 if (e
->type
== extensions_xkb_event_basep
) {
147 switch (((XkbAnyEvent
*)&e
)->xkb_type
) {
149 window
= ((XkbBellNotifyEvent
*)&e
)->window
;
154 window
= e
->xany
.window
;
157 /* grab the lasttime and hack up the state */
161 event_lasttime
= e
->xbutton
.time
;
162 e
->xbutton
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
163 /* kill off the Button1Mask etc, only want the modifiers */
164 e
->xbutton
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
165 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
168 event_lasttime
= e
->xkey
.time
;
169 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
170 /* kill off the Button1Mask etc, only want the modifiers */
171 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
172 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
173 /* add to the state the mask of the modifier being pressed, if it is
174 a modifier key being pressed (this is a little ugly..) */
175 /* I'm commenting this out cuz i don't want "C-Control_L" being returned. */
176 /* kp = modmap->modifiermap;*/
177 /* for (i = 0; i < mask_table_size; ++i) {*/
178 /* for (k = 0; k < modmap->max_keypermod; ++k) {*/
179 /* if (*kp == e->xkey.keycode) {*/ /* found the keycode */
180 /* add the mask for it */
181 /* e->xkey.state |= mask_table[i];*/
182 /* cause the first loop to break; */
183 /* i = mask_table_size;*/
184 /* break;*/ /* get outta here! */
192 event_lasttime
= e
->xkey
.time
;
193 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
194 /* kill off the Button1Mask etc, only want the modifiers */
195 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
196 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
197 /* remove from the state the mask of the modifier being released, if
198 it is a modifier key being released (this is a little ugly..) */
199 kp
= modmap
->modifiermap
;
200 for (i
= 0; i
< mask_table_size
; ++i
) {
201 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
202 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
203 /* remove the mask for it */
204 e
->xkey
.state
&= ~mask_table
[i
];
205 /* cause the first loop to break; */
207 break; /* get outta here! */
214 event_lasttime
= e
->xmotion
.time
;
215 e
->xmotion
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
216 /* kill off the Button1Mask etc, only want the modifiers */
217 e
->xmotion
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
218 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
219 /* compress events */
220 while (XCheckTypedWindowEvent(ob_display
, window
, e
->type
, &ce
)) {
221 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
222 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
226 event_lasttime
= e
->xproperty
.time
;
230 if (e
->xfocus
.mode
== NotifyGrab
)
231 /*|| e.xfocus.mode == NotifyUngrab ||*/
233 /* From Metacity, from WindowMaker, ignore all funky pointer
234 root events. Its commented out cuz I don't think we need this
235 at all. If problems arise we can look into it */
236 /*e.xfocus.detail > NotifyNonlinearVirtual) */
237 return; /* skip me! */
238 if (e
->type
== FocusOut
) {
239 /* FocusOut events just make us look for FocusIn events. They
240 are mostly ignored otherwise. */
242 if (XCheckTypedEvent(ob_display
, FocusIn
, &fi
)) {
244 /* dont unfocus the window we just focused! */
245 if (fi
.xfocus
.window
== e
->xfocus
.window
)
252 event_lasttime
= e
->xcrossing
.time
;
256 client
= g_hash_table_lookup(client_map
, (gpointer
)window
);
259 event_handle_client(client
, e
);
260 } else if (window
== ob_root
)
261 event_handle_root(e
);
262 else if (e
->type
== ConfigureRequest
) {
263 /* unhandled configure requests must be used to configure the
267 xwc
.x
= e
->xconfigurerequest
.x
;
268 xwc
.y
= e
->xconfigurerequest
.y
;
269 xwc
.width
= e
->xconfigurerequest
.width
;
270 xwc
.height
= e
->xconfigurerequest
.height
;
271 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
272 xwc
.sibling
= e
->xconfigurerequest
.above
;
273 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
275 g_message("Proxying configure event for 0x%lx", window
);
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 Crossing, Pointer and Key events to the hooks */
288 if (client
!= NULL
) engine_mouse_enter(client
->frame
, window
);
289 HOOKFIRECLIENT(pointerenter
, client
);
292 if (client
!= NULL
) engine_mouse_leave(client
->frame
, window
);
293 HOOKFIRECLIENT(pointerleave
, client
);
297 engine_mouse_press(client
->frame
, window
,
298 e
->xbutton
.x
, e
->xbutton
.y
);
299 pointer_event(e
, client
);
303 engine_mouse_release(client
->frame
, window
,
304 e
->xbutton
.x
, e
->xbutton
.y
);
305 pointer_event(e
, client
);
308 pointer_event(e
, client
);
312 keyboard_event(&e
->xkey
);
316 if (e
->type
== extensions_xkb_event_basep
) {
317 switch (((XkbAnyEvent
*)&e
)->xkb_type
) {
319 HOOKFIRECLIENT(bell
, client
);
326 static void event_handle_root(XEvent
*e
)
332 g_message("MapRequest on root");
333 client_manage(e
->xmap
.window
);
336 if (e
->xclient
.format
!= 32) break;
338 msgtype
= e
->xclient
.message_type
;
339 if (msgtype
== prop_atoms
.net_current_desktop
) {
340 unsigned int d
= e
->xclient
.data
.l
[0];
341 if (d
<= screen_num_desktops
)
342 screen_set_desktop(d
);
343 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
344 unsigned int d
= e
->xclient
.data
.l
[0];
346 screen_set_num_desktops(d
);
347 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
348 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
352 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
353 screen_update_desktop_names();
354 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
355 screen_update_layout();
360 static void event_handle_client(Client
*client
, XEvent
*e
)
367 client
->focused
= TRUE
;
368 engine_frame_adjust_focus(client
->frame
);
370 /* focus state can affect the stacking layer */
371 client_calc_layer(client
);
373 focus_set_client(client
);
376 client
->focused
= FALSE
;
377 engine_frame_adjust_focus(client
->frame
);
379 /* focus state can affect the stacking layer */
380 client_calc_layer(client
);
382 if (focus_client
== client
)
383 focus_set_client(NULL
);
385 case ConfigureRequest
:
386 g_message("ConfigureRequest for window %lx", client
->window
);
388 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
389 ConfigureRequest
, &ce
)) {
390 /* XXX if this causes bad things.. we can compress config req's
391 with the same mask. */
392 e
->xconfigurerequest
.value_mask
|=
393 ce
.xconfigurerequest
.value_mask
;
394 if (ce
.xconfigurerequest
.value_mask
& CWX
)
395 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
396 if (ce
.xconfigurerequest
.value_mask
& CWY
)
397 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
398 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
399 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
400 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
401 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
402 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
403 e
->xconfigurerequest
.border_width
=
404 ce
.xconfigurerequest
.border_width
;
405 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
406 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
409 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
410 if (client
->iconic
|| client
->shaded
) return;
412 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
413 client
->border_width
= e
->xconfigurerequest
.border_width
;
415 /* resize, then move, as specified in the EWMH section 7.7 */
416 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
421 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
422 e
->xconfigurerequest
.x
: client
->area
.x
;
423 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
424 e
->xconfigurerequest
.y
: client
->area
.y
;
425 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
426 e
->xconfigurerequest
.width
: client
->area
.width
;
427 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
428 e
->xconfigurerequest
.height
: client
->area
.height
;
430 switch (client
->gravity
) {
431 case NorthEastGravity
:
433 corner
= Corner_TopRight
;
435 case SouthWestGravity
:
437 corner
= Corner_BottomLeft
;
439 case SouthEastGravity
:
440 corner
= Corner_BottomRight
;
442 default: /* NorthWest, Static, etc */
443 corner
= Corner_TopLeft
;
446 client_configure(client
, corner
, x
, y
, w
, h
, FALSE
, FALSE
);
449 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
450 switch (e
->xconfigurerequest
.detail
) {
453 stacking_lower(client
);
459 stacking_raise(client
);
465 if (client
->ignore_unmaps
) {
466 client
->ignore_unmaps
--;
469 g_message("UnmapNotify for %lx", client
->window
);
470 client_unmanage(client
);
473 g_message("DestroyNotify for %lx", client
->window
);
474 client_unmanage(client
);
477 /* this is when the client is first taken captive in the frame */
478 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
481 This event is quite rare and is usually handled in unmapHandler.
482 However, if the window is unmapped when the reparent event occurs,
483 the window manager never sees it because an unmap event is not sent
484 to an already unmapped window.
487 /* we don't want the reparent event, put it back on the stack for the
488 X server to deal with after we unmanage the window */
489 XPutBackEvent(ob_display
, e
);
491 client_unmanage(client
);
494 /* we shouldn't be able to get this unless we're iconic */
495 g_assert(client
->iconic
);
497 HOOKFIRECLIENT(requestactivate
, client
);
500 /* validate cuz we query stuff off the client here */
501 if (!client_validate(client
)) break;
503 if (e
->xclient
.format
!= 32) return;
505 msgtype
= e
->xclient
.message_type
;
506 if (msgtype
== prop_atoms
.wm_change_state
) {
507 /* compress changes into a single change */
508 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
509 client
->window
, &ce
)) {
510 /* XXX: it would be nice to compress ALL messages of a
511 type, not just messages in a row without other
512 message types between. */
513 if (ce
.xclient
.message_type
!= msgtype
) {
514 XPutBackEvent(ob_display
, &ce
);
517 e
->xclient
= ce
.xclient
;
519 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
520 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
521 /* compress changes into a single change */
522 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
523 client
->window
, &ce
)) {
524 /* XXX: it would be nice to compress ALL messages of a
525 type, not just messages in a row without other
526 message types between. */
527 if (ce
.xclient
.message_type
!= msgtype
) {
528 XPutBackEvent(ob_display
, &ce
);
531 e
->xclient
= ce
.xclient
;
533 client_set_desktop(client
, e
->xclient
.data
.l
[0]);
534 } else if (msgtype
== prop_atoms
.net_wm_state
) {
535 /* can't compress these */
536 g_message("net_wm_state %s %ld %ld for 0x%lx\n",
537 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
538 e
->xclient
.data
.l
[0] == 1 ? "Add" :
539 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
540 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
542 client_set_state(client
, e
->xclient
.data
.l
[0],
543 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
544 } else if (msgtype
== prop_atoms
.net_close_window
) {
545 g_message("net_close_window for 0x%lx\n", client
->window
);
546 client_close(client
);
547 } else if (msgtype
== prop_atoms
.net_active_window
) {
548 g_message("net_active_window for 0x%lx\n", client
->window
);
549 if (screen_showing_desktop
)
550 screen_show_desktop(FALSE
);
552 client_iconify(client
, FALSE
, TRUE
);
553 else if (!client
->frame
->visible
)
554 /* if its not visible for other reasons, then don't mess
557 HOOKFIRECLIENT(requestactivate
, client
);
561 /* validate cuz we query stuff off the client here */
562 if (!client_validate(client
)) break;
564 /* compress changes to a single property into a single change */
565 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
566 client
->window
, &ce
)) {
567 /* XXX: it would be nice to compress ALL changes to a property,
568 not just changes in a row without other props between. */
569 if (ce
.xproperty
.atom
!= e
->xproperty
.atom
) {
570 XPutBackEvent(ob_display
, &ce
);
575 msgtype
= e
->xproperty
.atom
;
576 if (msgtype
== XA_WM_NORMAL_HINTS
) {
577 client_update_normal_hints(client
);
578 /* normal hints can make a window non-resizable */
579 client_setup_decor_and_functions(client
);
581 else if (msgtype
== XA_WM_HINTS
)
582 client_update_wmhints(client
);
583 else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
584 client_update_transient_for(client
);
585 client_get_type(client
);
586 /* type may have changed, so update the layer */
587 client_calc_layer(client
);
588 client_setup_decor_and_functions(client
);
590 else if (msgtype
== prop_atoms
.net_wm_name
||
591 msgtype
== prop_atoms
.wm_name
)
592 client_update_title(client
);
593 else if (msgtype
== prop_atoms
.net_wm_icon_name
||
594 msgtype
== prop_atoms
.wm_icon_name
)
595 client_update_icon_title(client
);
596 else if (msgtype
== prop_atoms
.wm_class
)
597 client_update_class(client
);
598 else if (msgtype
== prop_atoms
.wm_protocols
) {
599 client_update_protocols(client
);
600 client_setup_decor_and_functions(client
);
602 else if (msgtype
== prop_atoms
.net_wm_strut
)
603 client_update_strut(client
);
604 else if (msgtype
== prop_atoms
.net_wm_icon
)
605 client_update_icons(client
);
606 else if (msgtype
== prop_atoms
.kwm_win_icon
)
607 client_update_kwm_icon(client
);