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
);
606 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
610 g_assert(e
->type
== PropertyNotify
);
612 for (it
= group
->members
; it
; it
= g_slist_next(it
))
613 event_handle_client(it
->data
, e
);
616 void event_enter_client(ObClient
*client
)
618 g_assert(config_focus_follow
);
620 if (client_normal(client
) && client_can_focus(client
)) {
621 if (config_focus_delay
) {
622 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
623 ob_main_loop_timeout_add(ob_main_loop
,
628 focus_delay_func(client
);
632 static void event_handle_client(ObClient
*client
, XEvent
*e
)
640 case VisibilityNotify
:
641 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
645 /* Wheel buttons don't draw because they are an instant click, so it
646 is a waste of resources to go drawing it. */
647 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
648 con
= frame_context(client
, e
->xbutton
.window
);
649 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
651 case OB_FRAME_CONTEXT_MAXIMIZE
:
652 client
->frame
->max_press
= (e
->type
== ButtonPress
);
653 framerender_frame(client
->frame
);
655 case OB_FRAME_CONTEXT_CLOSE
:
656 client
->frame
->close_press
= (e
->type
== ButtonPress
);
657 framerender_frame(client
->frame
);
659 case OB_FRAME_CONTEXT_ICONIFY
:
660 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
661 framerender_frame(client
->frame
);
663 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
664 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
665 framerender_frame(client
->frame
);
667 case OB_FRAME_CONTEXT_SHADE
:
668 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
669 framerender_frame(client
->frame
);
672 /* nothing changes with clicks for any other contexts */
679 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
680 e
->xfocus
.window
, client
->window
,
681 e
->xfocus
.mode
, e
->xfocus
.detail
);
683 if (client
!= focus_client
) {
684 focus_set_client(client
);
685 frame_adjust_focus(client
->frame
, TRUE
);
686 client_calc_layer(client
);
691 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
692 e
->xfocus
.window
, client
->window
,
693 e
->xfocus
.mode
, e
->xfocus
.detail
);
696 frame_adjust_focus(client
->frame
, FALSE
);
697 client_calc_layer(client
);
700 con
= frame_context(client
, e
->xcrossing
.window
);
702 case OB_FRAME_CONTEXT_MAXIMIZE
:
703 client
->frame
->max_hover
= FALSE
;
704 frame_adjust_state(client
->frame
);
706 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
707 client
->frame
->desk_hover
= FALSE
;
708 frame_adjust_state(client
->frame
);
710 case OB_FRAME_CONTEXT_SHADE
:
711 client
->frame
->shade_hover
= FALSE
;
712 frame_adjust_state(client
->frame
);
714 case OB_FRAME_CONTEXT_ICONIFY
:
715 client
->frame
->iconify_hover
= FALSE
;
716 frame_adjust_state(client
->frame
);
718 case OB_FRAME_CONTEXT_CLOSE
:
719 client
->frame
->close_hover
= FALSE
;
720 frame_adjust_state(client
->frame
);
722 case OB_FRAME_CONTEXT_FRAME
:
723 if (config_focus_follow
&& config_focus_delay
)
724 ob_main_loop_timeout_remove_data(ob_main_loop
,
734 gboolean nofocus
= FALSE
;
736 if (ignore_enter_focus
) {
737 ignore_enter_focus
--;
741 con
= frame_context(client
, e
->xcrossing
.window
);
743 case OB_FRAME_CONTEXT_MAXIMIZE
:
744 client
->frame
->max_hover
= TRUE
;
745 frame_adjust_state(client
->frame
);
747 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
748 client
->frame
->desk_hover
= TRUE
;
749 frame_adjust_state(client
->frame
);
751 case OB_FRAME_CONTEXT_SHADE
:
752 client
->frame
->shade_hover
= TRUE
;
753 frame_adjust_state(client
->frame
);
755 case OB_FRAME_CONTEXT_ICONIFY
:
756 client
->frame
->iconify_hover
= TRUE
;
757 frame_adjust_state(client
->frame
);
759 case OB_FRAME_CONTEXT_CLOSE
:
760 client
->frame
->close_hover
= TRUE
;
761 frame_adjust_state(client
->frame
);
763 case OB_FRAME_CONTEXT_FRAME
:
764 if (e
->xcrossing
.mode
== NotifyGrab
||
765 e
->xcrossing
.mode
== NotifyUngrab
)
768 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
769 (e
->type
== EnterNotify
? "Enter" : "Leave"),
771 e
->xcrossing
.detail
, client
?client
->window
:0);
775 ob_debug("%sNotify mode %d detail %d on %lx, "
776 "focusing window: %d\n",
777 (e
->type
== EnterNotify
? "Enter" : "Leave"),
779 e
->xcrossing
.detail
, (client
?client
->window
:0),
782 if (!nofocus
&& config_focus_follow
)
783 event_enter_client(client
);
791 case ConfigureRequest
:
793 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
794 ConfigureRequest
, &ce
)) {
796 /* XXX if this causes bad things.. we can compress config req's
797 with the same mask. */
798 e
->xconfigurerequest
.value_mask
|=
799 ce
.xconfigurerequest
.value_mask
;
800 if (ce
.xconfigurerequest
.value_mask
& CWX
)
801 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
802 if (ce
.xconfigurerequest
.value_mask
& CWY
)
803 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
804 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
805 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
806 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
807 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
808 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
809 e
->xconfigurerequest
.border_width
=
810 ce
.xconfigurerequest
.border_width
;
811 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
812 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
815 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
816 if (client
->iconic
|| client
->shaded
) return;
818 /* resize, then move, as specified in the EWMH section 7.7 */
819 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
825 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
826 client
->border_width
= e
->xconfigurerequest
.border_width
;
828 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
829 e
->xconfigurerequest
.x
: client
->area
.x
;
830 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
831 e
->xconfigurerequest
.y
: client
->area
.y
;
832 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
833 e
->xconfigurerequest
.width
: client
->area
.width
;
834 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
835 e
->xconfigurerequest
.height
: client
->area
.height
;
841 client
->frame
->size
.left
+ client
->frame
->size
.right
;
843 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
844 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
845 client_normal(client
));
846 if (e
->xconfigurerequest
.value_mask
& CWX
)
848 if (e
->xconfigurerequest
.value_mask
& CWY
)
852 switch (client
->gravity
) {
853 case NorthEastGravity
:
855 corner
= OB_CORNER_TOPRIGHT
;
857 case SouthWestGravity
:
859 corner
= OB_CORNER_BOTTOMLEFT
;
861 case SouthEastGravity
:
862 corner
= OB_CORNER_BOTTOMRIGHT
;
864 default: /* NorthWest, Static, etc */
865 corner
= OB_CORNER_TOPLEFT
;
868 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
872 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
873 switch (e
->xconfigurerequest
.detail
) {
876 client_lower(client
);
882 client_raise(client
);
888 if (client
->ignore_unmaps
) {
889 client
->ignore_unmaps
--;
892 client_unmanage(client
);
895 client_unmanage(client
);
898 /* this is when the client is first taken captive in the frame */
899 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
902 This event is quite rare and is usually handled in unmapHandler.
903 However, if the window is unmapped when the reparent event occurs,
904 the window manager never sees it because an unmap event is not sent
905 to an already unmapped window.
908 /* we don't want the reparent event, put it back on the stack for the
909 X server to deal with after we unmanage the window */
910 XPutBackEvent(ob_display
, e
);
912 client_unmanage(client
);
915 ob_debug("MapRequest for 0x%lx\n", client
->window
);
916 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
917 does, we don't want it!
918 it can happen now when the window is on
919 another desktop, but we still don't
921 client_activate(client
, FALSE
);
924 /* validate cuz we query stuff off the client here */
925 if (!client_validate(client
)) break;
927 if (e
->xclient
.format
!= 32) return;
929 msgtype
= e
->xclient
.message_type
;
930 if (msgtype
== prop_atoms
.wm_change_state
) {
931 /* compress changes into a single change */
932 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
934 /* XXX: it would be nice to compress ALL messages of a
935 type, not just messages in a row without other
936 message types between. */
937 if (ce
.xclient
.message_type
!= msgtype
) {
938 XPutBackEvent(ob_display
, &ce
);
941 e
->xclient
= ce
.xclient
;
943 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
944 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
945 /* compress changes into a single change */
946 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
948 /* XXX: it would be nice to compress ALL messages of a
949 type, not just messages in a row without other
950 message types between. */
951 if (ce
.xclient
.message_type
!= msgtype
) {
952 XPutBackEvent(ob_display
, &ce
);
955 e
->xclient
= ce
.xclient
;
957 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
958 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
959 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
961 } else if (msgtype
== prop_atoms
.net_wm_state
) {
962 /* can't compress these */
963 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
964 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
965 e
->xclient
.data
.l
[0] == 1 ? "Add" :
966 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
967 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
969 client_set_state(client
, e
->xclient
.data
.l
[0],
970 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
971 } else if (msgtype
== prop_atoms
.net_close_window
) {
972 ob_debug("net_close_window for 0x%lx\n", client
->window
);
973 client_close(client
);
974 } else if (msgtype
== prop_atoms
.net_active_window
) {
975 ob_debug("net_active_window for 0x%lx\n", client
->window
);
976 client_activate(client
, FALSE
);
977 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
978 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
979 if ((Atom
)e
->xclient
.data
.l
[2] ==
980 prop_atoms
.net_wm_moveresize_size_topleft
||
981 (Atom
)e
->xclient
.data
.l
[2] ==
982 prop_atoms
.net_wm_moveresize_size_top
||
983 (Atom
)e
->xclient
.data
.l
[2] ==
984 prop_atoms
.net_wm_moveresize_size_topright
||
985 (Atom
)e
->xclient
.data
.l
[2] ==
986 prop_atoms
.net_wm_moveresize_size_right
||
987 (Atom
)e
->xclient
.data
.l
[2] ==
988 prop_atoms
.net_wm_moveresize_size_right
||
989 (Atom
)e
->xclient
.data
.l
[2] ==
990 prop_atoms
.net_wm_moveresize_size_bottomright
||
991 (Atom
)e
->xclient
.data
.l
[2] ==
992 prop_atoms
.net_wm_moveresize_size_bottom
||
993 (Atom
)e
->xclient
.data
.l
[2] ==
994 prop_atoms
.net_wm_moveresize_size_bottomleft
||
995 (Atom
)e
->xclient
.data
.l
[2] ==
996 prop_atoms
.net_wm_moveresize_size_left
||
997 (Atom
)e
->xclient
.data
.l
[2] ==
998 prop_atoms
.net_wm_moveresize_move
||
999 (Atom
)e
->xclient
.data
.l
[2] ==
1000 prop_atoms
.net_wm_moveresize_size_keyboard
||
1001 (Atom
)e
->xclient
.data
.l
[2] ==
1002 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1004 moveresize_start(client
, e
->xclient
.data
.l
[0],
1005 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1006 e
->xclient
.data
.l
[2]);
1008 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1009 gint oldg
= client
->gravity
;
1010 gint tmpg
, x
, y
, w
, h
;
1012 if (e
->xclient
.data
.l
[0] & 0xff)
1013 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
1017 if (e
->xclient
.data
.l
[0] & 1 << 8)
1018 x
= e
->xclient
.data
.l
[1];
1021 if (e
->xclient
.data
.l
[0] & 1 << 9)
1022 y
= e
->xclient
.data
.l
[2];
1025 if (e
->xclient
.data
.l
[0] & 1 << 10)
1026 w
= e
->xclient
.data
.l
[3];
1028 w
= client
->area
.width
;
1029 if (e
->xclient
.data
.l
[0] & 1 << 11)
1030 h
= e
->xclient
.data
.l
[4];
1032 h
= client
->area
.height
;
1033 client
->gravity
= tmpg
;
1039 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1041 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1042 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1043 client_normal(client
));
1044 if (e
->xclient
.data
.l
[0] & 1 << 8)
1046 if (e
->xclient
.data
.l
[0] & 1 << 9)
1050 client_configure(client
, OB_CORNER_TOPLEFT
,
1051 x
, y
, w
, h
, FALSE
, TRUE
);
1053 client
->gravity
= oldg
;
1056 case PropertyNotify
:
1057 /* validate cuz we query stuff off the client here */
1058 if (!client_validate(client
)) break;
1060 /* compress changes to a single property into a single change */
1061 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1065 /* XXX: it would be nice to compress ALL changes to a property,
1066 not just changes in a row without other props between. */
1068 a
= ce
.xproperty
.atom
;
1069 b
= e
->xproperty
.atom
;
1073 if ((a
== prop_atoms
.net_wm_name
||
1074 a
== prop_atoms
.wm_name
||
1075 a
== prop_atoms
.net_wm_icon_name
||
1076 a
== prop_atoms
.wm_icon_name
)
1078 (b
== prop_atoms
.net_wm_name
||
1079 b
== prop_atoms
.wm_name
||
1080 b
== prop_atoms
.net_wm_icon_name
||
1081 b
== prop_atoms
.wm_icon_name
)) {
1084 if ((a
== prop_atoms
.net_wm_icon
||
1085 a
== prop_atoms
.kwm_win_icon
)
1087 (b
== prop_atoms
.net_wm_icon
||
1088 b
== prop_atoms
.kwm_win_icon
))
1091 XPutBackEvent(ob_display
, &ce
);
1095 msgtype
= e
->xproperty
.atom
;
1096 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1097 client_update_normal_hints(client
);
1098 /* normal hints can make a window non-resizable */
1099 client_setup_decor_and_functions(client
);
1100 } else if (msgtype
== XA_WM_HINTS
) {
1101 client_update_wmhints(client
);
1102 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1103 client_update_transient_for(client
);
1104 client_get_type(client
);
1105 /* type may have changed, so update the layer */
1106 client_calc_layer(client
);
1107 client_setup_decor_and_functions(client
);
1108 } else if (msgtype
== prop_atoms
.net_wm_name
||
1109 msgtype
== prop_atoms
.wm_name
||
1110 msgtype
== prop_atoms
.net_wm_icon_name
||
1111 msgtype
== prop_atoms
.wm_icon_name
) {
1112 client_update_title(client
);
1113 } else if (msgtype
== prop_atoms
.wm_class
) {
1114 client_update_class(client
);
1115 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1116 client_update_protocols(client
);
1117 client_setup_decor_and_functions(client
);
1119 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1120 client_update_strut(client
);
1122 else if (msgtype
== prop_atoms
.net_wm_icon
||
1123 msgtype
== prop_atoms
.kwm_win_icon
) {
1124 client_update_icons(client
);
1126 else if (msgtype
== prop_atoms
.sm_client_id
) {
1127 client_update_sm_client_id(client
);
1132 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1133 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1134 frame_adjust_shape(client
->frame
);
1140 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1144 if (e
->xbutton
.button
== 1)
1145 stacking_raise(DOCK_AS_WINDOW(s
), FALSE
);
1146 else if (e
->xbutton
.button
== 2)
1147 stacking_lower(DOCK_AS_WINDOW(s
), FALSE
);
1158 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1162 dock_app_drag(app
, &e
->xmotion
);
1165 if (app
->ignore_unmaps
) {
1166 app
->ignore_unmaps
--;
1169 dock_remove(app
, TRUE
);
1172 dock_remove(app
, FALSE
);
1174 case ReparentNotify
:
1175 dock_remove(app
, FALSE
);
1177 case ConfigureNotify
:
1178 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1183 ObMenuFrame
* find_active_menu()
1186 ObMenuFrame
*ret
= NULL
;
1188 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1197 ObMenuFrame
* find_active_or_last_menu()
1199 ObMenuFrame
*ret
= NULL
;
1201 ret
= find_active_menu();
1202 if (!ret
&& menu_frame_visible
)
1203 ret
= menu_frame_visible
->data
;
1207 static void event_handle_menu(XEvent
*ev
)
1210 ObMenuEntryFrame
*e
;
1214 if (menu_can_hide
) {
1215 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1216 ev
->xbutton
.y_root
)))
1217 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1219 menu_frame_hide_all();
1223 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1224 ev
->xmotion
.y_root
))) {
1225 menu_frame_move_on_screen(f
);
1226 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1227 ev
->xmotion
.y_root
)))
1228 menu_frame_select(f
, e
);
1233 a
= find_active_menu();
1235 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1237 menu_frame_select(a
, NULL
);
1242 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1243 menu_frame_hide_all();
1244 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1246 if ((f
= find_active_menu()))
1247 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1248 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1250 if ((f
= find_active_or_last_menu()) && f
->parent
)
1251 menu_frame_select(f
, NULL
);
1252 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1254 if ((f
= find_active_or_last_menu()) && f
->child
)
1255 menu_frame_select_next(f
->child
);
1256 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1258 if ((f
= find_active_or_last_menu()))
1259 menu_frame_select_previous(f
);
1260 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1262 if ((f
= find_active_or_last_menu()))
1263 menu_frame_select_next(f
);
1269 static gboolean
menu_hide_delay_func(gpointer data
)
1271 menu_can_hide
= TRUE
;
1272 return FALSE
; /* no repeat */
1275 static gboolean
focus_delay_func(gpointer data
)
1279 if (focus_client
!= c
) {
1281 if (config_focus_raise
)
1284 return FALSE
; /* no repeat */
1287 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1289 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
, client
);
1292 static void event_client_dest(ObClient
*client
, gpointer data
)
1294 if (client
== focus_hilite
)
1295 focus_hilite
= NULL
;
1298 void event_halt_focus_delay()
1300 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1303 void event_ignore_queued_enters()
1305 GSList
*saved
= NULL
, *it
;
1308 XSync(ob_display
, FALSE
);
1310 /* count the events */
1312 e
= g_new(XEvent
, 1);
1313 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1316 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1317 if (win
&& WINDOW_IS_CLIENT(win
))
1318 ++ignore_enter_focus
;
1320 saved
= g_slist_append(saved
, e
);
1326 /* put the events back */
1327 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1328 XPutBackEvent(ob_display
, it
->data
);
1331 g_slist_free(saved
);