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);
599 } else if (msgtype
== prop_atoms
.ob_control
) {
600 if (e
->xclient
.data
.l
[0] == 1)
602 else if (e
->xclient
.data
.l
[0] == 2)
607 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
608 screen_update_desktop_names();
609 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
610 screen_update_layout();
612 case ConfigureNotify
:
614 XRRUpdateConfiguration(e
);
623 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
627 g_assert(e
->type
== PropertyNotify
);
629 for (it
= group
->members
; it
; it
= g_slist_next(it
))
630 event_handle_client(it
->data
, e
);
633 void event_enter_client(ObClient
*client
)
635 g_assert(config_focus_follow
);
637 if (client_normal(client
) && client_can_focus(client
)) {
638 if (config_focus_delay
) {
639 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
640 ob_main_loop_timeout_add(ob_main_loop
,
645 focus_delay_func(client
);
649 static void event_handle_client(ObClient
*client
, XEvent
*e
)
657 case VisibilityNotify
:
658 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
662 /* Wheel buttons don't draw because they are an instant click, so it
663 is a waste of resources to go drawing it. */
664 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
665 con
= frame_context(client
, e
->xbutton
.window
);
666 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
668 case OB_FRAME_CONTEXT_MAXIMIZE
:
669 client
->frame
->max_press
= (e
->type
== ButtonPress
);
670 framerender_frame(client
->frame
);
672 case OB_FRAME_CONTEXT_CLOSE
:
673 client
->frame
->close_press
= (e
->type
== ButtonPress
);
674 framerender_frame(client
->frame
);
676 case OB_FRAME_CONTEXT_ICONIFY
:
677 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
678 framerender_frame(client
->frame
);
680 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
681 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
682 framerender_frame(client
->frame
);
684 case OB_FRAME_CONTEXT_SHADE
:
685 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
686 framerender_frame(client
->frame
);
689 /* nothing changes with clicks for any other contexts */
696 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
697 e
->xfocus
.window
, client
->window
,
698 e
->xfocus
.mode
, e
->xfocus
.detail
);
700 if (client
!= focus_client
) {
701 focus_set_client(client
);
702 frame_adjust_focus(client
->frame
, TRUE
);
703 client_calc_layer(client
);
708 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
709 e
->xfocus
.window
, client
->window
,
710 e
->xfocus
.mode
, e
->xfocus
.detail
);
713 frame_adjust_focus(client
->frame
, FALSE
);
714 client_calc_layer(client
);
717 con
= frame_context(client
, e
->xcrossing
.window
);
719 case OB_FRAME_CONTEXT_MAXIMIZE
:
720 client
->frame
->max_hover
= FALSE
;
721 frame_adjust_state(client
->frame
);
723 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
724 client
->frame
->desk_hover
= FALSE
;
725 frame_adjust_state(client
->frame
);
727 case OB_FRAME_CONTEXT_SHADE
:
728 client
->frame
->shade_hover
= FALSE
;
729 frame_adjust_state(client
->frame
);
731 case OB_FRAME_CONTEXT_ICONIFY
:
732 client
->frame
->iconify_hover
= FALSE
;
733 frame_adjust_state(client
->frame
);
735 case OB_FRAME_CONTEXT_CLOSE
:
736 client
->frame
->close_hover
= FALSE
;
737 frame_adjust_state(client
->frame
);
739 case OB_FRAME_CONTEXT_FRAME
:
740 if (config_focus_follow
&& config_focus_delay
)
741 ob_main_loop_timeout_remove_data(ob_main_loop
,
751 gboolean nofocus
= FALSE
;
753 if (ignore_enter_focus
) {
754 ignore_enter_focus
--;
758 con
= frame_context(client
, e
->xcrossing
.window
);
760 case OB_FRAME_CONTEXT_MAXIMIZE
:
761 client
->frame
->max_hover
= TRUE
;
762 frame_adjust_state(client
->frame
);
764 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
765 client
->frame
->desk_hover
= TRUE
;
766 frame_adjust_state(client
->frame
);
768 case OB_FRAME_CONTEXT_SHADE
:
769 client
->frame
->shade_hover
= TRUE
;
770 frame_adjust_state(client
->frame
);
772 case OB_FRAME_CONTEXT_ICONIFY
:
773 client
->frame
->iconify_hover
= TRUE
;
774 frame_adjust_state(client
->frame
);
776 case OB_FRAME_CONTEXT_CLOSE
:
777 client
->frame
->close_hover
= TRUE
;
778 frame_adjust_state(client
->frame
);
780 case OB_FRAME_CONTEXT_FRAME
:
781 if (e
->xcrossing
.mode
== NotifyGrab
||
782 e
->xcrossing
.mode
== NotifyUngrab
)
785 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
786 (e
->type
== EnterNotify
? "Enter" : "Leave"),
788 e
->xcrossing
.detail
, client
?client
->window
:0);
792 ob_debug("%sNotify mode %d detail %d on %lx, "
793 "focusing window: %d\n",
794 (e
->type
== EnterNotify
? "Enter" : "Leave"),
796 e
->xcrossing
.detail
, (client
?client
->window
:0),
799 if (!nofocus
&& config_focus_follow
)
800 event_enter_client(client
);
808 case ConfigureRequest
:
810 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
811 ConfigureRequest
, &ce
)) {
813 /* XXX if this causes bad things.. we can compress config req's
814 with the same mask. */
815 e
->xconfigurerequest
.value_mask
|=
816 ce
.xconfigurerequest
.value_mask
;
817 if (ce
.xconfigurerequest
.value_mask
& CWX
)
818 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
819 if (ce
.xconfigurerequest
.value_mask
& CWY
)
820 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
821 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
822 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
823 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
824 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
825 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
826 e
->xconfigurerequest
.border_width
=
827 ce
.xconfigurerequest
.border_width
;
828 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
829 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
832 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
833 if (client
->iconic
|| client
->shaded
) return;
835 /* resize, then move, as specified in the EWMH section 7.7 */
836 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
842 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
843 client
->border_width
= e
->xconfigurerequest
.border_width
;
845 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
846 e
->xconfigurerequest
.x
: client
->area
.x
;
847 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
848 e
->xconfigurerequest
.y
: client
->area
.y
;
849 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
850 e
->xconfigurerequest
.width
: client
->area
.width
;
851 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
852 e
->xconfigurerequest
.height
: client
->area
.height
;
858 client
->frame
->size
.left
+ client
->frame
->size
.right
;
860 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
861 /* make this rude for size-only changes but not for position
863 gboolean moving
= ((e
->xconfigurerequest
.value_mask
& CWX
) ||
864 (e
->xconfigurerequest
.value_mask
& CWY
));
866 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
868 if (e
->xconfigurerequest
.value_mask
& CWX
)
870 if (e
->xconfigurerequest
.value_mask
& CWY
)
874 switch (client
->gravity
) {
875 case NorthEastGravity
:
877 corner
= OB_CORNER_TOPRIGHT
;
879 case SouthWestGravity
:
881 corner
= OB_CORNER_BOTTOMLEFT
;
883 case SouthEastGravity
:
884 corner
= OB_CORNER_BOTTOMRIGHT
;
886 default: /* NorthWest, Static, etc */
887 corner
= OB_CORNER_TOPLEFT
;
890 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
894 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
895 switch (e
->xconfigurerequest
.detail
) {
898 /* Apps are so rude. And this is totally disconnected from
899 activation/focus. Bleh. */
900 /*client_lower(client);*/
906 /* Apps are so rude. And this is totally disconnected from
907 activation/focus. Bleh. */
908 /*client_raise(client);*/
914 if (client
->ignore_unmaps
) {
915 client
->ignore_unmaps
--;
918 client_unmanage(client
);
921 client_unmanage(client
);
924 /* this is when the client is first taken captive in the frame */
925 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
928 This event is quite rare and is usually handled in unmapHandler.
929 However, if the window is unmapped when the reparent event occurs,
930 the window manager never sees it because an unmap event is not sent
931 to an already unmapped window.
934 /* we don't want the reparent event, put it back on the stack for the
935 X server to deal with after we unmanage the window */
936 XPutBackEvent(ob_display
, e
);
938 client_unmanage(client
);
941 ob_debug("MapRequest for 0x%lx\n", client
->window
);
942 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
943 does, we don't want it!
944 it can happen now when the window is on
945 another desktop, but we still don't
947 client_activate(client
, FALSE
, TRUE
, CurrentTime
);
950 /* validate cuz we query stuff off the client here */
951 if (!client_validate(client
)) break;
953 if (e
->xclient
.format
!= 32) return;
955 msgtype
= e
->xclient
.message_type
;
956 if (msgtype
== prop_atoms
.wm_change_state
) {
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 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
970 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
971 /* compress changes into a single change */
972 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
974 /* XXX: it would be nice to compress ALL messages of a
975 type, not just messages in a row without other
976 message types between. */
977 if (ce
.xclient
.message_type
!= msgtype
) {
978 XPutBackEvent(ob_display
, &ce
);
981 e
->xclient
= ce
.xclient
;
983 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
984 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
985 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
987 } else if (msgtype
== prop_atoms
.net_wm_state
) {
988 /* can't compress these */
989 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
990 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
991 e
->xclient
.data
.l
[0] == 1 ? "Add" :
992 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
993 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
995 client_set_state(client
, e
->xclient
.data
.l
[0],
996 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
997 } else if (msgtype
== prop_atoms
.net_close_window
) {
998 ob_debug("net_close_window for 0x%lx\n", client
->window
);
999 client_close(client
);
1000 } else if (msgtype
== prop_atoms
.net_active_window
) {
1001 ob_debug("net_active_window for 0x%lx source=%s\n",
1003 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
1004 (e
->xclient
.data
.l
[0] == 1 ? "application" :
1005 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
1006 /* XXX make use of data.l[1] and [2] ! */
1007 client_activate(client
, FALSE
,
1008 (e
->xclient
.data
.l
[0] == 0 ||
1009 e
->xclient
.data
.l
[0] == 2),
1010 e
->xclient
.data
.l
[1]);
1011 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
1012 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1013 client
->window
, e
->xclient
.data
.l
[2]);
1014 if ((Atom
)e
->xclient
.data
.l
[2] ==
1015 prop_atoms
.net_wm_moveresize_size_topleft
||
1016 (Atom
)e
->xclient
.data
.l
[2] ==
1017 prop_atoms
.net_wm_moveresize_size_top
||
1018 (Atom
)e
->xclient
.data
.l
[2] ==
1019 prop_atoms
.net_wm_moveresize_size_topright
||
1020 (Atom
)e
->xclient
.data
.l
[2] ==
1021 prop_atoms
.net_wm_moveresize_size_right
||
1022 (Atom
)e
->xclient
.data
.l
[2] ==
1023 prop_atoms
.net_wm_moveresize_size_right
||
1024 (Atom
)e
->xclient
.data
.l
[2] ==
1025 prop_atoms
.net_wm_moveresize_size_bottomright
||
1026 (Atom
)e
->xclient
.data
.l
[2] ==
1027 prop_atoms
.net_wm_moveresize_size_bottom
||
1028 (Atom
)e
->xclient
.data
.l
[2] ==
1029 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1030 (Atom
)e
->xclient
.data
.l
[2] ==
1031 prop_atoms
.net_wm_moveresize_size_left
||
1032 (Atom
)e
->xclient
.data
.l
[2] ==
1033 prop_atoms
.net_wm_moveresize_move
||
1034 (Atom
)e
->xclient
.data
.l
[2] ==
1035 prop_atoms
.net_wm_moveresize_size_keyboard
||
1036 (Atom
)e
->xclient
.data
.l
[2] ==
1037 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1039 moveresize_start(client
, e
->xclient
.data
.l
[0],
1040 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1041 e
->xclient
.data
.l
[2]);
1043 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1044 prop_atoms
.net_wm_moveresize_cancel
)
1045 moveresize_end(TRUE
);
1046 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1047 gint oldg
= client
->gravity
;
1048 gint tmpg
, x
, y
, w
, h
;
1050 if (e
->xclient
.data
.l
[0] & 0xff)
1051 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
1055 if (e
->xclient
.data
.l
[0] & 1 << 8)
1056 x
= e
->xclient
.data
.l
[1];
1059 if (e
->xclient
.data
.l
[0] & 1 << 9)
1060 y
= e
->xclient
.data
.l
[2];
1063 if (e
->xclient
.data
.l
[0] & 1 << 10)
1064 w
= e
->xclient
.data
.l
[3];
1066 w
= client
->area
.width
;
1067 if (e
->xclient
.data
.l
[0] & 1 << 11)
1068 h
= e
->xclient
.data
.l
[4];
1070 h
= client
->area
.height
;
1071 client
->gravity
= tmpg
;
1077 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1079 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1080 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1081 client_normal(client
));
1082 if (e
->xclient
.data
.l
[0] & 1 << 8)
1084 if (e
->xclient
.data
.l
[0] & 1 << 9)
1088 client_configure(client
, OB_CORNER_TOPLEFT
,
1089 x
, y
, w
, h
, FALSE
, TRUE
);
1091 client
->gravity
= oldg
;
1094 case PropertyNotify
:
1095 /* validate cuz we query stuff off the client here */
1096 if (!client_validate(client
)) break;
1098 /* compress changes to a single property into a single change */
1099 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1103 /* XXX: it would be nice to compress ALL changes to a property,
1104 not just changes in a row without other props between. */
1106 a
= ce
.xproperty
.atom
;
1107 b
= e
->xproperty
.atom
;
1111 if ((a
== prop_atoms
.net_wm_name
||
1112 a
== prop_atoms
.wm_name
||
1113 a
== prop_atoms
.net_wm_icon_name
||
1114 a
== prop_atoms
.wm_icon_name
)
1116 (b
== prop_atoms
.net_wm_name
||
1117 b
== prop_atoms
.wm_name
||
1118 b
== prop_atoms
.net_wm_icon_name
||
1119 b
== prop_atoms
.wm_icon_name
)) {
1122 if (a
== prop_atoms
.net_wm_icon
&&
1123 b
== prop_atoms
.net_wm_icon
)
1126 XPutBackEvent(ob_display
, &ce
);
1130 msgtype
= e
->xproperty
.atom
;
1131 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1132 client_update_normal_hints(client
);
1133 /* normal hints can make a window non-resizable */
1134 client_setup_decor_and_functions(client
);
1135 } else if (msgtype
== XA_WM_HINTS
) {
1136 client_update_wmhints(client
);
1137 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1138 client_update_transient_for(client
);
1139 client_get_type(client
);
1140 /* type may have changed, so update the layer */
1141 client_calc_layer(client
);
1142 client_setup_decor_and_functions(client
);
1143 } else if (msgtype
== prop_atoms
.net_wm_name
||
1144 msgtype
== prop_atoms
.wm_name
||
1145 msgtype
== prop_atoms
.net_wm_icon_name
||
1146 msgtype
== prop_atoms
.wm_icon_name
) {
1147 client_update_title(client
);
1148 } else if (msgtype
== prop_atoms
.wm_class
) {
1149 client_update_class(client
);
1150 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1151 client_update_protocols(client
);
1152 client_setup_decor_and_functions(client
);
1154 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1155 client_update_strut(client
);
1157 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1158 client_update_icons(client
);
1160 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1161 client_update_user_time(client
, TRUE
);
1163 else if (msgtype
== prop_atoms
.sm_client_id
) {
1164 client_update_sm_client_id(client
);
1169 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1170 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1171 frame_adjust_shape(client
->frame
);
1177 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1181 if (e
->xbutton
.button
== 1)
1182 stacking_raise(DOCK_AS_WINDOW(s
), FALSE
);
1183 else if (e
->xbutton
.button
== 2)
1184 stacking_lower(DOCK_AS_WINDOW(s
), FALSE
);
1195 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1199 dock_app_drag(app
, &e
->xmotion
);
1202 if (app
->ignore_unmaps
) {
1203 app
->ignore_unmaps
--;
1206 dock_remove(app
, TRUE
);
1209 dock_remove(app
, FALSE
);
1211 case ReparentNotify
:
1212 dock_remove(app
, FALSE
);
1214 case ConfigureNotify
:
1215 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1220 ObMenuFrame
* find_active_menu()
1223 ObMenuFrame
*ret
= NULL
;
1225 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1234 ObMenuFrame
* find_active_or_last_menu()
1236 ObMenuFrame
*ret
= NULL
;
1238 ret
= find_active_menu();
1239 if (!ret
&& menu_frame_visible
)
1240 ret
= menu_frame_visible
->data
;
1244 static void event_handle_menu(XEvent
*ev
)
1247 ObMenuEntryFrame
*e
;
1251 if (menu_can_hide
) {
1252 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1253 ev
->xbutton
.y_root
)))
1254 menu_entry_frame_execute(e
, ev
->xbutton
.state
,
1257 menu_frame_hide_all();
1261 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1262 ev
->xmotion
.y_root
))) {
1263 menu_frame_move_on_screen(f
);
1264 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1265 ev
->xmotion
.y_root
)))
1266 menu_frame_select(f
, e
);
1271 a
= find_active_menu();
1273 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1275 menu_frame_select(a
, NULL
);
1280 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1281 menu_frame_hide_all();
1282 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1284 if ((f
= find_active_menu()))
1285 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
,
1287 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1289 if ((f
= find_active_or_last_menu()) && f
->parent
)
1290 menu_frame_select(f
, NULL
);
1291 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1293 if ((f
= find_active_or_last_menu()) && f
->child
)
1294 menu_frame_select_next(f
->child
);
1295 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1297 if ((f
= find_active_or_last_menu()))
1298 menu_frame_select_previous(f
);
1299 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1301 if ((f
= find_active_or_last_menu()))
1302 menu_frame_select_next(f
);
1308 static gboolean
menu_hide_delay_func(gpointer data
)
1310 menu_can_hide
= TRUE
;
1311 return FALSE
; /* no repeat */
1314 static gboolean
focus_delay_func(gpointer data
)
1318 if (focus_client
!= c
) {
1320 if (config_focus_raise
)
1323 return FALSE
; /* no repeat */
1326 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1328 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1332 static void event_client_dest(ObClient
*client
, gpointer data
)
1334 if (client
== focus_hilite
)
1335 focus_hilite
= NULL
;
1338 void event_halt_focus_delay()
1340 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1343 void event_ignore_queued_enters()
1345 GSList
*saved
= NULL
, *it
;
1348 XSync(ob_display
, FALSE
);
1350 /* count the events */
1352 e
= g_new(XEvent
, 1);
1353 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1356 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1357 if (win
&& WINDOW_IS_CLIENT(win
))
1358 ++ignore_enter_focus
;
1360 saved
= g_slist_append(saved
, e
);
1366 /* put the events back */
1367 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1368 XPutBackEvent(ob_display
, it
->data
);
1371 g_slist_free(saved
);