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
;
253 if (e
->xcrossing
.mode
!= NotifyNormal
)
254 return; /* skip me! */
258 client
= g_hash_table_lookup(client_map
, (gpointer
)window
);
261 event_handle_client(client
, e
);
262 } else if (window
== ob_root
)
263 event_handle_root(e
);
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 g_message("Proxying configure event for 0x%lx", window
);
279 /* we are not to be held responsible if someone sends us an
281 xerror_set_ignore(TRUE
);
282 XConfigureWindow(ob_display
, window
,
283 e
->xconfigurerequest
.value_mask
, &xwc
);
284 xerror_set_ignore(FALSE
);
287 /* dispatch Crossing, Pointer and Key events to the hooks */
290 if (client
!= NULL
) engine_mouse_enter(client
->frame
, window
);
291 HOOKFIRECLIENT(pointerenter
, client
);
294 if (client
!= NULL
) engine_mouse_leave(client
->frame
, window
);
295 HOOKFIRECLIENT(pointerleave
, client
);
299 engine_mouse_press(client
->frame
, window
,
300 e
->xbutton
.x
, e
->xbutton
.y
);
301 pointer_event(e
, client
);
305 engine_mouse_release(client
->frame
, window
,
306 e
->xbutton
.x
, e
->xbutton
.y
);
307 pointer_event(e
, client
);
310 pointer_event(e
, client
);
314 keyboard_event(&e
->xkey
);
318 if (e
->type
== extensions_xkb_event_basep
) {
319 switch (((XkbAnyEvent
*)&e
)->xkb_type
) {
321 HOOKFIRECLIENT(bell
, client
);
328 static void event_handle_root(XEvent
*e
)
334 g_message("MapRequest on root");
335 client_manage(e
->xmap
.window
);
338 if (e
->xclient
.format
!= 32) break;
340 msgtype
= e
->xclient
.message_type
;
341 if (msgtype
== prop_atoms
.net_current_desktop
) {
342 unsigned int d
= e
->xclient
.data
.l
[0];
343 if (d
<= screen_num_desktops
)
344 screen_set_desktop(d
);
345 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
346 unsigned int d
= e
->xclient
.data
.l
[0];
348 screen_set_num_desktops(d
);
349 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
350 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
354 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
355 screen_update_desktop_names();
356 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
357 screen_update_layout();
362 static void event_handle_client(Client
*client
, XEvent
*e
)
369 client
->focused
= TRUE
;
370 engine_frame_adjust_focus(client
->frame
);
372 /* focus state can affect the stacking layer */
373 client_calc_layer(client
);
375 focus_set_client(client
);
378 client
->focused
= FALSE
;
379 engine_frame_adjust_focus(client
->frame
);
381 /* focus state can affect the stacking layer */
382 client_calc_layer(client
);
384 if (focus_client
== client
)
385 focus_set_client(NULL
);
387 case ConfigureRequest
:
388 g_message("ConfigureRequest for window %lx", client
->window
);
390 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
391 ConfigureRequest
, &ce
)) {
392 /* XXX if this causes bad things.. we can compress config req's
393 with the same mask. */
394 e
->xconfigurerequest
.value_mask
|=
395 ce
.xconfigurerequest
.value_mask
;
396 if (ce
.xconfigurerequest
.value_mask
& CWX
)
397 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
398 if (ce
.xconfigurerequest
.value_mask
& CWY
)
399 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
400 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
401 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
402 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
403 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
404 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
405 e
->xconfigurerequest
.border_width
=
406 ce
.xconfigurerequest
.border_width
;
407 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
408 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
411 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
412 if (client
->iconic
|| client
->shaded
) return;
414 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
415 client
->border_width
= e
->xconfigurerequest
.border_width
;
417 /* resize, then move, as specified in the EWMH section 7.7 */
418 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
423 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
424 e
->xconfigurerequest
.x
: client
->area
.x
;
425 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
426 e
->xconfigurerequest
.y
: client
->area
.y
;
427 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
428 e
->xconfigurerequest
.width
: client
->area
.width
;
429 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
430 e
->xconfigurerequest
.height
: client
->area
.height
;
432 switch (client
->gravity
) {
433 case NorthEastGravity
:
435 corner
= Corner_TopRight
;
437 case SouthWestGravity
:
439 corner
= Corner_BottomLeft
;
441 case SouthEastGravity
:
442 corner
= Corner_BottomRight
;
444 default: /* NorthWest, Static, etc */
445 corner
= Corner_TopLeft
;
448 client_configure(client
, corner
, x
, y
, w
, h
, FALSE
, FALSE
);
451 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
452 switch (e
->xconfigurerequest
.detail
) {
455 stacking_lower(client
);
461 stacking_raise(client
);
467 if (client
->ignore_unmaps
) {
468 client
->ignore_unmaps
--;
471 g_message("UnmapNotify for %lx", client
->window
);
472 client_unmanage(client
);
475 g_message("DestroyNotify for %lx", client
->window
);
476 client_unmanage(client
);
479 /* this is when the client is first taken captive in the frame */
480 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
483 This event is quite rare and is usually handled in unmapHandler.
484 However, if the window is unmapped when the reparent event occurs,
485 the window manager never sees it because an unmap event is not sent
486 to an already unmapped window.
489 /* we don't want the reparent event, put it back on the stack for the
490 X server to deal with after we unmanage the window */
491 XPutBackEvent(ob_display
, e
);
493 client_unmanage(client
);
496 /* we shouldn't be able to get this unless we're iconic */
497 g_assert(client
->iconic
);
499 HOOKFIRECLIENT(requestactivate
, client
);
502 /* validate cuz we query stuff off the client here */
503 if (!client_validate(client
)) break;
505 if (e
->xclient
.format
!= 32) return;
507 msgtype
= e
->xclient
.message_type
;
508 if (msgtype
== prop_atoms
.wm_change_state
) {
509 /* compress changes into a single change */
510 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
511 client
->window
, &ce
)) {
512 /* XXX: it would be nice to compress ALL messages of a
513 type, not just messages in a row without other
514 message types between. */
515 if (ce
.xclient
.message_type
!= msgtype
) {
516 XPutBackEvent(ob_display
, &ce
);
519 e
->xclient
= ce
.xclient
;
521 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
522 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
523 /* compress changes into a single change */
524 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
525 client
->window
, &ce
)) {
526 /* XXX: it would be nice to compress ALL messages of a
527 type, not just messages in a row without other
528 message types between. */
529 if (ce
.xclient
.message_type
!= msgtype
) {
530 XPutBackEvent(ob_display
, &ce
);
533 e
->xclient
= ce
.xclient
;
535 client_set_desktop(client
, e
->xclient
.data
.l
[0]);
536 } else if (msgtype
== prop_atoms
.net_wm_state
) {
537 /* can't compress these */
538 g_message("net_wm_state %s %ld %ld for 0x%lx\n",
539 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
540 e
->xclient
.data
.l
[0] == 1 ? "Add" :
541 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
542 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
544 client_set_state(client
, e
->xclient
.data
.l
[0],
545 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
546 } else if (msgtype
== prop_atoms
.net_close_window
) {
547 g_message("net_close_window for 0x%lx\n", client
->window
);
548 client_close(client
);
549 } else if (msgtype
== prop_atoms
.net_active_window
) {
550 g_message("net_active_window for 0x%lx\n", client
->window
);
551 if (screen_showing_desktop
)
552 screen_show_desktop(FALSE
);
554 client_iconify(client
, FALSE
, TRUE
);
555 else if (!client
->frame
->visible
)
556 /* if its not visible for other reasons, then don't mess
559 HOOKFIRECLIENT(requestactivate
, client
);
563 /* validate cuz we query stuff off the client here */
564 if (!client_validate(client
)) break;
566 /* compress changes to a single property into a single change */
567 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
568 client
->window
, &ce
)) {
569 /* XXX: it would be nice to compress ALL changes to a property,
570 not just changes in a row without other props between. */
571 if (ce
.xproperty
.atom
!= e
->xproperty
.atom
) {
572 XPutBackEvent(ob_display
, &ce
);
577 msgtype
= e
->xproperty
.atom
;
578 if (msgtype
== XA_WM_NORMAL_HINTS
) {
579 client_update_normal_hints(client
);
580 /* normal hints can make a window non-resizable */
581 client_setup_decor_and_functions(client
);
583 else if (msgtype
== XA_WM_HINTS
)
584 client_update_wmhints(client
);
585 else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
586 client_update_transient_for(client
);
587 client_get_type(client
);
588 /* type may have changed, so update the layer */
589 client_calc_layer(client
);
590 client_setup_decor_and_functions(client
);
592 else if (msgtype
== prop_atoms
.net_wm_name
||
593 msgtype
== prop_atoms
.wm_name
)
594 client_update_title(client
);
595 else if (msgtype
== prop_atoms
.net_wm_icon_name
||
596 msgtype
== prop_atoms
.wm_icon_name
)
597 client_update_icon_title(client
);
598 else if (msgtype
== prop_atoms
.wm_class
)
599 client_update_class(client
);
600 else if (msgtype
== prop_atoms
.wm_protocols
) {
601 client_update_protocols(client
);
602 client_setup_decor_and_functions(client
);
604 else if (msgtype
== prop_atoms
.net_wm_strut
)
605 client_update_strut(client
);
606 else if (msgtype
== prop_atoms
.net_wm_icon
)
607 client_update_icons(client
);
608 else if (msgtype
== prop_atoms
.kwm_win_icon
)
609 client_update_kwm_icon(client
);