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 /* The most recent time at which an event with a timestamp occured. */
90 static Time event_lasttime
= 0;
91 /* The time for the current event being processed
92 (it's the event_lasttime for events without times, if this is a bug then
93 use CurrentTime instead, but it seems ok) */
94 Time event_curtime
= CurrentTime
;
96 /*! The value of the mask for the NumLock modifier */
98 /*! The value of the mask for the ScrollLock modifier */
100 /*! The key codes for the modifier keys */
101 static XModifierKeymap
*modmap
;
102 /*! Table of the constant modifier masks */
103 static const gint mask_table
[] = {
104 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
105 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
107 static gint mask_table_size
;
109 static guint ignore_enter_focus
= 0;
111 static gboolean menu_can_hide
;
114 static void ice_handler(gint fd
, gpointer conn
)
117 IceProcessMessages(conn
, NULL
, &b
);
120 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
121 IcePointer
*watch_data
)
126 fd
= IceConnectionNumber(conn
);
127 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
129 ob_main_loop_fd_remove(ob_main_loop
, fd
);
135 void event_startup(gboolean reconfig
)
137 if (reconfig
) return;
139 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
141 /* get lock masks that are defined by the display (not constant) */
142 modmap
= XGetModifierMapping(ob_display
);
144 if (modmap
&& modmap
->max_keypermod
> 0) {
146 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
147 /* get the values of the keyboard lock modifiers
148 Note: Caps lock is not retrieved the same way as Scroll and Num
149 lock since it doesn't need to be. */
150 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
151 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
154 for (cnt
= 0; cnt
< size
; ++cnt
) {
155 if (! modmap
->modifiermap
[cnt
]) continue;
157 if (num_lock
== modmap
->modifiermap
[cnt
])
158 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
159 if (scroll_lock
== modmap
->modifiermap
[cnt
])
160 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
164 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
167 IceAddConnectionWatch(ice_watch
, NULL
);
170 client_add_destructor(focus_delay_client_dest
, NULL
);
171 client_add_destructor(event_client_dest
, NULL
);
174 void event_shutdown(gboolean reconfig
)
176 if (reconfig
) return;
179 IceRemoveConnectionWatch(ice_watch
, NULL
);
182 client_remove_destructor(focus_delay_client_dest
);
183 client_remove_destructor(event_client_dest
);
184 XFreeModifiermap(modmap
);
187 static Window
event_get_window(XEvent
*e
)
194 window
= RootWindow(ob_display
, ob_screen
);
197 window
= e
->xmap
.window
;
200 window
= e
->xunmap
.window
;
203 window
= e
->xdestroywindow
.window
;
205 case ConfigureRequest
:
206 window
= e
->xconfigurerequest
.window
;
208 case ConfigureNotify
:
209 window
= e
->xconfigure
.window
;
213 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
214 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
216 window
= ((XkbBellNotifyEvent
*)e
)->window
;
222 window
= e
->xany
.window
;
227 static void event_set_lasttime(XEvent
*e
)
231 /* grab the lasttime and hack up the state */
247 t
= e
->xproperty
.time
;
251 t
= e
->xcrossing
.time
;
254 /* if more event types are anticipated, get their timestamp
259 if (t
> event_lasttime
) {
261 event_curtime
= event_lasttime
;
263 event_curtime
= event_lasttime
;
269 #define STRIP_MODS(s) \
270 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
271 /* kill off the Button1Mask etc, only want the modifiers */ \
272 s &= (ControlMask | ShiftMask | Mod1Mask | \
273 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
275 static void event_hack_mods(XEvent *e)
278 XkbStateRec xkb_state
;
286 STRIP_MODS(e
->xbutton
.state
);
289 STRIP_MODS(e
->xkey
.state
);
292 STRIP_MODS(e
->xkey
.state
);
293 /* remove from the state the mask of the modifier being released, if
294 it is a modifier key being released (this is a little ugly..) */
296 if (XkbGetState(ob_display
, XkbUseCoreKbd
, &xkb_state
) == Success
) {
297 e
->xkey
.state
= xkb_state
.compat_state
;
301 kp
= modmap
->modifiermap
;
302 for (i
= 0; i
< mask_table_size
; ++i
) {
303 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
304 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
305 /* remove the mask for it */
306 e
->xkey
.state
&= ~mask_table
[i
];
307 /* cause the first loop to break; */
309 break; /* get outta here! */
316 STRIP_MODS(e
->xmotion
.state
);
317 /* compress events */
320 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
322 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
323 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
330 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
335 if (e
->xcrossing
.detail
== NotifyInferior
)
339 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
340 because of RevertToPointerRoot. If the focus ends up reverting to
341 pointer root on a workspace change, then the FocusIn event that we
342 want will be of type NotifyAncestor. This situation does not occur
343 for FocusOut, so it is safely ignored there.
345 if (INVALID_FOCUSIN(e
) ||
348 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
349 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
351 /* says a client was not found for the event (or a valid FocusIn
354 e
->xfocus
.window
= None
;
359 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
360 e
->xfocus
.mode
, e
->xfocus
.detail
);
364 if (INVALID_FOCUSOUT(e
)) {
366 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
367 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
373 ob_debug("FocusOut on %lx mode %d detail %d\n",
374 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
378 gboolean fallback
= TRUE
;
381 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
383 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
385 if (fe
.type
== FocusOut
) {
387 ob_debug("found pending FocusOut\n");
389 if (!INVALID_FOCUSOUT(&fe
)) {
390 /* if there is a VALID FocusOut still coming, don't
391 fallback focus yet, we'll deal with it then */
392 XPutBackEvent(ob_display
, &fe
);
398 ob_debug("found pending FocusIn\n");
400 /* is the focused window getting a FocusOut/In back to
403 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
404 !event_ignore(&fe
, client
)) {
406 if focus_client is not set, then we can't do
407 this. we need the FocusIn. This happens in the
408 case when the set_focus_client(NULL) in the
409 focus_fallback function fires and then
410 focus_fallback picks the currently focused
411 window (such as on a SendToDesktop-esque action.
415 ob_debug("focused window got an Out/In back to "
416 "itself IGNORED both\n");
420 event_process(&fe
, NULL
);
422 ob_debug("focused window got an Out/In back to "
423 "itself but focus_client was null "
424 "IGNORED just the Out\n");
433 /* once all the FocusOut's have been dealt with, if
434 there is a FocusIn still left and it is valid, then
436 event_process(&fe
, &d
);
439 ob_debug("FocusIn was OK, so don't fallback\n");
449 ob_debug("no valid FocusIn and no FocusOut events found, "
452 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
460 static void event_process(const XEvent
*ec
, gpointer data
)
463 ObGroup
*group
= NULL
;
464 ObClient
*client
= NULL
;
466 ObDockApp
*dockapp
= NULL
;
467 ObWindow
*obwin
= NULL
;
469 ObEventData
*ed
= data
;
471 /* make a copy we can mangle */
475 window
= event_get_window(e
);
476 if (!(e
->type
== PropertyNotify
&&
477 (group
= g_hash_table_lookup(group_map
, &window
))))
478 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
479 switch (obwin
->type
) {
481 dock
= WINDOW_AS_DOCK(obwin
);
484 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
487 client
= WINDOW_AS_CLIENT(obwin
);
490 case Window_Internal
:
491 /* not to be used for events */
492 g_assert_not_reached();
497 event_set_lasttime(e
);
499 if (event_ignore(e
, client
)) {
506 /* deal with it in the kernel */
508 event_handle_group(group
, e
);
510 event_handle_client(client
, e
);
512 event_handle_dockapp(dockapp
, e
);
514 event_handle_dock(dock
, e
);
515 else if (window
== RootWindow(ob_display
, ob_screen
))
516 event_handle_root(e
);
517 else if (e
->type
== MapRequest
)
518 client_manage(window
);
519 else if (e
->type
== ConfigureRequest
) {
520 /* unhandled configure requests must be used to configure the
524 xwc
.x
= e
->xconfigurerequest
.x
;
525 xwc
.y
= e
->xconfigurerequest
.y
;
526 xwc
.width
= e
->xconfigurerequest
.width
;
527 xwc
.height
= e
->xconfigurerequest
.height
;
528 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
529 xwc
.sibling
= e
->xconfigurerequest
.above
;
530 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
532 /* we are not to be held responsible if someone sends us an
534 xerror_set_ignore(TRUE
);
535 XConfigureWindow(ob_display
, window
,
536 e
->xconfigurerequest
.value_mask
, &xwc
);
537 xerror_set_ignore(FALSE
);
540 /* user input (action-bound) events */
541 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
542 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
543 e
->type
== KeyRelease
)
545 if (menu_frame_visible
)
546 event_handle_menu(e
);
548 if (!keyboard_process_interactive_grab(e
, &client
)) {
549 if (moveresize_in_progress
) {
552 /* make further actions work on the client being
554 client
= moveresize_client
;
557 menu_can_hide
= FALSE
;
558 ob_main_loop_timeout_add(ob_main_loop
,
559 config_menu_hide_delay
* 1000,
560 menu_hide_delay_func
,
563 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
564 e
->type
== MotionNotify
)
565 mouse_event(client
, e
);
566 else if (e
->type
== KeyPress
)
567 keyboard_event((focus_cycle_target
? focus_cycle_target
:
568 (focus_hilite
? focus_hilite
: client
)),
575 static void event_handle_root(XEvent
*e
)
581 ob_debug("Another WM has requested to replace us. Exiting.\n");
586 if (e
->xclient
.format
!= 32) break;
588 msgtype
= e
->xclient
.message_type
;
589 if (msgtype
== prop_atoms
.net_current_desktop
) {
590 guint d
= e
->xclient
.data
.l
[0];
591 if (d
< screen_num_desktops
)
592 screen_set_desktop(d
);
593 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
594 guint d
= e
->xclient
.data
.l
[0];
596 screen_set_num_desktops(d
);
597 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
598 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
602 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
603 screen_update_desktop_names();
604 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
605 screen_update_layout();
607 case ConfigureNotify
:
609 XRRUpdateConfiguration(e
);
618 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
622 g_assert(e
->type
== PropertyNotify
);
624 for (it
= group
->members
; it
; it
= g_slist_next(it
))
625 event_handle_client(it
->data
, e
);
628 void event_enter_client(ObClient
*client
)
630 g_assert(config_focus_follow
);
632 if (client_normal(client
) && client_can_focus(client
)) {
633 if (config_focus_delay
) {
634 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
635 ob_main_loop_timeout_add(ob_main_loop
,
640 focus_delay_func(client
);
644 static void event_handle_client(ObClient
*client
, XEvent
*e
)
652 case VisibilityNotify
:
653 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
657 /* Wheel buttons don't draw because they are an instant click, so it
658 is a waste of resources to go drawing it. */
659 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
660 con
= frame_context(client
, e
->xbutton
.window
);
661 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
663 case OB_FRAME_CONTEXT_MAXIMIZE
:
664 client
->frame
->max_press
= (e
->type
== ButtonPress
);
665 framerender_frame(client
->frame
);
667 case OB_FRAME_CONTEXT_CLOSE
:
668 client
->frame
->close_press
= (e
->type
== ButtonPress
);
669 framerender_frame(client
->frame
);
671 case OB_FRAME_CONTEXT_ICONIFY
:
672 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
673 framerender_frame(client
->frame
);
675 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
676 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
677 framerender_frame(client
->frame
);
679 case OB_FRAME_CONTEXT_SHADE
:
680 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
681 framerender_frame(client
->frame
);
684 /* nothing changes with clicks for any other contexts */
691 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
692 e
->xfocus
.window
, client
->window
,
693 e
->xfocus
.mode
, e
->xfocus
.detail
);
695 if (client
!= focus_client
) {
696 focus_set_client(client
);
697 frame_adjust_focus(client
->frame
, TRUE
);
698 client_calc_layer(client
);
703 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
704 e
->xfocus
.window
, client
->window
,
705 e
->xfocus
.mode
, e
->xfocus
.detail
);
708 frame_adjust_focus(client
->frame
, FALSE
);
709 client_calc_layer(client
);
712 con
= frame_context(client
, e
->xcrossing
.window
);
714 case OB_FRAME_CONTEXT_MAXIMIZE
:
715 client
->frame
->max_hover
= FALSE
;
716 frame_adjust_state(client
->frame
);
718 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
719 client
->frame
->desk_hover
= FALSE
;
720 frame_adjust_state(client
->frame
);
722 case OB_FRAME_CONTEXT_SHADE
:
723 client
->frame
->shade_hover
= FALSE
;
724 frame_adjust_state(client
->frame
);
726 case OB_FRAME_CONTEXT_ICONIFY
:
727 client
->frame
->iconify_hover
= FALSE
;
728 frame_adjust_state(client
->frame
);
730 case OB_FRAME_CONTEXT_CLOSE
:
731 client
->frame
->close_hover
= FALSE
;
732 frame_adjust_state(client
->frame
);
734 case OB_FRAME_CONTEXT_FRAME
:
735 if (config_focus_follow
&& config_focus_delay
)
736 ob_main_loop_timeout_remove_data(ob_main_loop
,
746 gboolean nofocus
= FALSE
;
748 if (ignore_enter_focus
) {
749 ignore_enter_focus
--;
753 con
= frame_context(client
, e
->xcrossing
.window
);
755 case OB_FRAME_CONTEXT_MAXIMIZE
:
756 client
->frame
->max_hover
= TRUE
;
757 frame_adjust_state(client
->frame
);
759 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
760 client
->frame
->desk_hover
= TRUE
;
761 frame_adjust_state(client
->frame
);
763 case OB_FRAME_CONTEXT_SHADE
:
764 client
->frame
->shade_hover
= TRUE
;
765 frame_adjust_state(client
->frame
);
767 case OB_FRAME_CONTEXT_ICONIFY
:
768 client
->frame
->iconify_hover
= TRUE
;
769 frame_adjust_state(client
->frame
);
771 case OB_FRAME_CONTEXT_CLOSE
:
772 client
->frame
->close_hover
= TRUE
;
773 frame_adjust_state(client
->frame
);
775 case OB_FRAME_CONTEXT_FRAME
:
776 if (e
->xcrossing
.mode
== NotifyGrab
||
777 e
->xcrossing
.mode
== NotifyUngrab
)
780 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
781 (e
->type
== EnterNotify
? "Enter" : "Leave"),
783 e
->xcrossing
.detail
, client
?client
->window
:0);
787 ob_debug("%sNotify mode %d detail %d on %lx, "
788 "focusing window: %d\n",
789 (e
->type
== EnterNotify
? "Enter" : "Leave"),
791 e
->xcrossing
.detail
, (client
?client
->window
:0),
794 if (!nofocus
&& config_focus_follow
)
795 event_enter_client(client
);
803 case ConfigureRequest
:
805 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
806 ConfigureRequest
, &ce
)) {
808 /* XXX if this causes bad things.. we can compress config req's
809 with the same mask. */
810 e
->xconfigurerequest
.value_mask
|=
811 ce
.xconfigurerequest
.value_mask
;
812 if (ce
.xconfigurerequest
.value_mask
& CWX
)
813 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
814 if (ce
.xconfigurerequest
.value_mask
& CWY
)
815 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
816 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
817 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
818 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
819 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
820 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
821 e
->xconfigurerequest
.border_width
=
822 ce
.xconfigurerequest
.border_width
;
823 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
824 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
827 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
828 if (client
->iconic
|| client
->shaded
) return;
830 /* resize, then move, as specified in the EWMH section 7.7 */
831 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
837 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
838 client
->border_width
= e
->xconfigurerequest
.border_width
;
840 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
841 e
->xconfigurerequest
.x
: client
->area
.x
;
842 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
843 e
->xconfigurerequest
.y
: client
->area
.y
;
844 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
845 e
->xconfigurerequest
.width
: client
->area
.width
;
846 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
847 e
->xconfigurerequest
.height
: client
->area
.height
;
853 client
->frame
->size
.left
+ client
->frame
->size
.right
;
855 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
856 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
857 client_normal(client
));
858 if (e
->xconfigurerequest
.value_mask
& CWX
)
860 if (e
->xconfigurerequest
.value_mask
& CWY
)
864 switch (client
->gravity
) {
865 case NorthEastGravity
:
867 corner
= OB_CORNER_TOPRIGHT
;
869 case SouthWestGravity
:
871 corner
= OB_CORNER_BOTTOMLEFT
;
873 case SouthEastGravity
:
874 corner
= OB_CORNER_BOTTOMRIGHT
;
876 default: /* NorthWest, Static, etc */
877 corner
= OB_CORNER_TOPLEFT
;
880 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
884 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
885 switch (e
->xconfigurerequest
.detail
) {
888 client_lower(client
);
894 client_raise(client
);
900 if (client
->ignore_unmaps
) {
901 client
->ignore_unmaps
--;
904 client_unmanage(client
);
907 client_unmanage(client
);
910 /* this is when the client is first taken captive in the frame */
911 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
914 This event is quite rare and is usually handled in unmapHandler.
915 However, if the window is unmapped when the reparent event occurs,
916 the window manager never sees it because an unmap event is not sent
917 to an already unmapped window.
920 /* we don't want the reparent event, put it back on the stack for the
921 X server to deal with after we unmanage the window */
922 XPutBackEvent(ob_display
, e
);
924 client_unmanage(client
);
927 ob_debug("MapRequest for 0x%lx\n", client
->window
);
928 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
929 does, we don't want it!
930 it can happen now when the window is on
931 another desktop, but we still don't
933 client_activate(client
, FALSE
, TRUE
);
936 /* validate cuz we query stuff off the client here */
937 if (!client_validate(client
)) break;
939 if (e
->xclient
.format
!= 32) return;
941 msgtype
= e
->xclient
.message_type
;
942 if (msgtype
== prop_atoms
.wm_change_state
) {
943 /* compress changes into a single change */
944 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
946 /* XXX: it would be nice to compress ALL messages of a
947 type, not just messages in a row without other
948 message types between. */
949 if (ce
.xclient
.message_type
!= msgtype
) {
950 XPutBackEvent(ob_display
, &ce
);
953 e
->xclient
= ce
.xclient
;
955 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
956 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
957 /* compress changes into a single change */
958 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
960 /* XXX: it would be nice to compress ALL messages of a
961 type, not just messages in a row without other
962 message types between. */
963 if (ce
.xclient
.message_type
!= msgtype
) {
964 XPutBackEvent(ob_display
, &ce
);
967 e
->xclient
= ce
.xclient
;
969 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
970 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
971 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
973 } else if (msgtype
== prop_atoms
.net_wm_state
) {
974 /* can't compress these */
975 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
976 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
977 e
->xclient
.data
.l
[0] == 1 ? "Add" :
978 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
979 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
981 client_set_state(client
, e
->xclient
.data
.l
[0],
982 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
983 } else if (msgtype
== prop_atoms
.net_close_window
) {
984 ob_debug("net_close_window for 0x%lx\n", client
->window
);
985 client_close(client
);
986 } else if (msgtype
== prop_atoms
.net_active_window
) {
987 ob_debug("net_active_window for 0x%lx source=%s\n",
989 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
990 (e
->xclient
.data
.l
[0] == 1 ? "application" :
991 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
992 /* XXX make use of data.l[1] and [2] ! */
993 client_activate(client
, FALSE
,
994 (e
->xclient
.data
.l
[0] == 0 ||
995 e
->xclient
.data
.l
[0] == 2));
996 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
997 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
998 if ((Atom
)e
->xclient
.data
.l
[2] ==
999 prop_atoms
.net_wm_moveresize_size_topleft
||
1000 (Atom
)e
->xclient
.data
.l
[2] ==
1001 prop_atoms
.net_wm_moveresize_size_top
||
1002 (Atom
)e
->xclient
.data
.l
[2] ==
1003 prop_atoms
.net_wm_moveresize_size_topright
||
1004 (Atom
)e
->xclient
.data
.l
[2] ==
1005 prop_atoms
.net_wm_moveresize_size_right
||
1006 (Atom
)e
->xclient
.data
.l
[2] ==
1007 prop_atoms
.net_wm_moveresize_size_right
||
1008 (Atom
)e
->xclient
.data
.l
[2] ==
1009 prop_atoms
.net_wm_moveresize_size_bottomright
||
1010 (Atom
)e
->xclient
.data
.l
[2] ==
1011 prop_atoms
.net_wm_moveresize_size_bottom
||
1012 (Atom
)e
->xclient
.data
.l
[2] ==
1013 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1014 (Atom
)e
->xclient
.data
.l
[2] ==
1015 prop_atoms
.net_wm_moveresize_size_left
||
1016 (Atom
)e
->xclient
.data
.l
[2] ==
1017 prop_atoms
.net_wm_moveresize_move
||
1018 (Atom
)e
->xclient
.data
.l
[2] ==
1019 prop_atoms
.net_wm_moveresize_size_keyboard
||
1020 (Atom
)e
->xclient
.data
.l
[2] ==
1021 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1023 moveresize_start(client
, e
->xclient
.data
.l
[0],
1024 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1025 e
->xclient
.data
.l
[2]);
1027 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1028 gint oldg
= client
->gravity
;
1029 gint tmpg
, x
, y
, w
, h
;
1031 if (e
->xclient
.data
.l
[0] & 0xff)
1032 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
1036 if (e
->xclient
.data
.l
[0] & 1 << 8)
1037 x
= e
->xclient
.data
.l
[1];
1040 if (e
->xclient
.data
.l
[0] & 1 << 9)
1041 y
= e
->xclient
.data
.l
[2];
1044 if (e
->xclient
.data
.l
[0] & 1 << 10)
1045 w
= e
->xclient
.data
.l
[3];
1047 w
= client
->area
.width
;
1048 if (e
->xclient
.data
.l
[0] & 1 << 11)
1049 h
= e
->xclient
.data
.l
[4];
1051 h
= client
->area
.height
;
1052 client
->gravity
= tmpg
;
1058 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1060 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1061 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1062 client_normal(client
));
1063 if (e
->xclient
.data
.l
[0] & 1 << 8)
1065 if (e
->xclient
.data
.l
[0] & 1 << 9)
1069 client_configure(client
, OB_CORNER_TOPLEFT
,
1070 x
, y
, w
, h
, FALSE
, TRUE
);
1072 client
->gravity
= oldg
;
1075 case PropertyNotify
:
1076 /* validate cuz we query stuff off the client here */
1077 if (!client_validate(client
)) break;
1079 /* compress changes to a single property into a single change */
1080 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1084 /* XXX: it would be nice to compress ALL changes to a property,
1085 not just changes in a row without other props between. */
1087 a
= ce
.xproperty
.atom
;
1088 b
= e
->xproperty
.atom
;
1092 if ((a
== prop_atoms
.net_wm_name
||
1093 a
== prop_atoms
.wm_name
||
1094 a
== prop_atoms
.net_wm_icon_name
||
1095 a
== prop_atoms
.wm_icon_name
)
1097 (b
== prop_atoms
.net_wm_name
||
1098 b
== prop_atoms
.wm_name
||
1099 b
== prop_atoms
.net_wm_icon_name
||
1100 b
== prop_atoms
.wm_icon_name
)) {
1103 if ((a
== prop_atoms
.net_wm_icon
||
1104 a
== prop_atoms
.kwm_win_icon
)
1106 (b
== prop_atoms
.net_wm_icon
||
1107 b
== prop_atoms
.kwm_win_icon
))
1110 XPutBackEvent(ob_display
, &ce
);
1114 msgtype
= e
->xproperty
.atom
;
1115 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1116 client_update_normal_hints(client
);
1117 /* normal hints can make a window non-resizable */
1118 client_setup_decor_and_functions(client
);
1119 } else if (msgtype
== XA_WM_HINTS
) {
1120 client_update_wmhints(client
);
1121 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1122 client_update_transient_for(client
);
1123 client_get_type(client
);
1124 /* type may have changed, so update the layer */
1125 client_calc_layer(client
);
1126 client_setup_decor_and_functions(client
);
1127 } else if (msgtype
== prop_atoms
.net_wm_name
||
1128 msgtype
== prop_atoms
.wm_name
||
1129 msgtype
== prop_atoms
.net_wm_icon_name
||
1130 msgtype
== prop_atoms
.wm_icon_name
) {
1131 client_update_title(client
);
1132 } else if (msgtype
== prop_atoms
.wm_class
) {
1133 client_update_class(client
);
1134 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1135 client_update_protocols(client
);
1136 client_setup_decor_and_functions(client
);
1138 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1139 client_update_strut(client
);
1141 else if (msgtype
== prop_atoms
.net_wm_icon
||
1142 msgtype
== prop_atoms
.kwm_win_icon
) {
1143 client_update_icons(client
);
1145 else if (msgtype
== prop_atoms
.sm_client_id
) {
1146 client_update_sm_client_id(client
);
1151 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1152 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1153 frame_adjust_shape(client
->frame
);
1159 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1163 if (e
->xbutton
.button
== 1)
1164 stacking_raise(DOCK_AS_WINDOW(s
), FALSE
);
1165 else if (e
->xbutton
.button
== 2)
1166 stacking_lower(DOCK_AS_WINDOW(s
), FALSE
);
1177 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1181 dock_app_drag(app
, &e
->xmotion
);
1184 if (app
->ignore_unmaps
) {
1185 app
->ignore_unmaps
--;
1188 dock_remove(app
, TRUE
);
1191 dock_remove(app
, FALSE
);
1193 case ReparentNotify
:
1194 dock_remove(app
, FALSE
);
1196 case ConfigureNotify
:
1197 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1202 ObMenuFrame
* find_active_menu()
1205 ObMenuFrame
*ret
= NULL
;
1207 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1216 ObMenuFrame
* find_active_or_last_menu()
1218 ObMenuFrame
*ret
= NULL
;
1220 ret
= find_active_menu();
1221 if (!ret
&& menu_frame_visible
)
1222 ret
= menu_frame_visible
->data
;
1226 static void event_handle_menu(XEvent
*ev
)
1229 ObMenuEntryFrame
*e
;
1233 if (menu_can_hide
) {
1234 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1235 ev
->xbutton
.y_root
)))
1236 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1238 menu_frame_hide_all();
1242 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1243 ev
->xmotion
.y_root
))) {
1244 menu_frame_move_on_screen(f
);
1245 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1246 ev
->xmotion
.y_root
)))
1247 menu_frame_select(f
, e
);
1252 a
= find_active_menu();
1254 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1256 menu_frame_select(a
, NULL
);
1261 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1262 menu_frame_hide_all();
1263 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1265 if ((f
= find_active_menu()))
1266 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1267 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1269 if ((f
= find_active_or_last_menu()) && f
->parent
)
1270 menu_frame_select(f
, NULL
);
1271 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1273 if ((f
= find_active_or_last_menu()) && f
->child
)
1274 menu_frame_select_next(f
->child
);
1275 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1277 if ((f
= find_active_or_last_menu()))
1278 menu_frame_select_previous(f
);
1279 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1281 if ((f
= find_active_or_last_menu()))
1282 menu_frame_select_next(f
);
1288 static gboolean
menu_hide_delay_func(gpointer data
)
1290 menu_can_hide
= TRUE
;
1291 return FALSE
; /* no repeat */
1294 static gboolean
focus_delay_func(gpointer data
)
1298 if (focus_client
!= c
) {
1300 if (config_focus_raise
)
1303 return FALSE
; /* no repeat */
1306 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1308 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1312 static void event_client_dest(ObClient
*client
, gpointer data
)
1314 if (client
== focus_hilite
)
1315 focus_hilite
= NULL
;
1318 void event_halt_focus_delay()
1320 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1323 void event_ignore_queued_enters()
1325 GSList
*saved
= NULL
, *it
;
1328 XSync(ob_display
, FALSE
);
1330 /* count the events */
1332 e
= g_new(XEvent
, 1);
1333 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1336 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1337 if (win
&& WINDOW_IS_CLIENT(win
))
1338 ++ignore_enter_focus
;
1340 saved
= g_slist_append(saved
, e
);
1346 /* put the events back */
1347 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1348 XPutBackEvent(ob_display
, it
->data
);
1351 g_slist_free(saved
);