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>
56 #include <X11/ICE/ICElib.h>
64 static void event_process(const XEvent
*e
, gpointer data
);
65 static void event_client_dest(ObClient
*client
, gpointer data
);
66 static void event_handle_root(XEvent
*e
);
67 static void event_handle_menu(XEvent
*e
);
68 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
69 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
70 static void event_handle_client(ObClient
*c
, XEvent
*e
);
71 static void event_handle_group(ObGroup
*g
, XEvent
*e
);
73 static gboolean
focus_delay_func(gpointer data
);
74 static void focus_delay_client_dest(ObClient
*client
, gpointer data
);
76 static gboolean
menu_hide_delay_func(gpointer data
);
78 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
79 (e)->xfocus.detail == NotifyAncestor || \
80 (e)->xfocus.detail > NotifyNonlinearVirtual)
81 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
82 (e)->xfocus.detail == NotifyInferior || \
83 (e)->xfocus.detail == NotifyAncestor || \
84 (e)->xfocus.detail > NotifyNonlinearVirtual)
86 Time event_lasttime
= 0;
88 /*! The value of the mask for the NumLock modifier */
90 /*! The value of the mask for the ScrollLock modifier */
92 /*! The key codes for the modifier keys */
93 static XModifierKeymap
*modmap
;
94 /*! Table of the constant modifier masks */
95 static const gint mask_table
[] = {
96 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
97 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
99 static gint mask_table_size
;
101 static guint ignore_enter_focus
= 0;
103 static gboolean menu_can_hide
;
106 static void ice_handler(gint fd
, gpointer conn
)
109 IceProcessMessages(conn
, NULL
, &b
);
112 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
113 IcePointer
*watch_data
)
118 fd
= IceConnectionNumber(conn
);
119 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
121 ob_main_loop_fd_remove(ob_main_loop
, fd
);
127 void event_startup(gboolean reconfig
)
129 if (reconfig
) return;
131 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
133 /* get lock masks that are defined by the display (not constant) */
134 modmap
= XGetModifierMapping(ob_display
);
136 if (modmap
&& modmap
->max_keypermod
> 0) {
138 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
139 /* get the values of the keyboard lock modifiers
140 Note: Caps lock is not retrieved the same way as Scroll and Num
141 lock since it doesn't need to be. */
142 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
143 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
146 for (cnt
= 0; cnt
< size
; ++cnt
) {
147 if (! modmap
->modifiermap
[cnt
]) continue;
149 if (num_lock
== modmap
->modifiermap
[cnt
])
150 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
151 if (scroll_lock
== modmap
->modifiermap
[cnt
])
152 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
156 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
159 IceAddConnectionWatch(ice_watch
, NULL
);
162 client_add_destructor(focus_delay_client_dest
, NULL
);
163 client_add_destructor(event_client_dest
, NULL
);
166 void event_shutdown(gboolean reconfig
)
168 if (reconfig
) return;
171 IceRemoveConnectionWatch(ice_watch
, NULL
);
174 client_remove_destructor(focus_delay_client_dest
);
175 XFreeModifiermap(modmap
);
178 static Window
event_get_window(XEvent
*e
)
185 window
= RootWindow(ob_display
, ob_screen
);
188 window
= e
->xmap
.window
;
191 window
= e
->xunmap
.window
;
194 window
= e
->xdestroywindow
.window
;
196 case ConfigureRequest
:
197 window
= e
->xconfigurerequest
.window
;
199 case ConfigureNotify
:
200 window
= e
->xconfigure
.window
;
204 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
205 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
207 window
= ((XkbBellNotifyEvent
*)e
)->window
;
213 window
= e
->xany
.window
;
218 static void event_set_lasttime(XEvent
*e
)
222 /* grab the lasttime and hack up the state */
238 t
= e
->xproperty
.time
;
242 t
= e
->xcrossing
.time
;
245 /* if more event types are anticipated, get their timestamp
250 if (t
> event_lasttime
)
254 #define STRIP_MODS(s) \
255 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
256 /* kill off the Button1Mask etc, only want the modifiers */ \
257 s &= (ControlMask | ShiftMask | Mod1Mask | \
258 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
260 static void event_hack_mods(XEvent *e)
268 STRIP_MODS(e
->xbutton
.state
);
271 STRIP_MODS(e
->xkey
.state
);
274 STRIP_MODS(e
->xkey
.state
);
275 /* remove from the state the mask of the modifier being released, if
276 it is a modifier key being released (this is a little ugly..) */
277 kp
= modmap
->modifiermap
;
278 for (i
= 0; i
< mask_table_size
; ++i
) {
279 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
280 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
281 /* remove the mask for it */
282 e
->xkey
.state
&= ~mask_table
[i
];
283 /* cause the first loop to break; */
285 break; /* get outta here! */
292 STRIP_MODS(e
->xmotion
.state
);
293 /* compress events */
296 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
298 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
299 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
306 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
311 if (e
->xcrossing
.detail
== NotifyInferior
)
315 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
316 because of RevertToPointerRoot. If the focus ends up reverting to
317 pointer root on a workspace change, then the FocusIn event that we
318 want will be of type NotifyAncestor. This situation does not occur
319 for FocusOut, so it is safely ignored there.
321 if (INVALID_FOCUSIN(e
) ||
324 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
325 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
327 /* says a client was not found for the event (or a valid FocusIn
330 e
->xfocus
.window
= None
;
335 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
336 e
->xfocus
.mode
, e
->xfocus
.detail
);
340 if (INVALID_FOCUSOUT(e
)) {
342 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
343 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
349 ob_debug("FocusOut on %lx mode %d detail %d\n",
350 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
354 gboolean fallback
= TRUE
;
357 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
359 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
361 if (fe
.type
== FocusOut
) {
363 ob_debug("found pending FocusOut\n");
365 if (!INVALID_FOCUSOUT(&fe
)) {
366 /* if there is a VALID FocusOut still coming, don't
367 fallback focus yet, we'll deal with it then */
368 XPutBackEvent(ob_display
, &fe
);
374 ob_debug("found pending FocusIn\n");
376 /* is the focused window getting a FocusOut/In back to
379 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
380 !event_ignore(&fe
, client
)) {
382 if focus_client is not set, then we can't do
383 this. we need the FocusIn. This happens in the
384 case when the set_focus_client(NULL) in the
385 focus_fallback function fires and then
386 focus_fallback picks the currently focused
387 window (such as on a SendToDesktop-esque action.
391 ob_debug("focused window got an Out/In back to "
392 "itself IGNORED both\n");
396 event_process(&fe
, NULL
);
398 ob_debug("focused window got an Out/In back to "
399 "itself but focus_client was null "
400 "IGNORED just the Out\n");
409 /* once all the FocusOut's have been dealt with, if
410 there is a FocusIn still left and it is valid, then
412 event_process(&fe
, &d
);
415 ob_debug("FocusIn was OK, so don't fallback\n");
425 ob_debug("no valid FocusIn and no FocusOut events found, "
428 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
436 static void event_process(const XEvent
*ec
, gpointer data
)
439 ObGroup
*group
= NULL
;
440 ObClient
*client
= NULL
;
442 ObDockApp
*dockapp
= NULL
;
443 ObWindow
*obwin
= NULL
;
445 ObEventData
*ed
= data
;
447 /* make a copy we can mangle */
451 window
= event_get_window(e
);
452 if (!(e
->type
== PropertyNotify
&&
453 (group
= g_hash_table_lookup(group_map
, &window
))))
454 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
455 switch (obwin
->type
) {
457 dock
= WINDOW_AS_DOCK(obwin
);
460 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
463 client
= WINDOW_AS_CLIENT(obwin
);
466 case Window_Internal
:
467 /* not to be used for events */
468 g_assert_not_reached();
473 event_set_lasttime(e
);
475 if (event_ignore(e
, client
)) {
482 /* deal with it in the kernel */
484 event_handle_group(group
, e
);
486 event_handle_client(client
, e
);
488 event_handle_dockapp(dockapp
, e
);
490 event_handle_dock(dock
, e
);
491 else if (window
== RootWindow(ob_display
, ob_screen
))
492 event_handle_root(e
);
493 else if (e
->type
== MapRequest
)
494 client_manage(window
);
495 else if (e
->type
== ConfigureRequest
) {
496 /* unhandled configure requests must be used to configure the
500 xwc
.x
= e
->xconfigurerequest
.x
;
501 xwc
.y
= e
->xconfigurerequest
.y
;
502 xwc
.width
= e
->xconfigurerequest
.width
;
503 xwc
.height
= e
->xconfigurerequest
.height
;
504 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
505 xwc
.sibling
= e
->xconfigurerequest
.above
;
506 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
508 /* we are not to be held responsible if someone sends us an
510 xerror_set_ignore(TRUE
);
511 XConfigureWindow(ob_display
, window
,
512 e
->xconfigurerequest
.value_mask
, &xwc
);
513 xerror_set_ignore(FALSE
);
516 /* user input (action-bound) events */
517 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
518 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
519 e
->type
== KeyRelease
)
521 if (menu_frame_visible
)
522 event_handle_menu(e
);
524 if (!keyboard_process_interactive_grab(e
, &client
)) {
525 if (moveresize_in_progress
) {
528 /* make further actions work on the client being
530 client
= moveresize_client
;
533 menu_can_hide
= FALSE
;
534 ob_main_loop_timeout_add(ob_main_loop
,
535 config_menu_hide_delay
* 1000,
536 menu_hide_delay_func
,
539 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
540 e
->type
== MotionNotify
)
541 mouse_event(client
, e
);
542 else if (e
->type
== KeyPress
)
543 keyboard_event((focus_cycle_target
? focus_cycle_target
:
544 (focus_hilite
? focus_hilite
: client
)),
551 static void event_handle_root(XEvent
*e
)
557 ob_debug("Another WM has requested to replace us. Exiting.\n");
562 if (e
->xclient
.format
!= 32) break;
564 msgtype
= e
->xclient
.message_type
;
565 if (msgtype
== prop_atoms
.net_current_desktop
) {
566 guint d
= e
->xclient
.data
.l
[0];
567 if (d
< screen_num_desktops
)
568 screen_set_desktop(d
);
569 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
570 guint d
= e
->xclient
.data
.l
[0];
572 screen_set_num_desktops(d
);
573 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
574 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
578 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
579 screen_update_desktop_names();
580 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
581 screen_update_layout();
583 case ConfigureNotify
:
585 XRRUpdateConfiguration(e
);
592 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
593 ob_debug("VIDMODE EVENT\n");
599 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
603 g_assert(e
->type
== PropertyNotify
);
605 for (it
= group
->members
; it
; it
= g_slist_next(it
))
606 event_handle_client(it
->data
, e
);
609 void event_enter_client(ObClient
*client
)
611 g_assert(config_focus_follow
);
613 if (client_normal(client
) && client_can_focus(client
)) {
614 if (config_focus_delay
) {
615 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
616 ob_main_loop_timeout_add(ob_main_loop
,
621 focus_delay_func(client
);
625 static void event_handle_client(ObClient
*client
, XEvent
*e
)
633 case VisibilityNotify
:
634 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
638 /* Wheel buttons don't draw because they are an instant click, so it
639 is a waste of resources to go drawing it. */
640 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
641 con
= frame_context(client
, e
->xbutton
.window
);
642 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
644 case OB_FRAME_CONTEXT_MAXIMIZE
:
645 client
->frame
->max_press
= (e
->type
== ButtonPress
);
646 framerender_frame(client
->frame
);
648 case OB_FRAME_CONTEXT_CLOSE
:
649 client
->frame
->close_press
= (e
->type
== ButtonPress
);
650 framerender_frame(client
->frame
);
652 case OB_FRAME_CONTEXT_ICONIFY
:
653 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
654 framerender_frame(client
->frame
);
656 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
657 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
658 framerender_frame(client
->frame
);
660 case OB_FRAME_CONTEXT_SHADE
:
661 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
662 framerender_frame(client
->frame
);
665 /* nothing changes with clicks for any other contexts */
672 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
673 e
->xfocus
.window
, client
->window
,
674 e
->xfocus
.mode
, e
->xfocus
.detail
);
676 if (client
!= focus_client
) {
677 focus_set_client(client
);
678 frame_adjust_focus(client
->frame
, TRUE
);
679 client_calc_layer(client
);
684 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
685 e
->xfocus
.window
, client
->window
,
686 e
->xfocus
.mode
, e
->xfocus
.detail
);
689 frame_adjust_focus(client
->frame
, FALSE
);
690 client_calc_layer(client
);
693 con
= frame_context(client
, e
->xcrossing
.window
);
695 case OB_FRAME_CONTEXT_MAXIMIZE
:
696 client
->frame
->max_hover
= FALSE
;
697 frame_adjust_state(client
->frame
);
699 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
700 client
->frame
->desk_hover
= FALSE
;
701 frame_adjust_state(client
->frame
);
703 case OB_FRAME_CONTEXT_SHADE
:
704 client
->frame
->shade_hover
= FALSE
;
705 frame_adjust_state(client
->frame
);
707 case OB_FRAME_CONTEXT_ICONIFY
:
708 client
->frame
->iconify_hover
= FALSE
;
709 frame_adjust_state(client
->frame
);
711 case OB_FRAME_CONTEXT_CLOSE
:
712 client
->frame
->close_hover
= FALSE
;
713 frame_adjust_state(client
->frame
);
715 case OB_FRAME_CONTEXT_FRAME
:
716 if (config_focus_follow
&& config_focus_delay
)
717 ob_main_loop_timeout_remove_data(ob_main_loop
,
727 gboolean nofocus
= FALSE
;
729 if (ignore_enter_focus
) {
730 ignore_enter_focus
--;
734 con
= frame_context(client
, e
->xcrossing
.window
);
736 case OB_FRAME_CONTEXT_MAXIMIZE
:
737 client
->frame
->max_hover
= TRUE
;
738 frame_adjust_state(client
->frame
);
740 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
741 client
->frame
->desk_hover
= TRUE
;
742 frame_adjust_state(client
->frame
);
744 case OB_FRAME_CONTEXT_SHADE
:
745 client
->frame
->shade_hover
= TRUE
;
746 frame_adjust_state(client
->frame
);
748 case OB_FRAME_CONTEXT_ICONIFY
:
749 client
->frame
->iconify_hover
= TRUE
;
750 frame_adjust_state(client
->frame
);
752 case OB_FRAME_CONTEXT_CLOSE
:
753 client
->frame
->close_hover
= TRUE
;
754 frame_adjust_state(client
->frame
);
756 case OB_FRAME_CONTEXT_FRAME
:
757 if (e
->xcrossing
.mode
== NotifyGrab
||
758 e
->xcrossing
.mode
== NotifyUngrab
)
761 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
762 (e
->type
== EnterNotify
? "Enter" : "Leave"),
764 e
->xcrossing
.detail
, client
?client
->window
:0);
768 ob_debug("%sNotify mode %d detail %d on %lx, "
769 "focusing window: %d\n",
770 (e
->type
== EnterNotify
? "Enter" : "Leave"),
772 e
->xcrossing
.detail
, (client
?client
->window
:0),
775 if (!nofocus
&& config_focus_follow
)
776 event_enter_client(client
);
784 case ConfigureRequest
:
786 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
787 ConfigureRequest
, &ce
)) {
789 /* XXX if this causes bad things.. we can compress config req's
790 with the same mask. */
791 e
->xconfigurerequest
.value_mask
|=
792 ce
.xconfigurerequest
.value_mask
;
793 if (ce
.xconfigurerequest
.value_mask
& CWX
)
794 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
795 if (ce
.xconfigurerequest
.value_mask
& CWY
)
796 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
797 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
798 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
799 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
800 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
801 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
802 e
->xconfigurerequest
.border_width
=
803 ce
.xconfigurerequest
.border_width
;
804 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
805 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
808 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
809 if (client
->iconic
|| client
->shaded
) return;
811 /* resize, then move, as specified in the EWMH section 7.7 */
812 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
818 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
819 client
->border_width
= e
->xconfigurerequest
.border_width
;
821 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
822 e
->xconfigurerequest
.x
: client
->area
.x
;
823 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
824 e
->xconfigurerequest
.y
: client
->area
.y
;
825 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
826 e
->xconfigurerequest
.width
: client
->area
.width
;
827 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
828 e
->xconfigurerequest
.height
: client
->area
.height
;
834 client
->frame
->size
.left
+ client
->frame
->size
.right
;
836 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
837 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
838 client_normal(client
));
839 if (e
->xconfigurerequest
.value_mask
& CWX
)
841 if (e
->xconfigurerequest
.value_mask
& CWY
)
845 switch (client
->gravity
) {
846 case NorthEastGravity
:
848 corner
= OB_CORNER_TOPRIGHT
;
850 case SouthWestGravity
:
852 corner
= OB_CORNER_BOTTOMLEFT
;
854 case SouthEastGravity
:
855 corner
= OB_CORNER_BOTTOMRIGHT
;
857 default: /* NorthWest, Static, etc */
858 corner
= OB_CORNER_TOPLEFT
;
861 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
865 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
866 switch (e
->xconfigurerequest
.detail
) {
869 client_lower(client
);
875 client_raise(client
);
881 if (client
->ignore_unmaps
) {
882 client
->ignore_unmaps
--;
885 client_unmanage(client
);
888 client_unmanage(client
);
891 /* this is when the client is first taken captive in the frame */
892 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
895 This event is quite rare and is usually handled in unmapHandler.
896 However, if the window is unmapped when the reparent event occurs,
897 the window manager never sees it because an unmap event is not sent
898 to an already unmapped window.
901 /* we don't want the reparent event, put it back on the stack for the
902 X server to deal with after we unmanage the window */
903 XPutBackEvent(ob_display
, e
);
905 client_unmanage(client
);
908 ob_debug("MapRequest for 0x%lx\n", client
->window
);
909 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
910 does, we don't want it!
911 it can happen now when the window is on
912 another desktop, but we still don't
914 client_activate(client
, FALSE
);
917 /* validate cuz we query stuff off the client here */
918 if (!client_validate(client
)) break;
920 if (e
->xclient
.format
!= 32) return;
922 msgtype
= e
->xclient
.message_type
;
923 if (msgtype
== prop_atoms
.wm_change_state
) {
924 /* compress changes into a single change */
925 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
927 /* XXX: it would be nice to compress ALL messages of a
928 type, not just messages in a row without other
929 message types between. */
930 if (ce
.xclient
.message_type
!= msgtype
) {
931 XPutBackEvent(ob_display
, &ce
);
934 e
->xclient
= ce
.xclient
;
936 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
937 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
938 /* compress changes into a single change */
939 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
941 /* XXX: it would be nice to compress ALL messages of a
942 type, not just messages in a row without other
943 message types between. */
944 if (ce
.xclient
.message_type
!= msgtype
) {
945 XPutBackEvent(ob_display
, &ce
);
948 e
->xclient
= ce
.xclient
;
950 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
951 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
952 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
954 } else if (msgtype
== prop_atoms
.net_wm_state
) {
955 /* can't compress these */
956 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
957 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
958 e
->xclient
.data
.l
[0] == 1 ? "Add" :
959 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
960 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
962 client_set_state(client
, e
->xclient
.data
.l
[0],
963 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
964 } else if (msgtype
== prop_atoms
.net_close_window
) {
965 ob_debug("net_close_window for 0x%lx\n", client
->window
);
966 client_close(client
);
967 } else if (msgtype
== prop_atoms
.net_active_window
) {
968 ob_debug("net_active_window for 0x%lx\n", client
->window
);
969 client_activate(client
, FALSE
);
970 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
971 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
972 if ((Atom
)e
->xclient
.data
.l
[2] ==
973 prop_atoms
.net_wm_moveresize_size_topleft
||
974 (Atom
)e
->xclient
.data
.l
[2] ==
975 prop_atoms
.net_wm_moveresize_size_top
||
976 (Atom
)e
->xclient
.data
.l
[2] ==
977 prop_atoms
.net_wm_moveresize_size_topright
||
978 (Atom
)e
->xclient
.data
.l
[2] ==
979 prop_atoms
.net_wm_moveresize_size_right
||
980 (Atom
)e
->xclient
.data
.l
[2] ==
981 prop_atoms
.net_wm_moveresize_size_right
||
982 (Atom
)e
->xclient
.data
.l
[2] ==
983 prop_atoms
.net_wm_moveresize_size_bottomright
||
984 (Atom
)e
->xclient
.data
.l
[2] ==
985 prop_atoms
.net_wm_moveresize_size_bottom
||
986 (Atom
)e
->xclient
.data
.l
[2] ==
987 prop_atoms
.net_wm_moveresize_size_bottomleft
||
988 (Atom
)e
->xclient
.data
.l
[2] ==
989 prop_atoms
.net_wm_moveresize_size_left
||
990 (Atom
)e
->xclient
.data
.l
[2] ==
991 prop_atoms
.net_wm_moveresize_move
||
992 (Atom
)e
->xclient
.data
.l
[2] ==
993 prop_atoms
.net_wm_moveresize_size_keyboard
||
994 (Atom
)e
->xclient
.data
.l
[2] ==
995 prop_atoms
.net_wm_moveresize_move_keyboard
) {
997 moveresize_start(client
, e
->xclient
.data
.l
[0],
998 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
999 e
->xclient
.data
.l
[2]);
1001 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1002 gint oldg
= client
->gravity
;
1003 gint tmpg
, x
, y
, w
, h
;
1005 if (e
->xclient
.data
.l
[0] & 0xff)
1006 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
1010 if (e
->xclient
.data
.l
[0] & 1 << 8)
1011 x
= e
->xclient
.data
.l
[1];
1014 if (e
->xclient
.data
.l
[0] & 1 << 9)
1015 y
= e
->xclient
.data
.l
[2];
1018 if (e
->xclient
.data
.l
[0] & 1 << 10)
1019 w
= e
->xclient
.data
.l
[3];
1021 w
= client
->area
.width
;
1022 if (e
->xclient
.data
.l
[0] & 1 << 11)
1023 h
= e
->xclient
.data
.l
[4];
1025 h
= client
->area
.height
;
1026 client
->gravity
= tmpg
;
1032 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1034 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1035 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1036 client_normal(client
));
1037 if (e
->xclient
.data
.l
[0] & 1 << 8)
1039 if (e
->xclient
.data
.l
[0] & 1 << 9)
1043 client_configure(client
, OB_CORNER_TOPLEFT
,
1044 x
, y
, w
, h
, FALSE
, TRUE
);
1046 client
->gravity
= oldg
;
1049 case PropertyNotify
:
1050 /* validate cuz we query stuff off the client here */
1051 if (!client_validate(client
)) break;
1053 /* compress changes to a single property into a single change */
1054 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1058 /* XXX: it would be nice to compress ALL changes to a property,
1059 not just changes in a row without other props between. */
1061 a
= ce
.xproperty
.atom
;
1062 b
= e
->xproperty
.atom
;
1066 if ((a
== prop_atoms
.net_wm_name
||
1067 a
== prop_atoms
.wm_name
||
1068 a
== prop_atoms
.net_wm_icon_name
||
1069 a
== prop_atoms
.wm_icon_name
)
1071 (b
== prop_atoms
.net_wm_name
||
1072 b
== prop_atoms
.wm_name
||
1073 b
== prop_atoms
.net_wm_icon_name
||
1074 b
== prop_atoms
.wm_icon_name
)) {
1077 if ((a
== prop_atoms
.net_wm_icon
||
1078 a
== prop_atoms
.kwm_win_icon
)
1080 (b
== prop_atoms
.net_wm_icon
||
1081 b
== prop_atoms
.kwm_win_icon
))
1084 XPutBackEvent(ob_display
, &ce
);
1088 msgtype
= e
->xproperty
.atom
;
1089 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1090 client_update_normal_hints(client
);
1091 /* normal hints can make a window non-resizable */
1092 client_setup_decor_and_functions(client
);
1093 } else if (msgtype
== XA_WM_HINTS
) {
1094 client_update_wmhints(client
);
1095 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1096 client_update_transient_for(client
);
1097 client_get_type(client
);
1098 /* type may have changed, so update the layer */
1099 client_calc_layer(client
);
1100 client_setup_decor_and_functions(client
);
1101 } else if (msgtype
== prop_atoms
.net_wm_name
||
1102 msgtype
== prop_atoms
.wm_name
||
1103 msgtype
== prop_atoms
.net_wm_icon_name
||
1104 msgtype
== prop_atoms
.wm_icon_name
) {
1105 client_update_title(client
);
1106 } else if (msgtype
== prop_atoms
.wm_class
) {
1107 client_update_class(client
);
1108 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1109 client_update_protocols(client
);
1110 client_setup_decor_and_functions(client
);
1112 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1113 client_update_strut(client
);
1115 else if (msgtype
== prop_atoms
.net_wm_icon
||
1116 msgtype
== prop_atoms
.kwm_win_icon
) {
1117 client_update_icons(client
);
1119 else if (msgtype
== prop_atoms
.sm_client_id
) {
1120 client_update_sm_client_id(client
);
1125 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1126 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1127 frame_adjust_shape(client
->frame
);
1133 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1137 if (e
->xbutton
.button
== 1)
1138 stacking_raise(DOCK_AS_WINDOW(s
));
1139 else if (e
->xbutton
.button
== 2)
1140 stacking_lower(DOCK_AS_WINDOW(s
));
1151 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1155 dock_app_drag(app
, &e
->xmotion
);
1158 if (app
->ignore_unmaps
) {
1159 app
->ignore_unmaps
--;
1162 dock_remove(app
, TRUE
);
1165 dock_remove(app
, FALSE
);
1167 case ReparentNotify
:
1168 dock_remove(app
, FALSE
);
1170 case ConfigureNotify
:
1171 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1176 ObMenuFrame
* find_active_menu()
1179 ObMenuFrame
*ret
= NULL
;
1181 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1190 ObMenuFrame
* find_active_or_last_menu()
1192 ObMenuFrame
*ret
= NULL
;
1194 ret
= find_active_menu();
1195 if (!ret
&& menu_frame_visible
)
1196 ret
= menu_frame_visible
->data
;
1200 static void event_handle_menu(XEvent
*ev
)
1203 ObMenuEntryFrame
*e
;
1207 if (menu_can_hide
) {
1208 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1209 ev
->xbutton
.y_root
)))
1210 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1212 menu_frame_hide_all();
1216 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1217 ev
->xmotion
.y_root
))) {
1218 menu_frame_move_on_screen(f
);
1219 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1220 ev
->xmotion
.y_root
)))
1221 menu_frame_select(f
, e
);
1226 a
= find_active_menu();
1228 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1230 menu_frame_select(a
, NULL
);
1235 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1236 menu_frame_hide_all();
1237 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1239 if ((f
= find_active_menu()))
1240 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1241 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1243 if ((f
= find_active_or_last_menu()) && f
->parent
)
1244 menu_frame_select(f
, NULL
);
1245 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1247 if ((f
= find_active_or_last_menu()) && f
->child
)
1248 menu_frame_select_next(f
->child
);
1249 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1251 if ((f
= find_active_or_last_menu()))
1252 menu_frame_select_previous(f
);
1253 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1255 if ((f
= find_active_or_last_menu()))
1256 menu_frame_select_next(f
);
1262 static gboolean
menu_hide_delay_func(gpointer data
)
1264 menu_can_hide
= TRUE
;
1265 return FALSE
; /* no repeat */
1268 static gboolean
focus_delay_func(gpointer data
)
1272 if (focus_client
!= c
) {
1274 if (config_focus_raise
)
1277 return FALSE
; /* no repeat */
1280 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1282 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
, client
);
1285 static void event_client_dest(ObClient
*client
, gpointer data
)
1287 if (client
== focus_hilite
)
1288 focus_hilite
= NULL
;
1291 void event_halt_focus_delay()
1293 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1296 void event_ignore_queued_enters()
1298 GSList
*saved
= NULL
, *it
;
1301 XSync(ob_display
, FALSE
);
1303 /* count the events */
1305 e
= g_new(XEvent
, 1);
1306 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1309 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1310 if (win
&& WINDOW_IS_CLIENT(win
))
1311 ++ignore_enter_focus
;
1313 saved
= g_slist_append(saved
, e
);
1319 /* put the events back */
1320 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1321 XPutBackEvent(ob_display
, it
->data
);
1324 g_slist_free(saved
);