1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 event.c for the Openbox window manager
4 Copyright (c) 2003 Ben Jansens
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 See the COPYING file for a copy of the GNU General Public License.
31 #include "menuframe.h"
35 #include "framerender.h"
37 #include "moveresize.h"
40 #include "extensions.h"
43 #include <X11/keysym.h>
44 #include <X11/Xatom.h>
47 #ifdef HAVE_SYS_SELECT_H
48 # include <sys/select.h>
55 #include <X11/ICE/ICElib.h>
63 static void event_process(const XEvent
*e
, gpointer data
);
64 static void event_client_dest(ObClient
*client
, gpointer data
);
65 static void event_handle_root(XEvent
*e
);
66 static void event_handle_menu(XEvent
*e
);
67 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
68 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
69 static void event_handle_client(ObClient
*c
, XEvent
*e
);
70 static void event_handle_group(ObGroup
*g
, XEvent
*e
);
72 static gboolean
focus_delay_func(gpointer data
);
73 static void focus_delay_client_dest(ObClient
*client
, gpointer data
);
75 static gboolean
menu_hide_delay_func(gpointer data
);
77 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
78 (e)->xfocus.detail == NotifyAncestor || \
79 (e)->xfocus.detail > NotifyNonlinearVirtual)
80 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
81 (e)->xfocus.detail == NotifyInferior || \
82 (e)->xfocus.detail == NotifyAncestor || \
83 (e)->xfocus.detail > NotifyNonlinearVirtual)
85 Time event_lasttime
= 0;
87 /*! The value of the mask for the NumLock modifier */
89 /*! The value of the mask for the ScrollLock modifier */
91 /*! The key codes for the modifier keys */
92 static XModifierKeymap
*modmap
;
93 /*! Table of the constant modifier masks */
94 static const gint mask_table
[] = {
95 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
96 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
98 static gint mask_table_size
;
100 static guint ignore_enter_focus
= 0;
102 static gboolean menu_can_hide
;
105 static void ice_handler(gint fd
, gpointer conn
)
108 IceProcessMessages(conn
, NULL
, &b
);
111 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
112 IcePointer
*watch_data
)
117 fd
= IceConnectionNumber(conn
);
118 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
120 ob_main_loop_fd_remove(ob_main_loop
, fd
);
126 void event_startup(gboolean reconfig
)
128 if (reconfig
) return;
130 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
132 /* get lock masks that are defined by the display (not constant) */
133 modmap
= XGetModifierMapping(ob_display
);
135 if (modmap
&& modmap
->max_keypermod
> 0) {
137 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
138 /* get the values of the keyboard lock modifiers
139 Note: Caps lock is not retrieved the same way as Scroll and Num
140 lock since it doesn't need to be. */
141 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
142 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
145 for (cnt
= 0; cnt
< size
; ++cnt
) {
146 if (! modmap
->modifiermap
[cnt
]) continue;
148 if (num_lock
== modmap
->modifiermap
[cnt
])
149 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
150 if (scroll_lock
== modmap
->modifiermap
[cnt
])
151 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
155 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
158 IceAddConnectionWatch(ice_watch
, NULL
);
161 client_add_destructor(focus_delay_client_dest
, NULL
);
162 client_add_destructor(event_client_dest
, NULL
);
165 void event_shutdown(gboolean reconfig
)
167 if (reconfig
) return;
170 IceRemoveConnectionWatch(ice_watch
, NULL
);
173 client_remove_destructor(focus_delay_client_dest
);
174 XFreeModifiermap(modmap
);
177 static Window
event_get_window(XEvent
*e
)
184 window
= RootWindow(ob_display
, ob_screen
);
187 window
= e
->xmap
.window
;
190 window
= e
->xunmap
.window
;
193 window
= e
->xdestroywindow
.window
;
195 case ConfigureRequest
:
196 window
= e
->xconfigurerequest
.window
;
198 case ConfigureNotify
:
199 window
= e
->xconfigure
.window
;
203 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
204 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
206 window
= ((XkbBellNotifyEvent
*)e
)->window
;
212 window
= e
->xany
.window
;
217 static void event_set_lasttime(XEvent
*e
)
221 /* grab the lasttime and hack up the state */
237 t
= e
->xproperty
.time
;
241 t
= e
->xcrossing
.time
;
244 /* if more event types are anticipated, get their timestamp
249 if (t
> event_lasttime
)
253 #define STRIP_MODS(s) \
254 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
255 /* kill off the Button1Mask etc, only want the modifiers */ \
256 s &= (ControlMask | ShiftMask | Mod1Mask | \
257 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
259 static void event_hack_mods(XEvent *e)
267 STRIP_MODS(e
->xbutton
.state
);
270 STRIP_MODS(e
->xkey
.state
);
273 STRIP_MODS(e
->xkey
.state
);
274 /* remove from the state the mask of the modifier being released, if
275 it is a modifier key being released (this is a little ugly..) */
276 kp
= modmap
->modifiermap
;
277 for (i
= 0; i
< mask_table_size
; ++i
) {
278 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
279 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
280 /* remove the mask for it */
281 e
->xkey
.state
&= ~mask_table
[i
];
282 /* cause the first loop to break; */
284 break; /* get outta here! */
291 STRIP_MODS(e
->xmotion
.state
);
292 /* compress events */
295 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
297 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
298 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
305 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
310 if (e
->xcrossing
.detail
== NotifyInferior
)
314 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
315 because of RevertToPointerRoot. If the focus ends up reverting to
316 pointer root on a workspace change, then the FocusIn event that we
317 want will be of type NotifyAncestor. This situation does not occur
318 for FocusOut, so it is safely ignored there.
320 if (INVALID_FOCUSIN(e
) ||
323 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
324 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
326 /* says a client was not found for the event (or a valid FocusIn
329 e
->xfocus
.window
= None
;
334 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
335 e
->xfocus
.mode
, e
->xfocus
.detail
);
339 if (INVALID_FOCUSOUT(e
)) {
341 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
342 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
348 ob_debug("FocusOut on %lx mode %d detail %d\n",
349 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
353 gboolean fallback
= TRUE
;
356 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
358 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
360 if (fe
.type
== FocusOut
) {
362 ob_debug("found pending FocusOut\n");
364 if (!INVALID_FOCUSOUT(&fe
)) {
365 /* if there is a VALID FocusOut still coming, don't
366 fallback focus yet, we'll deal with it then */
367 XPutBackEvent(ob_display
, &fe
);
373 ob_debug("found pending FocusIn\n");
375 /* is the focused window getting a FocusOut/In back to
378 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
379 !event_ignore(&fe
, client
)) {
381 if focus_client is not set, then we can't do
382 this. we need the FocusIn. This happens in the
383 case when the set_focus_client(NULL) in the
384 focus_fallback function fires and then
385 focus_fallback picks the currently focused
386 window (such as on a SendToDesktop-esque action.
390 ob_debug("focused window got an Out/In back to "
391 "itself IGNORED both\n");
395 event_process(&fe
, NULL
);
397 ob_debug("focused window got an Out/In back to "
398 "itself but focus_client was null "
399 "IGNORED just the Out\n");
408 /* once all the FocusOut's have been dealt with, if
409 there is a FocusIn still left and it is valid, then
411 event_process(&fe
, &d
);
414 ob_debug("FocusIn was OK, so don't fallback\n");
424 ob_debug("no valid FocusIn and no FocusOut events found, "
427 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
435 static void event_process(const XEvent
*ec
, gpointer data
)
438 ObGroup
*group
= NULL
;
439 ObClient
*client
= NULL
;
441 ObDockApp
*dockapp
= NULL
;
442 ObWindow
*obwin
= NULL
;
444 ObEventData
*ed
= data
;
446 /* make a copy we can mangle */
450 window
= event_get_window(e
);
451 if (!(e
->type
== PropertyNotify
&&
452 (group
= g_hash_table_lookup(group_map
, &window
))))
453 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
454 switch (obwin
->type
) {
456 dock
= WINDOW_AS_DOCK(obwin
);
459 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
462 client
= WINDOW_AS_CLIENT(obwin
);
465 case Window_Internal
:
466 /* not to be used for events */
467 g_assert_not_reached();
472 event_set_lasttime(e
);
474 if (event_ignore(e
, client
)) {
481 /* deal with it in the kernel */
483 event_handle_group(group
, e
);
485 event_handle_client(client
, e
);
487 event_handle_dockapp(dockapp
, e
);
489 event_handle_dock(dock
, e
);
490 else if (window
== RootWindow(ob_display
, ob_screen
))
491 event_handle_root(e
);
492 else if (e
->type
== MapRequest
)
493 client_manage(window
);
494 else if (e
->type
== ConfigureRequest
) {
495 /* unhandled configure requests must be used to configure the
499 xwc
.x
= e
->xconfigurerequest
.x
;
500 xwc
.y
= e
->xconfigurerequest
.y
;
501 xwc
.width
= e
->xconfigurerequest
.width
;
502 xwc
.height
= e
->xconfigurerequest
.height
;
503 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
504 xwc
.sibling
= e
->xconfigurerequest
.above
;
505 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
507 /* we are not to be held responsible if someone sends us an
509 xerror_set_ignore(TRUE
);
510 XConfigureWindow(ob_display
, window
,
511 e
->xconfigurerequest
.value_mask
, &xwc
);
512 xerror_set_ignore(FALSE
);
515 /* user input (action-bound) events */
516 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
517 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
518 e
->type
== KeyRelease
)
520 if (menu_frame_visible
)
521 event_handle_menu(e
);
523 if (!keyboard_process_interactive_grab(e
, &client
)) {
524 if (moveresize_in_progress
) {
527 /* make further actions work on the client being
529 client
= moveresize_client
;
532 menu_can_hide
= FALSE
;
533 ob_main_loop_timeout_add(ob_main_loop
,
534 config_menu_hide_delay
* 1000,
535 menu_hide_delay_func
,
538 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
539 e
->type
== MotionNotify
)
540 mouse_event(client
, e
);
541 else if (e
->type
== KeyPress
)
542 keyboard_event((focus_cycle_target
? focus_cycle_target
:
543 (focus_hilite
? focus_hilite
: client
)),
550 static void event_handle_root(XEvent
*e
)
556 ob_debug("Another WM has requested to replace us. Exiting.\n");
561 if (e
->xclient
.format
!= 32) break;
563 msgtype
= e
->xclient
.message_type
;
564 if (msgtype
== prop_atoms
.net_current_desktop
) {
565 guint d
= e
->xclient
.data
.l
[0];
566 if (d
< screen_num_desktops
)
567 screen_set_desktop(d
);
568 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
569 guint d
= e
->xclient
.data
.l
[0];
571 screen_set_num_desktops(d
);
572 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
573 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
577 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
578 screen_update_desktop_names();
579 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
580 screen_update_layout();
582 case ConfigureNotify
:
584 XRRUpdateConfiguration(e
);
591 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
592 ob_debug("VIDMODE EVENT\n");
598 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
602 g_assert(e
->type
== PropertyNotify
);
604 for (it
= group
->members
; it
; it
= g_slist_next(it
))
605 event_handle_client(it
->data
, e
);
608 void event_enter_client(ObClient
*client
)
610 g_assert(config_focus_follow
);
612 if (client_normal(client
) && client_can_focus(client
)) {
613 if (config_focus_delay
) {
614 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
615 ob_main_loop_timeout_add(ob_main_loop
,
620 focus_delay_func(client
);
624 static void event_handle_client(ObClient
*client
, XEvent
*e
)
632 case VisibilityNotify
:
633 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
637 /* Wheel buttons don't draw because they are an instant click, so it
638 is a waste of resources to go drawing it. */
639 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
640 con
= frame_context(client
, e
->xbutton
.window
);
641 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
643 case OB_FRAME_CONTEXT_MAXIMIZE
:
644 client
->frame
->max_press
= (e
->type
== ButtonPress
);
645 framerender_frame(client
->frame
);
647 case OB_FRAME_CONTEXT_CLOSE
:
648 client
->frame
->close_press
= (e
->type
== ButtonPress
);
649 framerender_frame(client
->frame
);
651 case OB_FRAME_CONTEXT_ICONIFY
:
652 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
653 framerender_frame(client
->frame
);
655 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
656 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
657 framerender_frame(client
->frame
);
659 case OB_FRAME_CONTEXT_SHADE
:
660 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
661 framerender_frame(client
->frame
);
664 /* nothing changes with clicks for any other contexts */
671 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
672 e
->xfocus
.window
, client
->window
,
673 e
->xfocus
.mode
, e
->xfocus
.detail
);
675 if (client
!= focus_client
) {
676 focus_set_client(client
);
677 frame_adjust_focus(client
->frame
, TRUE
);
678 client_calc_layer(client
);
683 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
684 e
->xfocus
.window
, client
->window
,
685 e
->xfocus
.mode
, e
->xfocus
.detail
);
688 frame_adjust_focus(client
->frame
, FALSE
);
689 client_calc_layer(client
);
692 con
= frame_context(client
, e
->xcrossing
.window
);
694 case OB_FRAME_CONTEXT_MAXIMIZE
:
695 client
->frame
->max_hover
= FALSE
;
696 frame_adjust_state(client
->frame
);
698 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
699 client
->frame
->desk_hover
= FALSE
;
700 frame_adjust_state(client
->frame
);
702 case OB_FRAME_CONTEXT_SHADE
:
703 client
->frame
->shade_hover
= FALSE
;
704 frame_adjust_state(client
->frame
);
706 case OB_FRAME_CONTEXT_ICONIFY
:
707 client
->frame
->iconify_hover
= FALSE
;
708 frame_adjust_state(client
->frame
);
710 case OB_FRAME_CONTEXT_CLOSE
:
711 client
->frame
->close_hover
= FALSE
;
712 frame_adjust_state(client
->frame
);
714 case OB_FRAME_CONTEXT_FRAME
:
715 if (config_focus_follow
&& config_focus_delay
)
716 ob_main_loop_timeout_remove_data(ob_main_loop
,
726 gboolean nofocus
= FALSE
;
728 if (ignore_enter_focus
) {
729 ignore_enter_focus
--;
733 con
= frame_context(client
, e
->xcrossing
.window
);
735 case OB_FRAME_CONTEXT_MAXIMIZE
:
736 client
->frame
->max_hover
= TRUE
;
737 frame_adjust_state(client
->frame
);
739 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
740 client
->frame
->desk_hover
= TRUE
;
741 frame_adjust_state(client
->frame
);
743 case OB_FRAME_CONTEXT_SHADE
:
744 client
->frame
->shade_hover
= TRUE
;
745 frame_adjust_state(client
->frame
);
747 case OB_FRAME_CONTEXT_ICONIFY
:
748 client
->frame
->iconify_hover
= TRUE
;
749 frame_adjust_state(client
->frame
);
751 case OB_FRAME_CONTEXT_CLOSE
:
752 client
->frame
->close_hover
= TRUE
;
753 frame_adjust_state(client
->frame
);
755 case OB_FRAME_CONTEXT_FRAME
:
756 if (e
->xcrossing
.mode
== NotifyGrab
||
757 e
->xcrossing
.mode
== NotifyUngrab
)
760 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
761 (e
->type
== EnterNotify
? "Enter" : "Leave"),
763 e
->xcrossing
.detail
, client
?client
->window
:0);
767 ob_debug("%sNotify mode %d detail %d on %lx, "
768 "focusing window: %d\n",
769 (e
->type
== EnterNotify
? "Enter" : "Leave"),
771 e
->xcrossing
.detail
, (client
?client
->window
:0),
774 if (!nofocus
&& config_focus_follow
)
775 event_enter_client(client
);
783 case ConfigureRequest
:
785 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
786 ConfigureRequest
, &ce
)) {
788 /* XXX if this causes bad things.. we can compress config req's
789 with the same mask. */
790 e
->xconfigurerequest
.value_mask
|=
791 ce
.xconfigurerequest
.value_mask
;
792 if (ce
.xconfigurerequest
.value_mask
& CWX
)
793 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
794 if (ce
.xconfigurerequest
.value_mask
& CWY
)
795 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
796 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
797 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
798 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
799 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
800 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
801 e
->xconfigurerequest
.border_width
=
802 ce
.xconfigurerequest
.border_width
;
803 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
804 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
807 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
808 if (client
->iconic
|| client
->shaded
) return;
810 /* resize, then move, as specified in the EWMH section 7.7 */
811 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
817 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
818 client
->border_width
= e
->xconfigurerequest
.border_width
;
820 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
821 e
->xconfigurerequest
.x
: client
->area
.x
;
822 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
823 e
->xconfigurerequest
.y
: client
->area
.y
;
824 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
825 e
->xconfigurerequest
.width
: client
->area
.width
;
826 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
827 e
->xconfigurerequest
.height
: client
->area
.height
;
833 client
->frame
->size
.left
+ client
->frame
->size
.right
;
835 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
836 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
837 client_normal(client
));
838 if (e
->xconfigurerequest
.value_mask
& CWX
)
840 if (e
->xconfigurerequest
.value_mask
& CWY
)
844 switch (client
->gravity
) {
845 case NorthEastGravity
:
847 corner
= OB_CORNER_TOPRIGHT
;
849 case SouthWestGravity
:
851 corner
= OB_CORNER_BOTTOMLEFT
;
853 case SouthEastGravity
:
854 corner
= OB_CORNER_BOTTOMRIGHT
;
856 default: /* NorthWest, Static, etc */
857 corner
= OB_CORNER_TOPLEFT
;
860 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
864 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
865 switch (e
->xconfigurerequest
.detail
) {
868 client_lower(client
);
874 client_raise(client
);
880 if (client
->ignore_unmaps
) {
881 client
->ignore_unmaps
--;
884 client_unmanage(client
);
887 client_unmanage(client
);
890 /* this is when the client is first taken captive in the frame */
891 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
894 This event is quite rare and is usually handled in unmapHandler.
895 However, if the window is unmapped when the reparent event occurs,
896 the window manager never sees it because an unmap event is not sent
897 to an already unmapped window.
900 /* we don't want the reparent event, put it back on the stack for the
901 X server to deal with after we unmanage the window */
902 XPutBackEvent(ob_display
, e
);
904 client_unmanage(client
);
907 ob_debug("MapRequest for 0x%lx\n", client
->window
);
908 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
909 does, we don't want it!
910 it can happen now when the window is on
911 another desktop, but we still don't
913 client_activate(client
, FALSE
);
916 /* validate cuz we query stuff off the client here */
917 if (!client_validate(client
)) break;
919 if (e
->xclient
.format
!= 32) return;
921 msgtype
= e
->xclient
.message_type
;
922 if (msgtype
== prop_atoms
.wm_change_state
) {
923 /* compress changes into a single change */
924 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
926 /* XXX: it would be nice to compress ALL messages of a
927 type, not just messages in a row without other
928 message types between. */
929 if (ce
.xclient
.message_type
!= msgtype
) {
930 XPutBackEvent(ob_display
, &ce
);
933 e
->xclient
= ce
.xclient
;
935 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
936 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
937 /* compress changes into a single change */
938 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
940 /* XXX: it would be nice to compress ALL messages of a
941 type, not just messages in a row without other
942 message types between. */
943 if (ce
.xclient
.message_type
!= msgtype
) {
944 XPutBackEvent(ob_display
, &ce
);
947 e
->xclient
= ce
.xclient
;
949 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
950 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
951 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
953 } else if (msgtype
== prop_atoms
.net_wm_state
) {
954 /* can't compress these */
955 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
956 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
957 e
->xclient
.data
.l
[0] == 1 ? "Add" :
958 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
959 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
961 client_set_state(client
, e
->xclient
.data
.l
[0],
962 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
963 } else if (msgtype
== prop_atoms
.net_close_window
) {
964 ob_debug("net_close_window for 0x%lx\n", client
->window
);
965 client_close(client
);
966 } else if (msgtype
== prop_atoms
.net_active_window
) {
967 ob_debug("net_active_window for 0x%lx\n", client
->window
);
968 client_activate(client
, FALSE
);
969 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
970 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
971 if ((Atom
)e
->xclient
.data
.l
[2] ==
972 prop_atoms
.net_wm_moveresize_size_topleft
||
973 (Atom
)e
->xclient
.data
.l
[2] ==
974 prop_atoms
.net_wm_moveresize_size_top
||
975 (Atom
)e
->xclient
.data
.l
[2] ==
976 prop_atoms
.net_wm_moveresize_size_topright
||
977 (Atom
)e
->xclient
.data
.l
[2] ==
978 prop_atoms
.net_wm_moveresize_size_right
||
979 (Atom
)e
->xclient
.data
.l
[2] ==
980 prop_atoms
.net_wm_moveresize_size_right
||
981 (Atom
)e
->xclient
.data
.l
[2] ==
982 prop_atoms
.net_wm_moveresize_size_bottomright
||
983 (Atom
)e
->xclient
.data
.l
[2] ==
984 prop_atoms
.net_wm_moveresize_size_bottom
||
985 (Atom
)e
->xclient
.data
.l
[2] ==
986 prop_atoms
.net_wm_moveresize_size_bottomleft
||
987 (Atom
)e
->xclient
.data
.l
[2] ==
988 prop_atoms
.net_wm_moveresize_size_left
||
989 (Atom
)e
->xclient
.data
.l
[2] ==
990 prop_atoms
.net_wm_moveresize_move
||
991 (Atom
)e
->xclient
.data
.l
[2] ==
992 prop_atoms
.net_wm_moveresize_size_keyboard
||
993 (Atom
)e
->xclient
.data
.l
[2] ==
994 prop_atoms
.net_wm_moveresize_move_keyboard
) {
996 moveresize_start(client
, e
->xclient
.data
.l
[0],
997 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
998 e
->xclient
.data
.l
[2]);
1000 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1001 gint oldg
= client
->gravity
;
1002 gint tmpg
, x
, y
, w
, h
;
1004 if (e
->xclient
.data
.l
[0] & 0xff)
1005 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
1009 if (e
->xclient
.data
.l
[0] & 1 << 8)
1010 x
= e
->xclient
.data
.l
[1];
1013 if (e
->xclient
.data
.l
[0] & 1 << 9)
1014 y
= e
->xclient
.data
.l
[2];
1017 if (e
->xclient
.data
.l
[0] & 1 << 10)
1018 w
= e
->xclient
.data
.l
[3];
1020 w
= client
->area
.width
;
1021 if (e
->xclient
.data
.l
[0] & 1 << 11)
1022 h
= e
->xclient
.data
.l
[4];
1024 h
= client
->area
.height
;
1025 client
->gravity
= tmpg
;
1031 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1033 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1034 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1035 client_normal(client
));
1036 if (e
->xclient
.data
.l
[0] & 1 << 8)
1038 if (e
->xclient
.data
.l
[0] & 1 << 9)
1042 client_configure(client
, OB_CORNER_TOPLEFT
,
1043 x
, y
, w
, h
, FALSE
, TRUE
);
1045 client
->gravity
= oldg
;
1048 case PropertyNotify
:
1049 /* validate cuz we query stuff off the client here */
1050 if (!client_validate(client
)) break;
1052 /* compress changes to a single property into a single change */
1053 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1057 /* XXX: it would be nice to compress ALL changes to a property,
1058 not just changes in a row without other props between. */
1060 a
= ce
.xproperty
.atom
;
1061 b
= e
->xproperty
.atom
;
1065 if ((a
== prop_atoms
.net_wm_name
||
1066 a
== prop_atoms
.wm_name
||
1067 a
== prop_atoms
.net_wm_icon_name
||
1068 a
== prop_atoms
.wm_icon_name
)
1070 (b
== prop_atoms
.net_wm_name
||
1071 b
== prop_atoms
.wm_name
||
1072 b
== prop_atoms
.net_wm_icon_name
||
1073 b
== prop_atoms
.wm_icon_name
)) {
1076 if ((a
== prop_atoms
.net_wm_icon
||
1077 a
== prop_atoms
.kwm_win_icon
)
1079 (b
== prop_atoms
.net_wm_icon
||
1080 b
== prop_atoms
.kwm_win_icon
))
1083 XPutBackEvent(ob_display
, &ce
);
1087 msgtype
= e
->xproperty
.atom
;
1088 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1089 client_update_normal_hints(client
);
1090 /* normal hints can make a window non-resizable */
1091 client_setup_decor_and_functions(client
);
1092 } else if (msgtype
== XA_WM_HINTS
) {
1093 client_update_wmhints(client
);
1094 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1095 client_update_transient_for(client
);
1096 client_get_type(client
);
1097 /* type may have changed, so update the layer */
1098 client_calc_layer(client
);
1099 client_setup_decor_and_functions(client
);
1100 } else if (msgtype
== prop_atoms
.net_wm_name
||
1101 msgtype
== prop_atoms
.wm_name
||
1102 msgtype
== prop_atoms
.net_wm_icon_name
||
1103 msgtype
== prop_atoms
.wm_icon_name
) {
1104 client_update_title(client
);
1105 } else if (msgtype
== prop_atoms
.wm_class
) {
1106 client_update_class(client
);
1107 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1108 client_update_protocols(client
);
1109 client_setup_decor_and_functions(client
);
1111 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1112 client_update_strut(client
);
1114 else if (msgtype
== prop_atoms
.net_wm_icon
||
1115 msgtype
== prop_atoms
.kwm_win_icon
) {
1116 client_update_icons(client
);
1118 else if (msgtype
== prop_atoms
.sm_client_id
) {
1119 client_update_sm_client_id(client
);
1124 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1125 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1126 frame_adjust_shape(client
->frame
);
1132 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1136 stacking_raise(DOCK_AS_WINDOW(s
));
1147 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1151 dock_app_drag(app
, &e
->xmotion
);
1154 if (app
->ignore_unmaps
) {
1155 app
->ignore_unmaps
--;
1158 dock_remove(app
, TRUE
);
1161 dock_remove(app
, FALSE
);
1163 case ReparentNotify
:
1164 dock_remove(app
, FALSE
);
1166 case ConfigureNotify
:
1167 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1172 ObMenuFrame
* find_active_menu()
1175 ObMenuFrame
*ret
= NULL
;
1177 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1186 ObMenuFrame
* find_active_or_last_menu()
1188 ObMenuFrame
*ret
= NULL
;
1190 ret
= find_active_menu();
1191 if (!ret
&& menu_frame_visible
)
1192 ret
= menu_frame_visible
->data
;
1196 static void event_handle_menu(XEvent
*ev
)
1199 ObMenuEntryFrame
*e
;
1203 if (menu_can_hide
) {
1204 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1205 ev
->xbutton
.y_root
)))
1206 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1208 menu_frame_hide_all();
1212 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1213 ev
->xmotion
.y_root
))) {
1214 menu_frame_move_on_screen(f
);
1215 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1216 ev
->xmotion
.y_root
)))
1217 menu_frame_select(f
, e
);
1222 a
= find_active_menu();
1224 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1226 menu_frame_select(a
, NULL
);
1231 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1232 menu_frame_hide_all();
1233 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1235 if ((f
= find_active_menu()))
1236 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1237 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1239 if ((f
= find_active_or_last_menu()) && f
->parent
)
1240 menu_frame_select(f
, NULL
);
1241 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1243 if ((f
= find_active_or_last_menu()) && f
->child
)
1244 menu_frame_select_next(f
->child
);
1245 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1247 if ((f
= find_active_or_last_menu()))
1248 menu_frame_select_previous(f
);
1249 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1251 if ((f
= find_active_or_last_menu()))
1252 menu_frame_select_next(f
);
1258 static gboolean
menu_hide_delay_func(gpointer data
)
1260 menu_can_hide
= TRUE
;
1261 return FALSE
; /* no repeat */
1264 static gboolean
focus_delay_func(gpointer data
)
1268 if (focus_client
!= c
) {
1270 if (config_focus_raise
)
1273 return FALSE
; /* no repeat */
1276 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1278 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
, client
);
1281 static void event_client_dest(ObClient
*client
, gpointer data
)
1283 if (client
== focus_hilite
)
1284 focus_hilite
= NULL
;
1287 void event_halt_focus_delay()
1289 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1292 void event_ignore_queued_enters()
1294 GSList
*saved
= NULL
, *it
;
1297 XSync(ob_display
, FALSE
);
1299 /* count the events */
1301 e
= g_new(XEvent
, 1);
1302 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1305 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1306 if (win
&& WINDOW_IS_CLIENT(win
))
1307 ++ignore_enter_focus
;
1309 saved
= g_slist_append(saved
, e
);
1315 /* put the events back */
1316 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1317 XPutBackEvent(ob_display
, it
->data
);
1320 g_slist_free(saved
);