1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 event.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003 Ben Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
32 #include "menuframe.h"
36 #include "framerender.h"
38 #include "moveresize.h"
41 #include "extensions.h"
44 #include <X11/keysym.h>
45 #include <X11/Xatom.h>
48 #ifdef HAVE_SYS_SELECT_H
49 # include <sys/select.h>
55 # include <X11/XKBlib.h>
59 #include <X11/ICE/ICElib.h>
67 static void event_process(const XEvent
*e
, gpointer data
);
68 static void event_client_dest(ObClient
*client
, gpointer data
);
69 static void event_handle_root(XEvent
*e
);
70 static void event_handle_menu(XEvent
*e
);
71 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
72 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
73 static void event_handle_client(ObClient
*c
, XEvent
*e
);
74 static void event_handle_group(ObGroup
*g
, XEvent
*e
);
76 static gboolean
focus_delay_func(gpointer data
);
77 static void focus_delay_client_dest(ObClient
*client
, gpointer data
);
79 static gboolean
menu_hide_delay_func(gpointer data
);
81 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
82 (e)->xfocus.detail == NotifyAncestor || \
83 (e)->xfocus.detail > NotifyNonlinearVirtual)
84 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
85 (e)->xfocus.detail == NotifyInferior || \
86 (e)->xfocus.detail == NotifyAncestor || \
87 (e)->xfocus.detail > NotifyNonlinearVirtual)
89 Time event_lasttime
= 0;
91 /*! The value of the mask for the NumLock modifier */
93 /*! The value of the mask for the ScrollLock modifier */
95 /*! The key codes for the modifier keys */
96 static XModifierKeymap
*modmap
;
97 /*! Table of the constant modifier masks */
98 static const gint mask_table
[] = {
99 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
100 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
102 static gint mask_table_size
;
104 static guint ignore_enter_focus
= 0;
106 static gboolean menu_can_hide
;
109 static void ice_handler(gint fd
, gpointer conn
)
112 IceProcessMessages(conn
, NULL
, &b
);
115 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
116 IcePointer
*watch_data
)
121 fd
= IceConnectionNumber(conn
);
122 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
124 ob_main_loop_fd_remove(ob_main_loop
, fd
);
130 void event_startup(gboolean reconfig
)
132 if (reconfig
) return;
134 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
136 /* get lock masks that are defined by the display (not constant) */
137 modmap
= XGetModifierMapping(ob_display
);
139 if (modmap
&& modmap
->max_keypermod
> 0) {
141 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
142 /* get the values of the keyboard lock modifiers
143 Note: Caps lock is not retrieved the same way as Scroll and Num
144 lock since it doesn't need to be. */
145 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
146 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
149 for (cnt
= 0; cnt
< size
; ++cnt
) {
150 if (! modmap
->modifiermap
[cnt
]) continue;
152 if (num_lock
== modmap
->modifiermap
[cnt
])
153 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
154 if (scroll_lock
== modmap
->modifiermap
[cnt
])
155 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
159 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
162 IceAddConnectionWatch(ice_watch
, NULL
);
165 client_add_destructor(focus_delay_client_dest
, NULL
);
166 client_add_destructor(event_client_dest
, NULL
);
169 void event_shutdown(gboolean reconfig
)
171 if (reconfig
) return;
174 IceRemoveConnectionWatch(ice_watch
, NULL
);
177 client_remove_destructor(focus_delay_client_dest
);
178 client_remove_destructor(event_client_dest
);
179 XFreeModifiermap(modmap
);
182 static Window
event_get_window(XEvent
*e
)
189 window
= RootWindow(ob_display
, ob_screen
);
192 window
= e
->xmap
.window
;
195 window
= e
->xunmap
.window
;
198 window
= e
->xdestroywindow
.window
;
200 case ConfigureRequest
:
201 window
= e
->xconfigurerequest
.window
;
203 case ConfigureNotify
:
204 window
= e
->xconfigure
.window
;
208 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
209 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
211 window
= ((XkbBellNotifyEvent
*)e
)->window
;
217 window
= e
->xany
.window
;
222 static void event_set_lasttime(XEvent
*e
)
226 /* grab the lasttime and hack up the state */
242 t
= e
->xproperty
.time
;
246 t
= e
->xcrossing
.time
;
249 /* if more event types are anticipated, get their timestamp
254 if (t
> event_lasttime
)
258 #define STRIP_MODS(s) \
259 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
260 /* kill off the Button1Mask etc, only want the modifiers */ \
261 s &= (ControlMask | ShiftMask | Mod1Mask | \
262 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
264 static void event_hack_mods(XEvent *e)
267 XkbStateRec xkb_state
;
275 STRIP_MODS(e
->xbutton
.state
);
278 STRIP_MODS(e
->xkey
.state
);
281 STRIP_MODS(e
->xkey
.state
);
282 /* remove from the state the mask of the modifier being released, if
283 it is a modifier key being released (this is a little ugly..) */
285 if (XkbGetState(ob_display
, XkbUseCoreKbd
, &xkb_state
) == Success
) {
286 e
->xkey
.state
= xkb_state
.compat_state
;
290 kp
= modmap
->modifiermap
;
291 for (i
= 0; i
< mask_table_size
; ++i
) {
292 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
293 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
294 /* remove the mask for it */
295 e
->xkey
.state
&= ~mask_table
[i
];
296 /* cause the first loop to break; */
298 break; /* get outta here! */
305 STRIP_MODS(e
->xmotion
.state
);
306 /* compress events */
309 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
311 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
312 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
319 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
324 if (e
->xcrossing
.detail
== NotifyInferior
)
328 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
329 because of RevertToPointerRoot. If the focus ends up reverting to
330 pointer root on a workspace change, then the FocusIn event that we
331 want will be of type NotifyAncestor. This situation does not occur
332 for FocusOut, so it is safely ignored there.
334 if (INVALID_FOCUSIN(e
) ||
337 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
338 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
340 /* says a client was not found for the event (or a valid FocusIn
343 e
->xfocus
.window
= None
;
348 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
349 e
->xfocus
.mode
, e
->xfocus
.detail
);
353 if (INVALID_FOCUSOUT(e
)) {
355 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
356 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
362 ob_debug("FocusOut on %lx mode %d detail %d\n",
363 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
367 gboolean fallback
= TRUE
;
370 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
372 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
374 if (fe
.type
== FocusOut
) {
376 ob_debug("found pending FocusOut\n");
378 if (!INVALID_FOCUSOUT(&fe
)) {
379 /* if there is a VALID FocusOut still coming, don't
380 fallback focus yet, we'll deal with it then */
381 XPutBackEvent(ob_display
, &fe
);
387 ob_debug("found pending FocusIn\n");
389 /* is the focused window getting a FocusOut/In back to
392 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
393 !event_ignore(&fe
, client
)) {
395 if focus_client is not set, then we can't do
396 this. we need the FocusIn. This happens in the
397 case when the set_focus_client(NULL) in the
398 focus_fallback function fires and then
399 focus_fallback picks the currently focused
400 window (such as on a SendToDesktop-esque action.
404 ob_debug("focused window got an Out/In back to "
405 "itself IGNORED both\n");
409 event_process(&fe
, NULL
);
411 ob_debug("focused window got an Out/In back to "
412 "itself but focus_client was null "
413 "IGNORED just the Out\n");
422 /* once all the FocusOut's have been dealt with, if
423 there is a FocusIn still left and it is valid, then
425 event_process(&fe
, &d
);
428 ob_debug("FocusIn was OK, so don't fallback\n");
438 ob_debug("no valid FocusIn and no FocusOut events found, "
441 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
449 static void event_process(const XEvent
*ec
, gpointer data
)
452 ObGroup
*group
= NULL
;
453 ObClient
*client
= NULL
;
455 ObDockApp
*dockapp
= NULL
;
456 ObWindow
*obwin
= NULL
;
458 ObEventData
*ed
= data
;
460 /* make a copy we can mangle */
464 window
= event_get_window(e
);
465 if (!(e
->type
== PropertyNotify
&&
466 (group
= g_hash_table_lookup(group_map
, &window
))))
467 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
468 switch (obwin
->type
) {
470 dock
= WINDOW_AS_DOCK(obwin
);
473 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
476 client
= WINDOW_AS_CLIENT(obwin
);
479 case Window_Internal
:
480 /* not to be used for events */
481 g_assert_not_reached();
486 event_set_lasttime(e
);
488 if (event_ignore(e
, client
)) {
495 /* deal with it in the kernel */
497 event_handle_group(group
, e
);
499 event_handle_client(client
, e
);
501 event_handle_dockapp(dockapp
, e
);
503 event_handle_dock(dock
, e
);
504 else if (window
== RootWindow(ob_display
, ob_screen
))
505 event_handle_root(e
);
506 else if (e
->type
== MapRequest
)
507 client_manage(window
);
508 else if (e
->type
== ConfigureRequest
) {
509 /* unhandled configure requests must be used to configure the
513 xwc
.x
= e
->xconfigurerequest
.x
;
514 xwc
.y
= e
->xconfigurerequest
.y
;
515 xwc
.width
= e
->xconfigurerequest
.width
;
516 xwc
.height
= e
->xconfigurerequest
.height
;
517 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
518 xwc
.sibling
= e
->xconfigurerequest
.above
;
519 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
521 /* we are not to be held responsible if someone sends us an
523 xerror_set_ignore(TRUE
);
524 XConfigureWindow(ob_display
, window
,
525 e
->xconfigurerequest
.value_mask
, &xwc
);
526 xerror_set_ignore(FALSE
);
529 /* user input (action-bound) events */
530 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
531 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
532 e
->type
== KeyRelease
)
534 if (menu_frame_visible
)
535 event_handle_menu(e
);
537 if (!keyboard_process_interactive_grab(e
, &client
)) {
538 if (moveresize_in_progress
) {
541 /* make further actions work on the client being
543 client
= moveresize_client
;
546 menu_can_hide
= FALSE
;
547 ob_main_loop_timeout_add(ob_main_loop
,
548 config_menu_hide_delay
* 1000,
549 menu_hide_delay_func
,
552 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
553 e
->type
== MotionNotify
)
554 mouse_event(client
, e
);
555 else if (e
->type
== KeyPress
)
556 keyboard_event((focus_cycle_target
? focus_cycle_target
:
557 (focus_hilite
? focus_hilite
: client
)),
564 static void event_handle_root(XEvent
*e
)
570 ob_debug("Another WM has requested to replace us. Exiting.\n");
575 if (e
->xclient
.format
!= 32) break;
577 msgtype
= e
->xclient
.message_type
;
578 if (msgtype
== prop_atoms
.net_current_desktop
) {
579 guint d
= e
->xclient
.data
.l
[0];
580 if (d
< screen_num_desktops
)
581 screen_set_desktop(d
);
582 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
583 guint d
= e
->xclient
.data
.l
[0];
585 screen_set_num_desktops(d
);
586 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
587 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
591 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
592 screen_update_desktop_names();
593 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
594 screen_update_layout();
596 case ConfigureNotify
:
598 XRRUpdateConfiguration(e
);
607 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
611 g_assert(e
->type
== PropertyNotify
);
613 for (it
= group
->members
; it
; it
= g_slist_next(it
))
614 event_handle_client(it
->data
, e
);
617 void event_enter_client(ObClient
*client
)
619 g_assert(config_focus_follow
);
621 if (client_normal(client
) && client_can_focus(client
)) {
622 if (config_focus_delay
) {
623 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
624 ob_main_loop_timeout_add(ob_main_loop
,
629 focus_delay_func(client
);
633 static void event_handle_client(ObClient
*client
, XEvent
*e
)
641 case VisibilityNotify
:
642 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
646 /* Wheel buttons don't draw because they are an instant click, so it
647 is a waste of resources to go drawing it. */
648 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
649 con
= frame_context(client
, e
->xbutton
.window
);
650 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
652 case OB_FRAME_CONTEXT_MAXIMIZE
:
653 client
->frame
->max_press
= (e
->type
== ButtonPress
);
654 framerender_frame(client
->frame
);
656 case OB_FRAME_CONTEXT_CLOSE
:
657 client
->frame
->close_press
= (e
->type
== ButtonPress
);
658 framerender_frame(client
->frame
);
660 case OB_FRAME_CONTEXT_ICONIFY
:
661 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
662 framerender_frame(client
->frame
);
664 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
665 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
666 framerender_frame(client
->frame
);
668 case OB_FRAME_CONTEXT_SHADE
:
669 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
670 framerender_frame(client
->frame
);
673 /* nothing changes with clicks for any other contexts */
680 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
681 e
->xfocus
.window
, client
->window
,
682 e
->xfocus
.mode
, e
->xfocus
.detail
);
684 if (client
!= focus_client
) {
685 focus_set_client(client
);
686 frame_adjust_focus(client
->frame
, TRUE
);
687 client_calc_layer(client
);
692 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
693 e
->xfocus
.window
, client
->window
,
694 e
->xfocus
.mode
, e
->xfocus
.detail
);
697 frame_adjust_focus(client
->frame
, FALSE
);
698 client_calc_layer(client
);
701 con
= frame_context(client
, e
->xcrossing
.window
);
703 case OB_FRAME_CONTEXT_MAXIMIZE
:
704 client
->frame
->max_hover
= FALSE
;
705 frame_adjust_state(client
->frame
);
707 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
708 client
->frame
->desk_hover
= FALSE
;
709 frame_adjust_state(client
->frame
);
711 case OB_FRAME_CONTEXT_SHADE
:
712 client
->frame
->shade_hover
= FALSE
;
713 frame_adjust_state(client
->frame
);
715 case OB_FRAME_CONTEXT_ICONIFY
:
716 client
->frame
->iconify_hover
= FALSE
;
717 frame_adjust_state(client
->frame
);
719 case OB_FRAME_CONTEXT_CLOSE
:
720 client
->frame
->close_hover
= FALSE
;
721 frame_adjust_state(client
->frame
);
723 case OB_FRAME_CONTEXT_FRAME
:
724 if (config_focus_follow
&& config_focus_delay
)
725 ob_main_loop_timeout_remove_data(ob_main_loop
,
735 gboolean nofocus
= FALSE
;
737 if (ignore_enter_focus
) {
738 ignore_enter_focus
--;
742 con
= frame_context(client
, e
->xcrossing
.window
);
744 case OB_FRAME_CONTEXT_MAXIMIZE
:
745 client
->frame
->max_hover
= TRUE
;
746 frame_adjust_state(client
->frame
);
748 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
749 client
->frame
->desk_hover
= TRUE
;
750 frame_adjust_state(client
->frame
);
752 case OB_FRAME_CONTEXT_SHADE
:
753 client
->frame
->shade_hover
= TRUE
;
754 frame_adjust_state(client
->frame
);
756 case OB_FRAME_CONTEXT_ICONIFY
:
757 client
->frame
->iconify_hover
= TRUE
;
758 frame_adjust_state(client
->frame
);
760 case OB_FRAME_CONTEXT_CLOSE
:
761 client
->frame
->close_hover
= TRUE
;
762 frame_adjust_state(client
->frame
);
764 case OB_FRAME_CONTEXT_FRAME
:
765 if (e
->xcrossing
.mode
== NotifyGrab
||
766 e
->xcrossing
.mode
== NotifyUngrab
)
769 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
770 (e
->type
== EnterNotify
? "Enter" : "Leave"),
772 e
->xcrossing
.detail
, client
?client
->window
:0);
776 ob_debug("%sNotify mode %d detail %d on %lx, "
777 "focusing window: %d\n",
778 (e
->type
== EnterNotify
? "Enter" : "Leave"),
780 e
->xcrossing
.detail
, (client
?client
->window
:0),
783 if (!nofocus
&& config_focus_follow
)
784 event_enter_client(client
);
792 case ConfigureRequest
:
794 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
795 ConfigureRequest
, &ce
)) {
797 /* XXX if this causes bad things.. we can compress config req's
798 with the same mask. */
799 e
->xconfigurerequest
.value_mask
|=
800 ce
.xconfigurerequest
.value_mask
;
801 if (ce
.xconfigurerequest
.value_mask
& CWX
)
802 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
803 if (ce
.xconfigurerequest
.value_mask
& CWY
)
804 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
805 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
806 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
807 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
808 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
809 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
810 e
->xconfigurerequest
.border_width
=
811 ce
.xconfigurerequest
.border_width
;
812 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
813 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
816 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
817 if (client
->iconic
|| client
->shaded
) return;
819 /* resize, then move, as specified in the EWMH section 7.7 */
820 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
826 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
827 client
->border_width
= e
->xconfigurerequest
.border_width
;
829 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
830 e
->xconfigurerequest
.x
: client
->area
.x
;
831 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
832 e
->xconfigurerequest
.y
: client
->area
.y
;
833 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
834 e
->xconfigurerequest
.width
: client
->area
.width
;
835 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
836 e
->xconfigurerequest
.height
: client
->area
.height
;
842 client
->frame
->size
.left
+ client
->frame
->size
.right
;
844 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
845 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
846 client_normal(client
));
847 if (e
->xconfigurerequest
.value_mask
& CWX
)
849 if (e
->xconfigurerequest
.value_mask
& CWY
)
853 switch (client
->gravity
) {
854 case NorthEastGravity
:
856 corner
= OB_CORNER_TOPRIGHT
;
858 case SouthWestGravity
:
860 corner
= OB_CORNER_BOTTOMLEFT
;
862 case SouthEastGravity
:
863 corner
= OB_CORNER_BOTTOMRIGHT
;
865 default: /* NorthWest, Static, etc */
866 corner
= OB_CORNER_TOPLEFT
;
869 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
873 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
874 switch (e
->xconfigurerequest
.detail
) {
877 client_lower(client
);
883 client_raise(client
);
889 if (client
->ignore_unmaps
) {
890 client
->ignore_unmaps
--;
893 client_unmanage(client
);
896 client_unmanage(client
);
899 /* this is when the client is first taken captive in the frame */
900 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
903 This event is quite rare and is usually handled in unmapHandler.
904 However, if the window is unmapped when the reparent event occurs,
905 the window manager never sees it because an unmap event is not sent
906 to an already unmapped window.
909 /* we don't want the reparent event, put it back on the stack for the
910 X server to deal with after we unmanage the window */
911 XPutBackEvent(ob_display
, e
);
913 client_unmanage(client
);
916 ob_debug("MapRequest for 0x%lx\n", client
->window
);
917 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
918 does, we don't want it!
919 it can happen now when the window is on
920 another desktop, but we still don't
922 client_activate(client
, FALSE
);
925 /* validate cuz we query stuff off the client here */
926 if (!client_validate(client
)) break;
928 if (e
->xclient
.format
!= 32) return;
930 msgtype
= e
->xclient
.message_type
;
931 if (msgtype
== prop_atoms
.wm_change_state
) {
932 /* compress changes into a single change */
933 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
935 /* XXX: it would be nice to compress ALL messages of a
936 type, not just messages in a row without other
937 message types between. */
938 if (ce
.xclient
.message_type
!= msgtype
) {
939 XPutBackEvent(ob_display
, &ce
);
942 e
->xclient
= ce
.xclient
;
944 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
945 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
946 /* compress changes into a single change */
947 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
949 /* XXX: it would be nice to compress ALL messages of a
950 type, not just messages in a row without other
951 message types between. */
952 if (ce
.xclient
.message_type
!= msgtype
) {
953 XPutBackEvent(ob_display
, &ce
);
956 e
->xclient
= ce
.xclient
;
958 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
959 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
960 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
962 } else if (msgtype
== prop_atoms
.net_wm_state
) {
963 /* can't compress these */
964 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
965 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
966 e
->xclient
.data
.l
[0] == 1 ? "Add" :
967 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
968 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
970 client_set_state(client
, e
->xclient
.data
.l
[0],
971 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
972 } else if (msgtype
== prop_atoms
.net_close_window
) {
973 ob_debug("net_close_window for 0x%lx\n", client
->window
);
974 client_close(client
);
975 } else if (msgtype
== prop_atoms
.net_active_window
) {
976 ob_debug("net_active_window for 0x%lx\n", client
->window
);
977 client_activate(client
, FALSE
);
978 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
979 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
980 if ((Atom
)e
->xclient
.data
.l
[2] ==
981 prop_atoms
.net_wm_moveresize_size_topleft
||
982 (Atom
)e
->xclient
.data
.l
[2] ==
983 prop_atoms
.net_wm_moveresize_size_top
||
984 (Atom
)e
->xclient
.data
.l
[2] ==
985 prop_atoms
.net_wm_moveresize_size_topright
||
986 (Atom
)e
->xclient
.data
.l
[2] ==
987 prop_atoms
.net_wm_moveresize_size_right
||
988 (Atom
)e
->xclient
.data
.l
[2] ==
989 prop_atoms
.net_wm_moveresize_size_right
||
990 (Atom
)e
->xclient
.data
.l
[2] ==
991 prop_atoms
.net_wm_moveresize_size_bottomright
||
992 (Atom
)e
->xclient
.data
.l
[2] ==
993 prop_atoms
.net_wm_moveresize_size_bottom
||
994 (Atom
)e
->xclient
.data
.l
[2] ==
995 prop_atoms
.net_wm_moveresize_size_bottomleft
||
996 (Atom
)e
->xclient
.data
.l
[2] ==
997 prop_atoms
.net_wm_moveresize_size_left
||
998 (Atom
)e
->xclient
.data
.l
[2] ==
999 prop_atoms
.net_wm_moveresize_move
||
1000 (Atom
)e
->xclient
.data
.l
[2] ==
1001 prop_atoms
.net_wm_moveresize_size_keyboard
||
1002 (Atom
)e
->xclient
.data
.l
[2] ==
1003 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1005 moveresize_start(client
, e
->xclient
.data
.l
[0],
1006 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1007 e
->xclient
.data
.l
[2]);
1009 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1010 gint oldg
= client
->gravity
;
1011 gint tmpg
, x
, y
, w
, h
;
1013 if (e
->xclient
.data
.l
[0] & 0xff)
1014 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
1018 if (e
->xclient
.data
.l
[0] & 1 << 8)
1019 x
= e
->xclient
.data
.l
[1];
1022 if (e
->xclient
.data
.l
[0] & 1 << 9)
1023 y
= e
->xclient
.data
.l
[2];
1026 if (e
->xclient
.data
.l
[0] & 1 << 10)
1027 w
= e
->xclient
.data
.l
[3];
1029 w
= client
->area
.width
;
1030 if (e
->xclient
.data
.l
[0] & 1 << 11)
1031 h
= e
->xclient
.data
.l
[4];
1033 h
= client
->area
.height
;
1034 client
->gravity
= tmpg
;
1040 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1042 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1043 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1044 client_normal(client
));
1045 if (e
->xclient
.data
.l
[0] & 1 << 8)
1047 if (e
->xclient
.data
.l
[0] & 1 << 9)
1051 client_configure(client
, OB_CORNER_TOPLEFT
,
1052 x
, y
, w
, h
, FALSE
, TRUE
);
1054 client
->gravity
= oldg
;
1057 case PropertyNotify
:
1058 /* validate cuz we query stuff off the client here */
1059 if (!client_validate(client
)) break;
1061 /* compress changes to a single property into a single change */
1062 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1066 /* XXX: it would be nice to compress ALL changes to a property,
1067 not just changes in a row without other props between. */
1069 a
= ce
.xproperty
.atom
;
1070 b
= e
->xproperty
.atom
;
1074 if ((a
== prop_atoms
.net_wm_name
||
1075 a
== prop_atoms
.wm_name
||
1076 a
== prop_atoms
.net_wm_icon_name
||
1077 a
== prop_atoms
.wm_icon_name
)
1079 (b
== prop_atoms
.net_wm_name
||
1080 b
== prop_atoms
.wm_name
||
1081 b
== prop_atoms
.net_wm_icon_name
||
1082 b
== prop_atoms
.wm_icon_name
)) {
1085 if ((a
== prop_atoms
.net_wm_icon
||
1086 a
== prop_atoms
.kwm_win_icon
)
1088 (b
== prop_atoms
.net_wm_icon
||
1089 b
== prop_atoms
.kwm_win_icon
))
1092 XPutBackEvent(ob_display
, &ce
);
1096 msgtype
= e
->xproperty
.atom
;
1097 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1098 client_update_normal_hints(client
);
1099 /* normal hints can make a window non-resizable */
1100 client_setup_decor_and_functions(client
);
1101 } else if (msgtype
== XA_WM_HINTS
) {
1102 client_update_wmhints(client
);
1103 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1104 client_update_transient_for(client
);
1105 client_get_type(client
);
1106 /* type may have changed, so update the layer */
1107 client_calc_layer(client
);
1108 client_setup_decor_and_functions(client
);
1109 } else if (msgtype
== prop_atoms
.net_wm_name
||
1110 msgtype
== prop_atoms
.wm_name
||
1111 msgtype
== prop_atoms
.net_wm_icon_name
||
1112 msgtype
== prop_atoms
.wm_icon_name
) {
1113 client_update_title(client
);
1114 } else if (msgtype
== prop_atoms
.wm_class
) {
1115 client_update_class(client
);
1116 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1117 client_update_protocols(client
);
1118 client_setup_decor_and_functions(client
);
1120 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1121 client_update_strut(client
);
1123 else if (msgtype
== prop_atoms
.net_wm_icon
||
1124 msgtype
== prop_atoms
.kwm_win_icon
) {
1125 client_update_icons(client
);
1127 else if (msgtype
== prop_atoms
.sm_client_id
) {
1128 client_update_sm_client_id(client
);
1133 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1134 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1135 frame_adjust_shape(client
->frame
);
1141 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1145 if (e
->xbutton
.button
== 1)
1146 stacking_raise(DOCK_AS_WINDOW(s
), FALSE
);
1147 else if (e
->xbutton
.button
== 2)
1148 stacking_lower(DOCK_AS_WINDOW(s
), FALSE
);
1159 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1163 dock_app_drag(app
, &e
->xmotion
);
1166 if (app
->ignore_unmaps
) {
1167 app
->ignore_unmaps
--;
1170 dock_remove(app
, TRUE
);
1173 dock_remove(app
, FALSE
);
1175 case ReparentNotify
:
1176 dock_remove(app
, FALSE
);
1178 case ConfigureNotify
:
1179 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1184 ObMenuFrame
* find_active_menu()
1187 ObMenuFrame
*ret
= NULL
;
1189 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1198 ObMenuFrame
* find_active_or_last_menu()
1200 ObMenuFrame
*ret
= NULL
;
1202 ret
= find_active_menu();
1203 if (!ret
&& menu_frame_visible
)
1204 ret
= menu_frame_visible
->data
;
1208 static void event_handle_menu(XEvent
*ev
)
1211 ObMenuEntryFrame
*e
;
1215 if (menu_can_hide
) {
1216 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1217 ev
->xbutton
.y_root
)))
1218 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1220 menu_frame_hide_all();
1224 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1225 ev
->xmotion
.y_root
))) {
1226 menu_frame_move_on_screen(f
);
1227 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1228 ev
->xmotion
.y_root
)))
1229 menu_frame_select(f
, e
);
1234 a
= find_active_menu();
1236 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1238 menu_frame_select(a
, NULL
);
1243 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1244 menu_frame_hide_all();
1245 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1247 if ((f
= find_active_menu()))
1248 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1249 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1251 if ((f
= find_active_or_last_menu()) && f
->parent
)
1252 menu_frame_select(f
, NULL
);
1253 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1255 if ((f
= find_active_or_last_menu()) && f
->child
)
1256 menu_frame_select_next(f
->child
);
1257 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1259 if ((f
= find_active_or_last_menu()))
1260 menu_frame_select_previous(f
);
1261 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1263 if ((f
= find_active_or_last_menu()))
1264 menu_frame_select_next(f
);
1270 static gboolean
menu_hide_delay_func(gpointer data
)
1272 menu_can_hide
= TRUE
;
1273 return FALSE
; /* no repeat */
1276 static gboolean
focus_delay_func(gpointer data
)
1280 if (focus_client
!= c
) {
1282 if (config_focus_raise
)
1285 return FALSE
; /* no repeat */
1288 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1290 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1294 static void event_client_dest(ObClient
*client
, gpointer data
)
1296 if (client
== focus_hilite
)
1297 focus_hilite
= NULL
;
1300 void event_halt_focus_delay()
1302 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1305 void event_ignore_queued_enters()
1307 GSList
*saved
= NULL
, *it
;
1310 XSync(ob_display
, FALSE
);
1312 /* count the events */
1314 e
= g_new(XEvent
, 1);
1315 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1318 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1319 if (win
&& WINDOW_IS_CLIENT(win
))
1320 ++ignore_enter_focus
;
1322 saved
= g_slist_append(saved
, e
);
1328 /* put the events back */
1329 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1330 XPutBackEvent(ob_display
, it
->data
);
1333 g_slist_free(saved
);