1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 event.c for the Openbox window manager
4 Copyright (c) 2003 Ben Jansens
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 See the COPYING file for a copy of the GNU General Public License.
31 #include "menuframe.h"
35 #include "framerender.h"
37 #include "moveresize.h"
40 #include "extensions.h"
43 #include <X11/keysym.h>
44 #include <X11/Xatom.h>
47 #ifdef HAVE_SYS_SELECT_H
48 # include <sys/select.h>
55 #include <X11/ICE/ICElib.h>
58 static void event_process(const XEvent
*e
, gpointer data
);
59 static void event_done(gpointer data
);
60 static void event_client_dest(ObClient
*client
, gpointer data
);
61 static void event_handle_root(XEvent
*e
);
62 static void event_handle_menu(XEvent
*e
);
63 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
64 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
65 static void event_handle_client(ObClient
*c
, XEvent
*e
);
66 static void event_handle_group(ObGroup
*g
, XEvent
*e
);
68 static gboolean
focus_delay_func(gpointer data
);
69 static void focus_delay_client_dest(ObClient
*client
, gpointer data
);
71 static gboolean
menu_hide_delay_func(gpointer data
);
73 Time event_lasttime
= 0;
75 /*! The value of the mask for the NumLock modifier */
77 /*! The value of the mask for the ScrollLock modifier */
79 /*! The key codes for the modifier keys */
80 static XModifierKeymap
*modmap
;
81 /*! Table of the constant modifier masks */
82 static const gint mask_table
[] = {
83 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
84 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
86 static gint mask_table_size
;
88 static guint ignore_enter_focus
= 0;
90 static gboolean menu_can_hide
;
92 static ObClient
*focus_in
, *focus_out
;
95 static void ice_handler(gint fd
, gpointer conn
)
98 IceProcessMessages(conn
, NULL
, &b
);
101 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
102 IcePointer
*watch_data
)
107 fd
= IceConnectionNumber(conn
);
108 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
110 ob_main_loop_fd_remove(ob_main_loop
, fd
);
116 void event_startup(gboolean reconfig
)
118 if (reconfig
) return;
120 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
122 /* get lock masks that are defined by the display (not constant) */
123 modmap
= XGetModifierMapping(ob_display
);
125 if (modmap
&& modmap
->max_keypermod
> 0) {
127 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
128 /* get the values of the keyboard lock modifiers
129 Note: Caps lock is not retrieved the same way as Scroll and Num
130 lock since it doesn't need to be. */
131 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
132 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
135 for (cnt
= 0; cnt
< size
; ++cnt
) {
136 if (! modmap
->modifiermap
[cnt
]) continue;
138 if (num_lock
== modmap
->modifiermap
[cnt
])
139 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
140 if (scroll_lock
== modmap
->modifiermap
[cnt
])
141 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
145 ob_main_loop_x_add(ob_main_loop
, event_process
, event_done
, NULL
, NULL
);
148 IceAddConnectionWatch(ice_watch
, NULL
);
151 client_add_destructor(focus_delay_client_dest
, NULL
);
152 client_add_destructor(event_client_dest
, NULL
);
155 void event_shutdown(gboolean reconfig
)
157 if (reconfig
) return;
160 IceRemoveConnectionWatch(ice_watch
, NULL
);
163 client_remove_destructor(focus_delay_client_dest
);
164 XFreeModifiermap(modmap
);
167 static Window
event_get_window(XEvent
*e
)
174 window
= RootWindow(ob_display
, ob_screen
);
177 window
= e
->xmap
.window
;
180 window
= e
->xunmap
.window
;
183 window
= e
->xdestroywindow
.window
;
185 case ConfigureRequest
:
186 window
= e
->xconfigurerequest
.window
;
188 case ConfigureNotify
:
189 window
= e
->xconfigure
.window
;
193 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
194 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
196 window
= ((XkbBellNotifyEvent
*)e
)->window
;
202 window
= e
->xany
.window
;
207 static void event_set_lasttime(XEvent
*e
)
211 /* grab the lasttime and hack up the state */
227 t
= e
->xproperty
.time
;
231 t
= e
->xcrossing
.time
;
234 /* if more event types are anticipated, get their timestamp
239 if (t
> event_lasttime
)
243 #define STRIP_MODS(s) \
244 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
245 /* kill off the Button1Mask etc, only want the modifiers */ \
246 s &= (ControlMask | ShiftMask | Mod1Mask | \
247 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
249 static void event_hack_mods(XEvent *e)
257 STRIP_MODS(e
->xbutton
.state
);
260 STRIP_MODS(e
->xkey
.state
);
263 STRIP_MODS(e
->xkey
.state
);
264 /* remove from the state the mask of the modifier being released, if
265 it is a modifier key being released (this is a little ugly..) */
266 kp
= modmap
->modifiermap
;
267 for (i
= 0; i
< mask_table_size
; ++i
) {
268 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
269 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
270 /* remove the mask for it */
271 e
->xkey
.state
&= ~mask_table
[i
];
272 /* cause the first loop to break; */
274 break; /* get outta here! */
281 STRIP_MODS(e
->xmotion
.state
);
282 /* compress events */
285 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
287 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
288 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
295 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
300 if (e
->xcrossing
.detail
== NotifyInferior
)
304 if (e
->xfocus
.detail
> NotifyNonlinearVirtual
)
308 if (e
->xfocus
.detail
> NotifyNonlinearVirtual
)
310 if (e
->xfocus
.detail
== NotifyInferior
||
311 e
->xfocus
.mode
== NotifyGrab
)
318 static void event_client_dest(ObClient
*client
, gpointer data
)
320 if (client
== focus_in
)
322 if (client
== focus_out
)
324 if (client
== focus_hilite
)
328 static void event_done(gpointer data
)
330 static ObClient
*last
= NULL
;
332 /* sometimes focus_hilite can be on an unfocused window, this make sure
333 it loses its focus hilite when focus moves */
335 (focus_in
&& focus_hilite
!= focus_in
) &&
336 (focus_out
&& focus_hilite
!= focus_out
))
338 frame_adjust_focus(focus_hilite
->frame
, FALSE
);
342 if (focus_in
!= focus_client
) {
343 focus_set_client(focus_in
);
344 frame_adjust_focus(focus_in
->frame
, TRUE
);
345 client_calc_layer(focus_in
);
348 focus_hilite
= focus_in
;
351 if (focus_out
== focus_client
)
352 focus_set_client(NULL
);
353 frame_adjust_focus(focus_out
->frame
, FALSE
);
354 client_calc_layer(focus_out
);
360 if (focus_client
!= last
) {
365 /* is focus anywhere valid? */
366 XGetInputFocus(ob_display
, &w
, &r
);
367 if (!w
|| w
== RootWindow(ob_display
, ob_screen
))
368 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
373 focus_in
= focus_out
= NULL
;
376 static void event_process(const XEvent
*ec
, gpointer data
)
379 ObGroup
*group
= NULL
;
380 ObClient
*client
= NULL
;
382 ObDockApp
*dockapp
= NULL
;
383 ObWindow
*obwin
= NULL
;
386 /* make a copy we can mangle */
390 window
= event_get_window(e
);
391 if (!(e
->type
== PropertyNotify
&&
392 (group
= g_hash_table_lookup(group_map
, &window
))))
393 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
394 switch (obwin
->type
) {
396 dock
= WINDOW_AS_DOCK(obwin
);
399 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
402 client
= WINDOW_AS_CLIENT(obwin
);
405 case Window_Internal
:
406 /* not to be used for events */
407 g_assert_not_reached();
412 event_set_lasttime(e
);
414 if (event_ignore(e
, client
))
417 /* deal with it in the kernel */
419 event_handle_group(group
, e
);
421 event_handle_client(client
, e
);
423 event_handle_dockapp(dockapp
, e
);
425 event_handle_dock(dock
, e
);
426 else if (window
== RootWindow(ob_display
, ob_screen
))
427 event_handle_root(e
);
428 else if (e
->type
== MapRequest
)
429 client_manage(window
);
430 else if (e
->type
== ConfigureRequest
) {
431 /* unhandled configure requests must be used to configure the
435 xwc
.x
= e
->xconfigurerequest
.x
;
436 xwc
.y
= e
->xconfigurerequest
.y
;
437 xwc
.width
= e
->xconfigurerequest
.width
;
438 xwc
.height
= e
->xconfigurerequest
.height
;
439 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
440 xwc
.sibling
= e
->xconfigurerequest
.above
;
441 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
443 /* we are not to be held responsible if someone sends us an
445 xerror_set_ignore(TRUE
);
446 XConfigureWindow(ob_display
, window
,
447 e
->xconfigurerequest
.value_mask
, &xwc
);
448 xerror_set_ignore(FALSE
);
451 /* user input (action-bound) events */
452 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
453 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
454 e
->type
== KeyRelease
)
456 if (menu_frame_visible
)
457 event_handle_menu(e
);
459 if (!keyboard_process_interactive_grab(e
, &client
)) {
460 if (moveresize_in_progress
) {
463 /* make further actions work on the client being
465 client
= moveresize_client
;
468 menu_can_hide
= FALSE
;
469 ob_main_loop_timeout_add(ob_main_loop
,
471 menu_hide_delay_func
,
474 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
475 e
->type
== MotionNotify
)
476 mouse_event(client
, e
);
477 else if (e
->type
== KeyPress
)
478 keyboard_event((focus_cycle_target
? focus_cycle_target
:
479 (focus_hilite
? focus_hilite
: client
)),
486 static void event_handle_root(XEvent
*e
)
492 ob_debug("Another WM has requested to replace us. Exiting.\n");
497 if (e
->xclient
.format
!= 32) break;
499 msgtype
= e
->xclient
.message_type
;
500 if (msgtype
== prop_atoms
.net_current_desktop
) {
501 guint d
= e
->xclient
.data
.l
[0];
502 if (d
< screen_num_desktops
)
503 screen_set_desktop(d
);
504 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
505 guint d
= e
->xclient
.data
.l
[0];
507 screen_set_num_desktops(d
);
508 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
509 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
513 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
514 screen_update_desktop_names();
515 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
516 screen_update_layout();
518 case ConfigureNotify
:
520 XRRUpdateConfiguration(e
);
527 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
528 ob_debug("VIDMODE EVENT\n");
534 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
538 g_assert(e
->type
== PropertyNotify
);
540 for (it
= group
->members
; it
; it
= g_slist_next(it
))
541 event_handle_client(it
->data
, e
);
544 void event_enter_client(ObClient
*client
)
546 g_assert(config_focus_follow
);
548 if (client_normal(client
) && client_can_focus(client
)) {
549 if (config_focus_delay
) {
550 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
551 ob_main_loop_timeout_add(ob_main_loop
,
556 focus_delay_func(client
);
560 static void event_handle_client(ObClient
*client
, XEvent
*e
)
568 case VisibilityNotify
:
569 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
573 /* Wheel buttons don't draw because they are an instant click, so it
574 is a waste of resources to go drawing it. */
575 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
576 con
= frame_context(client
, e
->xbutton
.window
);
577 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
579 case OB_FRAME_CONTEXT_MAXIMIZE
:
580 client
->frame
->max_press
= (e
->type
== ButtonPress
);
581 framerender_frame(client
->frame
);
583 case OB_FRAME_CONTEXT_CLOSE
:
584 client
->frame
->close_press
= (e
->type
== ButtonPress
);
585 framerender_frame(client
->frame
);
587 case OB_FRAME_CONTEXT_ICONIFY
:
588 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
589 framerender_frame(client
->frame
);
591 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
592 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
593 framerender_frame(client
->frame
);
595 case OB_FRAME_CONTEXT_SHADE
:
596 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
597 framerender_frame(client
->frame
);
600 /* nothing changes with clicks for any other contexts */
607 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
608 e
->xfocus
.window
, client
->window
,
609 e
->xfocus
.mode
, e
->xfocus
.detail
);
612 if (focus_out
== client
)
617 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
618 e
->xfocus
.window
, client
->window
,
619 e
->xfocus
.mode
, e
->xfocus
.detail
);
621 if (focus_hilite
== client
|| focus_client
== client
)
623 if (focus_in
== client
)
627 con
= frame_context(client
, e
->xcrossing
.window
);
629 case OB_FRAME_CONTEXT_MAXIMIZE
:
630 client
->frame
->max_hover
= FALSE
;
631 frame_adjust_state(client
->frame
);
633 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
634 client
->frame
->desk_hover
= FALSE
;
635 frame_adjust_state(client
->frame
);
637 case OB_FRAME_CONTEXT_SHADE
:
638 client
->frame
->shade_hover
= FALSE
;
639 frame_adjust_state(client
->frame
);
641 case OB_FRAME_CONTEXT_ICONIFY
:
642 client
->frame
->iconify_hover
= FALSE
;
643 frame_adjust_state(client
->frame
);
645 case OB_FRAME_CONTEXT_CLOSE
:
646 client
->frame
->close_hover
= FALSE
;
647 frame_adjust_state(client
->frame
);
649 case OB_FRAME_CONTEXT_FRAME
:
650 if (config_focus_follow
&& config_focus_delay
)
651 ob_main_loop_timeout_remove_data(ob_main_loop
,
661 gboolean nofocus
= FALSE
;
663 if (ignore_enter_focus
) {
664 ignore_enter_focus
--;
668 con
= frame_context(client
, e
->xcrossing
.window
);
670 case OB_FRAME_CONTEXT_MAXIMIZE
:
671 client
->frame
->max_hover
= TRUE
;
672 frame_adjust_state(client
->frame
);
674 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
675 client
->frame
->desk_hover
= TRUE
;
676 frame_adjust_state(client
->frame
);
678 case OB_FRAME_CONTEXT_SHADE
:
679 client
->frame
->shade_hover
= TRUE
;
680 frame_adjust_state(client
->frame
);
682 case OB_FRAME_CONTEXT_ICONIFY
:
683 client
->frame
->iconify_hover
= TRUE
;
684 frame_adjust_state(client
->frame
);
686 case OB_FRAME_CONTEXT_CLOSE
:
687 client
->frame
->close_hover
= TRUE
;
688 frame_adjust_state(client
->frame
);
690 case OB_FRAME_CONTEXT_FRAME
:
691 if (e
->xcrossing
.mode
== NotifyGrab
||
692 e
->xcrossing
.mode
== NotifyUngrab
)
695 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
696 (e
->type
== EnterNotify
? "Enter" : "Leave"),
698 e
->xcrossing
.detail
, client
?client
->window
:0);
702 ob_debug("%sNotify mode %d detail %d on %lx, "
703 "focusing window: %d\n",
704 (e
->type
== EnterNotify
? "Enter" : "Leave"),
706 e
->xcrossing
.detail
, (client
?client
->window
:0),
709 if (!nofocus
&& config_focus_follow
)
710 event_enter_client(client
);
718 case ConfigureRequest
:
720 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
721 ConfigureRequest
, &ce
)) {
723 /* XXX if this causes bad things.. we can compress config req's
724 with the same mask. */
725 e
->xconfigurerequest
.value_mask
|=
726 ce
.xconfigurerequest
.value_mask
;
727 if (ce
.xconfigurerequest
.value_mask
& CWX
)
728 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
729 if (ce
.xconfigurerequest
.value_mask
& CWY
)
730 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
731 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
732 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
733 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
734 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
735 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
736 e
->xconfigurerequest
.border_width
=
737 ce
.xconfigurerequest
.border_width
;
738 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
739 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
742 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
743 if (client
->iconic
|| client
->shaded
) return;
745 /* resize, then move, as specified in the EWMH section 7.7 */
746 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
752 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
753 client
->border_width
= e
->xconfigurerequest
.border_width
;
755 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
756 e
->xconfigurerequest
.x
: client
->area
.x
;
757 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
758 e
->xconfigurerequest
.y
: client
->area
.y
;
759 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
760 e
->xconfigurerequest
.width
: client
->area
.width
;
761 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
762 e
->xconfigurerequest
.height
: client
->area
.height
;
768 client
->frame
->size
.left
+ client
->frame
->size
.right
;
770 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
771 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
772 client_normal(client
));
773 if (e
->xconfigurerequest
.value_mask
& CWX
)
775 if (e
->xconfigurerequest
.value_mask
& CWY
)
779 switch (client
->gravity
) {
780 case NorthEastGravity
:
782 corner
= OB_CORNER_TOPRIGHT
;
784 case SouthWestGravity
:
786 corner
= OB_CORNER_BOTTOMLEFT
;
788 case SouthEastGravity
:
789 corner
= OB_CORNER_BOTTOMRIGHT
;
791 default: /* NorthWest, Static, etc */
792 corner
= OB_CORNER_TOPLEFT
;
795 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
799 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
800 switch (e
->xconfigurerequest
.detail
) {
803 client_lower(client
);
809 client_raise(client
);
815 if (client
->ignore_unmaps
) {
816 client
->ignore_unmaps
--;
819 client_unmanage(client
);
822 client_unmanage(client
);
825 /* this is when the client is first taken captive in the frame */
826 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
829 This event is quite rare and is usually handled in unmapHandler.
830 However, if the window is unmapped when the reparent event occurs,
831 the window manager never sees it because an unmap event is not sent
832 to an already unmapped window.
835 /* we don't want the reparent event, put it back on the stack for the
836 X server to deal with after we unmanage the window */
837 XPutBackEvent(ob_display
, e
);
839 client_unmanage(client
);
842 ob_debug("MapRequest for 0x%lx\n", client
->window
);
843 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
844 does, we don't want it! */
845 client_activate(client
, FALSE
);
848 /* validate cuz we query stuff off the client here */
849 if (!client_validate(client
)) break;
851 if (e
->xclient
.format
!= 32) return;
853 msgtype
= e
->xclient
.message_type
;
854 if (msgtype
== prop_atoms
.wm_change_state
) {
855 /* compress changes into a single change */
856 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
858 /* XXX: it would be nice to compress ALL messages of a
859 type, not just messages in a row without other
860 message types between. */
861 if (ce
.xclient
.message_type
!= msgtype
) {
862 XPutBackEvent(ob_display
, &ce
);
865 e
->xclient
= ce
.xclient
;
867 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
868 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
869 /* compress changes into a single change */
870 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
872 /* XXX: it would be nice to compress ALL messages of a
873 type, not just messages in a row without other
874 message types between. */
875 if (ce
.xclient
.message_type
!= msgtype
) {
876 XPutBackEvent(ob_display
, &ce
);
879 e
->xclient
= ce
.xclient
;
881 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
882 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
883 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
885 } else if (msgtype
== prop_atoms
.net_wm_state
) {
886 /* can't compress these */
887 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
888 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
889 e
->xclient
.data
.l
[0] == 1 ? "Add" :
890 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
891 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
893 client_set_state(client
, e
->xclient
.data
.l
[0],
894 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
895 } else if (msgtype
== prop_atoms
.net_close_window
) {
896 ob_debug("net_close_window for 0x%lx\n", client
->window
);
897 client_close(client
);
898 } else if (msgtype
== prop_atoms
.net_active_window
) {
899 ob_debug("net_active_window for 0x%lx\n", client
->window
);
900 client_activate(client
, FALSE
);
901 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
902 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
903 if ((Atom
)e
->xclient
.data
.l
[2] ==
904 prop_atoms
.net_wm_moveresize_size_topleft
||
905 (Atom
)e
->xclient
.data
.l
[2] ==
906 prop_atoms
.net_wm_moveresize_size_top
||
907 (Atom
)e
->xclient
.data
.l
[2] ==
908 prop_atoms
.net_wm_moveresize_size_topright
||
909 (Atom
)e
->xclient
.data
.l
[2] ==
910 prop_atoms
.net_wm_moveresize_size_right
||
911 (Atom
)e
->xclient
.data
.l
[2] ==
912 prop_atoms
.net_wm_moveresize_size_right
||
913 (Atom
)e
->xclient
.data
.l
[2] ==
914 prop_atoms
.net_wm_moveresize_size_bottomright
||
915 (Atom
)e
->xclient
.data
.l
[2] ==
916 prop_atoms
.net_wm_moveresize_size_bottom
||
917 (Atom
)e
->xclient
.data
.l
[2] ==
918 prop_atoms
.net_wm_moveresize_size_bottomleft
||
919 (Atom
)e
->xclient
.data
.l
[2] ==
920 prop_atoms
.net_wm_moveresize_size_left
||
921 (Atom
)e
->xclient
.data
.l
[2] ==
922 prop_atoms
.net_wm_moveresize_move
||
923 (Atom
)e
->xclient
.data
.l
[2] ==
924 prop_atoms
.net_wm_moveresize_size_keyboard
||
925 (Atom
)e
->xclient
.data
.l
[2] ==
926 prop_atoms
.net_wm_moveresize_move_keyboard
) {
928 moveresize_start(client
, e
->xclient
.data
.l
[0],
929 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
930 e
->xclient
.data
.l
[2]);
932 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
933 gint oldg
= client
->gravity
;
934 gint tmpg
, x
, y
, w
, h
;
936 if (e
->xclient
.data
.l
[0] & 0xff)
937 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
941 if (e
->xclient
.data
.l
[0] & 1 << 8)
942 x
= e
->xclient
.data
.l
[1];
945 if (e
->xclient
.data
.l
[0] & 1 << 9)
946 y
= e
->xclient
.data
.l
[2];
949 if (e
->xclient
.data
.l
[0] & 1 << 10)
950 w
= e
->xclient
.data
.l
[3];
952 w
= client
->area
.width
;
953 if (e
->xclient
.data
.l
[0] & 1 << 11)
954 h
= e
->xclient
.data
.l
[4];
956 h
= client
->area
.height
;
957 client
->gravity
= tmpg
;
963 client
->frame
->size
.left
+ client
->frame
->size
.right
;
965 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
966 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
967 client_normal(client
));
968 if (e
->xclient
.data
.l
[0] & 1 << 8)
970 if (e
->xclient
.data
.l
[0] & 1 << 9)
974 client_configure(client
, OB_CORNER_TOPLEFT
,
975 x
, y
, w
, h
, FALSE
, TRUE
);
977 client
->gravity
= oldg
;
981 /* validate cuz we query stuff off the client here */
982 if (!client_validate(client
)) break;
984 /* compress changes to a single property into a single change */
985 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
989 /* XXX: it would be nice to compress ALL changes to a property,
990 not just changes in a row without other props between. */
992 a
= ce
.xproperty
.atom
;
993 b
= e
->xproperty
.atom
;
997 if ((a
== prop_atoms
.net_wm_name
||
998 a
== prop_atoms
.wm_name
||
999 a
== prop_atoms
.net_wm_icon_name
||
1000 a
== prop_atoms
.wm_icon_name
)
1002 (b
== prop_atoms
.net_wm_name
||
1003 b
== prop_atoms
.wm_name
||
1004 b
== prop_atoms
.net_wm_icon_name
||
1005 b
== prop_atoms
.wm_icon_name
)) {
1008 if ((a
== prop_atoms
.net_wm_icon
||
1009 a
== prop_atoms
.kwm_win_icon
)
1011 (b
== prop_atoms
.net_wm_icon
||
1012 b
== prop_atoms
.kwm_win_icon
))
1015 XPutBackEvent(ob_display
, &ce
);
1019 msgtype
= e
->xproperty
.atom
;
1020 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1021 client_update_normal_hints(client
);
1022 /* normal hints can make a window non-resizable */
1023 client_setup_decor_and_functions(client
);
1024 } else if (msgtype
== XA_WM_HINTS
) {
1025 client_update_wmhints(client
);
1026 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1027 client_update_transient_for(client
);
1028 client_get_type(client
);
1029 /* type may have changed, so update the layer */
1030 client_calc_layer(client
);
1031 client_setup_decor_and_functions(client
);
1032 } else if (msgtype
== prop_atoms
.net_wm_name
||
1033 msgtype
== prop_atoms
.wm_name
||
1034 msgtype
== prop_atoms
.net_wm_icon_name
||
1035 msgtype
== prop_atoms
.wm_icon_name
) {
1036 client_update_title(client
);
1037 } else if (msgtype
== prop_atoms
.wm_class
) {
1038 client_update_class(client
);
1039 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1040 client_update_protocols(client
);
1041 client_setup_decor_and_functions(client
);
1043 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1044 client_update_strut(client
);
1046 else if (msgtype
== prop_atoms
.net_wm_icon
||
1047 msgtype
== prop_atoms
.kwm_win_icon
) {
1048 client_update_icons(client
);
1050 else if (msgtype
== prop_atoms
.sm_client_id
) {
1051 client_update_sm_client_id(client
);
1056 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1057 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1058 frame_adjust_shape(client
->frame
);
1064 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1068 stacking_raise(DOCK_AS_WINDOW(s
));
1079 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1083 dock_app_drag(app
, &e
->xmotion
);
1086 if (app
->ignore_unmaps
) {
1087 app
->ignore_unmaps
--;
1090 dock_remove(app
, TRUE
);
1093 dock_remove(app
, FALSE
);
1095 case ReparentNotify
:
1096 dock_remove(app
, FALSE
);
1098 case ConfigureNotify
:
1099 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1104 ObMenuFrame
* find_active_menu()
1107 ObMenuFrame
*ret
= NULL
;
1109 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1118 ObMenuFrame
* find_active_or_last_menu()
1120 ObMenuFrame
*ret
= NULL
;
1122 ret
= find_active_menu();
1123 if (!ret
&& menu_frame_visible
)
1124 ret
= menu_frame_visible
->data
;
1128 static void event_handle_menu(XEvent
*ev
)
1131 ObMenuEntryFrame
*e
;
1135 if (menu_can_hide
) {
1136 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1137 ev
->xbutton
.y_root
)))
1138 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1140 menu_frame_hide_all();
1144 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1145 ev
->xmotion
.y_root
))) {
1146 menu_frame_move_on_screen(f
);
1147 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1148 ev
->xmotion
.y_root
)))
1149 menu_frame_select(f
, e
);
1154 a
= find_active_menu();
1156 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1158 menu_frame_select(a
, NULL
);
1163 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1164 menu_frame_hide_all();
1165 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1167 if ((f
= find_active_menu()))
1168 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1169 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1171 if ((f
= find_active_or_last_menu()) && f
->parent
)
1172 menu_frame_select(f
, NULL
);
1173 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1175 if ((f
= find_active_or_last_menu()) && f
->child
)
1176 menu_frame_select_next(f
->child
);
1177 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1179 if ((f
= find_active_or_last_menu()))
1180 menu_frame_select_previous(f
);
1181 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1183 if ((f
= find_active_or_last_menu()))
1184 menu_frame_select_next(f
);
1190 static gboolean
menu_hide_delay_func(gpointer data
)
1192 menu_can_hide
= TRUE
;
1193 return FALSE
; /* no repeat */
1196 static gboolean
focus_delay_func(gpointer data
)
1200 if (focus_client
!= c
) {
1202 if (config_focus_raise
)
1205 return FALSE
; /* no repeat */
1208 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1210 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
, client
);
1213 void event_halt_focus_delay()
1215 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1218 void event_ignore_queued_enters()
1220 GSList
*saved
= NULL
, *it
;
1223 XSync(ob_display
, FALSE
);
1225 /* count the events */
1227 e
= g_new(XEvent
, 1);
1228 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1231 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1232 if (win
&& WINDOW_IS_CLIENT(win
))
1233 ++ignore_enter_focus
;
1235 saved
= g_slist_append(saved
, e
);
1241 /* put the events back */
1242 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1243 XPutBackEvent(ob_display
, it
->data
);
1246 g_slist_free(saved
);