1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 event.c for the Openbox window manager
4 Copyright (c) 2004 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 XFreeModifiermap(modmap
);
181 static Window
event_get_window(XEvent
*e
)
188 window
= RootWindow(ob_display
, ob_screen
);
191 window
= e
->xmap
.window
;
194 window
= e
->xunmap
.window
;
197 window
= e
->xdestroywindow
.window
;
199 case ConfigureRequest
:
200 window
= e
->xconfigurerequest
.window
;
202 case ConfigureNotify
:
203 window
= e
->xconfigure
.window
;
207 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
208 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
210 window
= ((XkbBellNotifyEvent
*)e
)->window
;
216 window
= e
->xany
.window
;
221 static void event_set_lasttime(XEvent
*e
)
225 /* grab the lasttime and hack up the state */
241 t
= e
->xproperty
.time
;
245 t
= e
->xcrossing
.time
;
248 /* if more event types are anticipated, get their timestamp
253 if (t
> event_lasttime
)
257 #define STRIP_MODS(s) \
258 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
259 /* kill off the Button1Mask etc, only want the modifiers */ \
260 s &= (ControlMask | ShiftMask | Mod1Mask | \
261 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
263 static void event_hack_mods(XEvent *e)
266 XkbStateRec xkb_state
;
274 STRIP_MODS(e
->xbutton
.state
);
277 STRIP_MODS(e
->xkey
.state
);
280 STRIP_MODS(e
->xkey
.state
);
281 /* remove from the state the mask of the modifier being released, if
282 it is a modifier key being released (this is a little ugly..) */
284 if (XkbGetState(ob_display
, XkbUseCoreKbd
, &xkb_state
) == Success
) {
285 e
->xkey
.state
= xkb_state
.compat_state
;
289 kp
= modmap
->modifiermap
;
290 for (i
= 0; i
< mask_table_size
; ++i
) {
291 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
292 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
293 /* remove the mask for it */
294 e
->xkey
.state
&= ~mask_table
[i
];
295 /* cause the first loop to break; */
297 break; /* get outta here! */
304 STRIP_MODS(e
->xmotion
.state
);
305 /* compress events */
308 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
310 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
311 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
318 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
323 if (e
->xcrossing
.detail
== NotifyInferior
)
327 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
328 because of RevertToPointerRoot. If the focus ends up reverting to
329 pointer root on a workspace change, then the FocusIn event that we
330 want will be of type NotifyAncestor. This situation does not occur
331 for FocusOut, so it is safely ignored there.
333 if (INVALID_FOCUSIN(e
) ||
336 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
337 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
339 /* says a client was not found for the event (or a valid FocusIn
342 e
->xfocus
.window
= None
;
347 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
348 e
->xfocus
.mode
, e
->xfocus
.detail
);
352 if (INVALID_FOCUSOUT(e
)) {
354 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
355 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
361 ob_debug("FocusOut on %lx mode %d detail %d\n",
362 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
366 gboolean fallback
= TRUE
;
369 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
371 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
373 if (fe
.type
== FocusOut
) {
375 ob_debug("found pending FocusOut\n");
377 if (!INVALID_FOCUSOUT(&fe
)) {
378 /* if there is a VALID FocusOut still coming, don't
379 fallback focus yet, we'll deal with it then */
380 XPutBackEvent(ob_display
, &fe
);
386 ob_debug("found pending FocusIn\n");
388 /* is the focused window getting a FocusOut/In back to
391 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
392 !event_ignore(&fe
, client
)) {
394 if focus_client is not set, then we can't do
395 this. we need the FocusIn. This happens in the
396 case when the set_focus_client(NULL) in the
397 focus_fallback function fires and then
398 focus_fallback picks the currently focused
399 window (such as on a SendToDesktop-esque action.
403 ob_debug("focused window got an Out/In back to "
404 "itself IGNORED both\n");
408 event_process(&fe
, NULL
);
410 ob_debug("focused window got an Out/In back to "
411 "itself but focus_client was null "
412 "IGNORED just the Out\n");
421 /* once all the FocusOut's have been dealt with, if
422 there is a FocusIn still left and it is valid, then
424 event_process(&fe
, &d
);
427 ob_debug("FocusIn was OK, so don't fallback\n");
437 ob_debug("no valid FocusIn and no FocusOut events found, "
440 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
448 static void event_process(const XEvent
*ec
, gpointer data
)
451 ObGroup
*group
= NULL
;
452 ObClient
*client
= NULL
;
454 ObDockApp
*dockapp
= NULL
;
455 ObWindow
*obwin
= NULL
;
457 ObEventData
*ed
= data
;
459 /* make a copy we can mangle */
463 window
= event_get_window(e
);
464 if (!(e
->type
== PropertyNotify
&&
465 (group
= g_hash_table_lookup(group_map
, &window
))))
466 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
467 switch (obwin
->type
) {
469 dock
= WINDOW_AS_DOCK(obwin
);
472 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
475 client
= WINDOW_AS_CLIENT(obwin
);
478 case Window_Internal
:
479 /* not to be used for events */
480 g_assert_not_reached();
485 event_set_lasttime(e
);
487 if (event_ignore(e
, client
)) {
494 /* deal with it in the kernel */
496 event_handle_group(group
, e
);
498 event_handle_client(client
, e
);
500 event_handle_dockapp(dockapp
, e
);
502 event_handle_dock(dock
, e
);
503 else if (window
== RootWindow(ob_display
, ob_screen
))
504 event_handle_root(e
);
505 else if (e
->type
== MapRequest
)
506 client_manage(window
);
507 else if (e
->type
== ConfigureRequest
) {
508 /* unhandled configure requests must be used to configure the
512 xwc
.x
= e
->xconfigurerequest
.x
;
513 xwc
.y
= e
->xconfigurerequest
.y
;
514 xwc
.width
= e
->xconfigurerequest
.width
;
515 xwc
.height
= e
->xconfigurerequest
.height
;
516 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
517 xwc
.sibling
= e
->xconfigurerequest
.above
;
518 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
520 /* we are not to be held responsible if someone sends us an
522 xerror_set_ignore(TRUE
);
523 XConfigureWindow(ob_display
, window
,
524 e
->xconfigurerequest
.value_mask
, &xwc
);
525 xerror_set_ignore(FALSE
);
528 /* user input (action-bound) events */
529 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
530 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
531 e
->type
== KeyRelease
)
533 if (menu_frame_visible
)
534 event_handle_menu(e
);
536 if (!keyboard_process_interactive_grab(e
, &client
)) {
537 if (moveresize_in_progress
) {
540 /* make further actions work on the client being
542 client
= moveresize_client
;
545 menu_can_hide
= FALSE
;
546 ob_main_loop_timeout_add(ob_main_loop
,
547 config_menu_hide_delay
* 1000,
548 menu_hide_delay_func
,
551 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
552 e
->type
== MotionNotify
)
553 mouse_event(client
, e
);
554 else if (e
->type
== KeyPress
)
555 keyboard_event((focus_cycle_target
? focus_cycle_target
:
556 (focus_hilite
? focus_hilite
: client
)),
563 static void event_handle_root(XEvent
*e
)
569 ob_debug("Another WM has requested to replace us. Exiting.\n");
574 if (e
->xclient
.format
!= 32) break;
576 msgtype
= e
->xclient
.message_type
;
577 if (msgtype
== prop_atoms
.net_current_desktop
) {
578 guint d
= e
->xclient
.data
.l
[0];
579 if (d
< screen_num_desktops
)
580 screen_set_desktop(d
);
581 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
582 guint d
= e
->xclient
.data
.l
[0];
584 screen_set_num_desktops(d
);
585 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
586 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
590 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
591 screen_update_desktop_names();
592 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
593 screen_update_layout();
595 case ConfigureNotify
:
597 XRRUpdateConfiguration(e
);
604 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
605 ob_debug("VIDMODE EVENT\n");
611 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
615 g_assert(e
->type
== PropertyNotify
);
617 for (it
= group
->members
; it
; it
= g_slist_next(it
))
618 event_handle_client(it
->data
, e
);
621 void event_enter_client(ObClient
*client
)
623 g_assert(config_focus_follow
);
625 if (client_normal(client
) && client_can_focus(client
)) {
626 if (config_focus_delay
) {
627 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
628 ob_main_loop_timeout_add(ob_main_loop
,
633 focus_delay_func(client
);
637 static void event_handle_client(ObClient
*client
, XEvent
*e
)
645 case VisibilityNotify
:
646 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
650 /* Wheel buttons don't draw because they are an instant click, so it
651 is a waste of resources to go drawing it. */
652 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
653 con
= frame_context(client
, e
->xbutton
.window
);
654 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
656 case OB_FRAME_CONTEXT_MAXIMIZE
:
657 client
->frame
->max_press
= (e
->type
== ButtonPress
);
658 framerender_frame(client
->frame
);
660 case OB_FRAME_CONTEXT_CLOSE
:
661 client
->frame
->close_press
= (e
->type
== ButtonPress
);
662 framerender_frame(client
->frame
);
664 case OB_FRAME_CONTEXT_ICONIFY
:
665 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
666 framerender_frame(client
->frame
);
668 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
669 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
670 framerender_frame(client
->frame
);
672 case OB_FRAME_CONTEXT_SHADE
:
673 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
674 framerender_frame(client
->frame
);
677 /* nothing changes with clicks for any other contexts */
684 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
685 e
->xfocus
.window
, client
->window
,
686 e
->xfocus
.mode
, e
->xfocus
.detail
);
688 if (client
!= focus_client
) {
689 focus_set_client(client
);
690 frame_adjust_focus(client
->frame
, TRUE
);
691 client_calc_layer(client
);
696 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
697 e
->xfocus
.window
, client
->window
,
698 e
->xfocus
.mode
, e
->xfocus
.detail
);
701 frame_adjust_focus(client
->frame
, FALSE
);
702 client_calc_layer(client
);
705 con
= frame_context(client
, e
->xcrossing
.window
);
707 case OB_FRAME_CONTEXT_MAXIMIZE
:
708 client
->frame
->max_hover
= FALSE
;
709 frame_adjust_state(client
->frame
);
711 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
712 client
->frame
->desk_hover
= FALSE
;
713 frame_adjust_state(client
->frame
);
715 case OB_FRAME_CONTEXT_SHADE
:
716 client
->frame
->shade_hover
= FALSE
;
717 frame_adjust_state(client
->frame
);
719 case OB_FRAME_CONTEXT_ICONIFY
:
720 client
->frame
->iconify_hover
= FALSE
;
721 frame_adjust_state(client
->frame
);
723 case OB_FRAME_CONTEXT_CLOSE
:
724 client
->frame
->close_hover
= FALSE
;
725 frame_adjust_state(client
->frame
);
727 case OB_FRAME_CONTEXT_FRAME
:
728 if (config_focus_follow
&& config_focus_delay
)
729 ob_main_loop_timeout_remove_data(ob_main_loop
,
739 gboolean nofocus
= FALSE
;
741 if (ignore_enter_focus
) {
742 ignore_enter_focus
--;
746 con
= frame_context(client
, e
->xcrossing
.window
);
748 case OB_FRAME_CONTEXT_MAXIMIZE
:
749 client
->frame
->max_hover
= TRUE
;
750 frame_adjust_state(client
->frame
);
752 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
753 client
->frame
->desk_hover
= TRUE
;
754 frame_adjust_state(client
->frame
);
756 case OB_FRAME_CONTEXT_SHADE
:
757 client
->frame
->shade_hover
= TRUE
;
758 frame_adjust_state(client
->frame
);
760 case OB_FRAME_CONTEXT_ICONIFY
:
761 client
->frame
->iconify_hover
= TRUE
;
762 frame_adjust_state(client
->frame
);
764 case OB_FRAME_CONTEXT_CLOSE
:
765 client
->frame
->close_hover
= TRUE
;
766 frame_adjust_state(client
->frame
);
768 case OB_FRAME_CONTEXT_FRAME
:
769 if (e
->xcrossing
.mode
== NotifyGrab
||
770 e
->xcrossing
.mode
== NotifyUngrab
)
773 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
774 (e
->type
== EnterNotify
? "Enter" : "Leave"),
776 e
->xcrossing
.detail
, client
?client
->window
:0);
780 ob_debug("%sNotify mode %d detail %d on %lx, "
781 "focusing window: %d\n",
782 (e
->type
== EnterNotify
? "Enter" : "Leave"),
784 e
->xcrossing
.detail
, (client
?client
->window
:0),
787 if (!nofocus
&& config_focus_follow
)
788 event_enter_client(client
);
796 case ConfigureRequest
:
798 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
799 ConfigureRequest
, &ce
)) {
801 /* XXX if this causes bad things.. we can compress config req's
802 with the same mask. */
803 e
->xconfigurerequest
.value_mask
|=
804 ce
.xconfigurerequest
.value_mask
;
805 if (ce
.xconfigurerequest
.value_mask
& CWX
)
806 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
807 if (ce
.xconfigurerequest
.value_mask
& CWY
)
808 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
809 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
810 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
811 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
812 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
813 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
814 e
->xconfigurerequest
.border_width
=
815 ce
.xconfigurerequest
.border_width
;
816 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
817 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
820 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
821 if (client
->iconic
|| client
->shaded
) return;
823 /* resize, then move, as specified in the EWMH section 7.7 */
824 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
830 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
831 client
->border_width
= e
->xconfigurerequest
.border_width
;
833 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
834 e
->xconfigurerequest
.x
: client
->area
.x
;
835 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
836 e
->xconfigurerequest
.y
: client
->area
.y
;
837 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
838 e
->xconfigurerequest
.width
: client
->area
.width
;
839 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
840 e
->xconfigurerequest
.height
: client
->area
.height
;
846 client
->frame
->size
.left
+ client
->frame
->size
.right
;
848 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
849 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
850 client_normal(client
));
851 if (e
->xconfigurerequest
.value_mask
& CWX
)
853 if (e
->xconfigurerequest
.value_mask
& CWY
)
857 switch (client
->gravity
) {
858 case NorthEastGravity
:
860 corner
= OB_CORNER_TOPRIGHT
;
862 case SouthWestGravity
:
864 corner
= OB_CORNER_BOTTOMLEFT
;
866 case SouthEastGravity
:
867 corner
= OB_CORNER_BOTTOMRIGHT
;
869 default: /* NorthWest, Static, etc */
870 corner
= OB_CORNER_TOPLEFT
;
873 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
877 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
878 switch (e
->xconfigurerequest
.detail
) {
881 client_lower(client
);
887 client_raise(client
);
893 if (client
->ignore_unmaps
) {
894 client
->ignore_unmaps
--;
897 client_unmanage(client
);
900 client_unmanage(client
);
903 /* this is when the client is first taken captive in the frame */
904 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
907 This event is quite rare and is usually handled in unmapHandler.
908 However, if the window is unmapped when the reparent event occurs,
909 the window manager never sees it because an unmap event is not sent
910 to an already unmapped window.
913 /* we don't want the reparent event, put it back on the stack for the
914 X server to deal with after we unmanage the window */
915 XPutBackEvent(ob_display
, e
);
917 client_unmanage(client
);
920 ob_debug("MapRequest for 0x%lx\n", client
->window
);
921 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
922 does, we don't want it!
923 it can happen now when the window is on
924 another desktop, but we still don't
926 client_activate(client
, FALSE
);
929 /* validate cuz we query stuff off the client here */
930 if (!client_validate(client
)) break;
932 if (e
->xclient
.format
!= 32) return;
934 msgtype
= e
->xclient
.message_type
;
935 if (msgtype
== prop_atoms
.wm_change_state
) {
936 /* compress changes into a single change */
937 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
939 /* XXX: it would be nice to compress ALL messages of a
940 type, not just messages in a row without other
941 message types between. */
942 if (ce
.xclient
.message_type
!= msgtype
) {
943 XPutBackEvent(ob_display
, &ce
);
946 e
->xclient
= ce
.xclient
;
948 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
949 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
950 /* compress changes into a single change */
951 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
953 /* XXX: it would be nice to compress ALL messages of a
954 type, not just messages in a row without other
955 message types between. */
956 if (ce
.xclient
.message_type
!= msgtype
) {
957 XPutBackEvent(ob_display
, &ce
);
960 e
->xclient
= ce
.xclient
;
962 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
963 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
964 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
966 } else if (msgtype
== prop_atoms
.net_wm_state
) {
967 /* can't compress these */
968 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
969 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
970 e
->xclient
.data
.l
[0] == 1 ? "Add" :
971 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
972 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
974 client_set_state(client
, e
->xclient
.data
.l
[0],
975 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
976 } else if (msgtype
== prop_atoms
.net_close_window
) {
977 ob_debug("net_close_window for 0x%lx\n", client
->window
);
978 client_close(client
);
979 } else if (msgtype
== prop_atoms
.net_active_window
) {
980 ob_debug("net_active_window for 0x%lx\n", client
->window
);
981 client_activate(client
, FALSE
);
982 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
983 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
984 if ((Atom
)e
->xclient
.data
.l
[2] ==
985 prop_atoms
.net_wm_moveresize_size_topleft
||
986 (Atom
)e
->xclient
.data
.l
[2] ==
987 prop_atoms
.net_wm_moveresize_size_top
||
988 (Atom
)e
->xclient
.data
.l
[2] ==
989 prop_atoms
.net_wm_moveresize_size_topright
||
990 (Atom
)e
->xclient
.data
.l
[2] ==
991 prop_atoms
.net_wm_moveresize_size_right
||
992 (Atom
)e
->xclient
.data
.l
[2] ==
993 prop_atoms
.net_wm_moveresize_size_right
||
994 (Atom
)e
->xclient
.data
.l
[2] ==
995 prop_atoms
.net_wm_moveresize_size_bottomright
||
996 (Atom
)e
->xclient
.data
.l
[2] ==
997 prop_atoms
.net_wm_moveresize_size_bottom
||
998 (Atom
)e
->xclient
.data
.l
[2] ==
999 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1000 (Atom
)e
->xclient
.data
.l
[2] ==
1001 prop_atoms
.net_wm_moveresize_size_left
||
1002 (Atom
)e
->xclient
.data
.l
[2] ==
1003 prop_atoms
.net_wm_moveresize_move
||
1004 (Atom
)e
->xclient
.data
.l
[2] ==
1005 prop_atoms
.net_wm_moveresize_size_keyboard
||
1006 (Atom
)e
->xclient
.data
.l
[2] ==
1007 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1009 moveresize_start(client
, e
->xclient
.data
.l
[0],
1010 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1011 e
->xclient
.data
.l
[2]);
1013 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1014 gint oldg
= client
->gravity
;
1015 gint tmpg
, x
, y
, w
, h
;
1017 if (e
->xclient
.data
.l
[0] & 0xff)
1018 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
1022 if (e
->xclient
.data
.l
[0] & 1 << 8)
1023 x
= e
->xclient
.data
.l
[1];
1026 if (e
->xclient
.data
.l
[0] & 1 << 9)
1027 y
= e
->xclient
.data
.l
[2];
1030 if (e
->xclient
.data
.l
[0] & 1 << 10)
1031 w
= e
->xclient
.data
.l
[3];
1033 w
= client
->area
.width
;
1034 if (e
->xclient
.data
.l
[0] & 1 << 11)
1035 h
= e
->xclient
.data
.l
[4];
1037 h
= client
->area
.height
;
1038 client
->gravity
= tmpg
;
1044 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1046 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1047 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1048 client_normal(client
));
1049 if (e
->xclient
.data
.l
[0] & 1 << 8)
1051 if (e
->xclient
.data
.l
[0] & 1 << 9)
1055 client_configure(client
, OB_CORNER_TOPLEFT
,
1056 x
, y
, w
, h
, FALSE
, TRUE
);
1058 client
->gravity
= oldg
;
1061 case PropertyNotify
:
1062 /* validate cuz we query stuff off the client here */
1063 if (!client_validate(client
)) break;
1065 /* compress changes to a single property into a single change */
1066 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1070 /* XXX: it would be nice to compress ALL changes to a property,
1071 not just changes in a row without other props between. */
1073 a
= ce
.xproperty
.atom
;
1074 b
= e
->xproperty
.atom
;
1078 if ((a
== prop_atoms
.net_wm_name
||
1079 a
== prop_atoms
.wm_name
||
1080 a
== prop_atoms
.net_wm_icon_name
||
1081 a
== prop_atoms
.wm_icon_name
)
1083 (b
== prop_atoms
.net_wm_name
||
1084 b
== prop_atoms
.wm_name
||
1085 b
== prop_atoms
.net_wm_icon_name
||
1086 b
== prop_atoms
.wm_icon_name
)) {
1089 if ((a
== prop_atoms
.net_wm_icon
||
1090 a
== prop_atoms
.kwm_win_icon
)
1092 (b
== prop_atoms
.net_wm_icon
||
1093 b
== prop_atoms
.kwm_win_icon
))
1096 XPutBackEvent(ob_display
, &ce
);
1100 msgtype
= e
->xproperty
.atom
;
1101 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1102 client_update_normal_hints(client
);
1103 /* normal hints can make a window non-resizable */
1104 client_setup_decor_and_functions(client
);
1105 } else if (msgtype
== XA_WM_HINTS
) {
1106 client_update_wmhints(client
);
1107 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1108 client_update_transient_for(client
);
1109 client_get_type(client
);
1110 /* type may have changed, so update the layer */
1111 client_calc_layer(client
);
1112 client_setup_decor_and_functions(client
);
1113 } else if (msgtype
== prop_atoms
.net_wm_name
||
1114 msgtype
== prop_atoms
.wm_name
||
1115 msgtype
== prop_atoms
.net_wm_icon_name
||
1116 msgtype
== prop_atoms
.wm_icon_name
) {
1117 client_update_title(client
);
1118 } else if (msgtype
== prop_atoms
.wm_class
) {
1119 client_update_class(client
);
1120 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1121 client_update_protocols(client
);
1122 client_setup_decor_and_functions(client
);
1124 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1125 client_update_strut(client
);
1127 else if (msgtype
== prop_atoms
.net_wm_icon
||
1128 msgtype
== prop_atoms
.kwm_win_icon
) {
1129 client_update_icons(client
);
1131 else if (msgtype
== prop_atoms
.sm_client_id
) {
1132 client_update_sm_client_id(client
);
1137 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1138 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1139 frame_adjust_shape(client
->frame
);
1145 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1149 if (e
->xbutton
.button
== 1)
1150 stacking_raise(DOCK_AS_WINDOW(s
), FALSE
);
1151 else if (e
->xbutton
.button
== 2)
1152 stacking_lower(DOCK_AS_WINDOW(s
), FALSE
);
1163 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1167 dock_app_drag(app
, &e
->xmotion
);
1170 if (app
->ignore_unmaps
) {
1171 app
->ignore_unmaps
--;
1174 dock_remove(app
, TRUE
);
1177 dock_remove(app
, FALSE
);
1179 case ReparentNotify
:
1180 dock_remove(app
, FALSE
);
1182 case ConfigureNotify
:
1183 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1188 ObMenuFrame
* find_active_menu()
1191 ObMenuFrame
*ret
= NULL
;
1193 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1202 ObMenuFrame
* find_active_or_last_menu()
1204 ObMenuFrame
*ret
= NULL
;
1206 ret
= find_active_menu();
1207 if (!ret
&& menu_frame_visible
)
1208 ret
= menu_frame_visible
->data
;
1212 static void event_handle_menu(XEvent
*ev
)
1215 ObMenuEntryFrame
*e
;
1219 if (menu_can_hide
) {
1220 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1221 ev
->xbutton
.y_root
)))
1222 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1224 menu_frame_hide_all();
1228 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1229 ev
->xmotion
.y_root
))) {
1230 menu_frame_move_on_screen(f
);
1231 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1232 ev
->xmotion
.y_root
)))
1233 menu_frame_select(f
, e
);
1238 a
= find_active_menu();
1240 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1242 menu_frame_select(a
, NULL
);
1247 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1248 menu_frame_hide_all();
1249 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1251 if ((f
= find_active_menu()))
1252 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1253 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1255 if ((f
= find_active_or_last_menu()) && f
->parent
)
1256 menu_frame_select(f
, NULL
);
1257 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1259 if ((f
= find_active_or_last_menu()) && f
->child
)
1260 menu_frame_select_next(f
->child
);
1261 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1263 if ((f
= find_active_or_last_menu()))
1264 menu_frame_select_previous(f
);
1265 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1267 if ((f
= find_active_or_last_menu()))
1268 menu_frame_select_next(f
);
1274 static gboolean
menu_hide_delay_func(gpointer data
)
1276 menu_can_hide
= TRUE
;
1277 return FALSE
; /* no repeat */
1280 static gboolean
focus_delay_func(gpointer data
)
1284 if (focus_client
!= c
) {
1286 if (config_focus_raise
)
1289 return FALSE
; /* no repeat */
1292 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1294 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
, client
);
1297 static void event_client_dest(ObClient
*client
, gpointer data
)
1299 if (client
== focus_hilite
)
1300 focus_hilite
= NULL
;
1303 void event_halt_focus_delay()
1305 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1308 void event_ignore_queued_enters()
1310 GSList
*saved
= NULL
, *it
;
1313 XSync(ob_display
, FALSE
);
1315 /* count the events */
1317 e
= g_new(XEvent
, 1);
1318 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1321 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1322 if (win
&& WINDOW_IS_CLIENT(win
))
1323 ++ignore_enter_focus
;
1325 saved
= g_slist_append(saved
, e
);
1331 /* put the events back */
1332 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1333 XPutBackEvent(ob_display
, it
->data
);
1336 g_slist_free(saved
);