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_handle_root(XEvent
*e
);
65 static void event_handle_menu(XEvent
*e
);
66 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
67 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
68 static void event_handle_client(ObClient
*c
, XEvent
*e
);
69 static void event_handle_group(ObGroup
*g
, XEvent
*e
);
71 static gboolean
focus_delay_func(gpointer data
);
72 static void focus_delay_client_dest(gpointer data
);
74 static gboolean
menu_hide_delay_func(gpointer data
);
76 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
77 (e)->xfocus.detail == NotifyAncestor || \
78 (e)->xfocus.detail > NotifyNonlinearVirtual)
79 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
80 (e)->xfocus.detail == NotifyInferior || \
81 (e)->xfocus.detail == NotifyAncestor || \
82 (e)->xfocus.detail > NotifyNonlinearVirtual)
84 Time event_lasttime
= 0;
86 /*! The value of the mask for the NumLock modifier */
87 unsigned int NumLockMask
;
88 /*! The value of the mask for the ScrollLock modifier */
89 unsigned int ScrollLockMask
;
90 /*! The key codes for the modifier keys */
91 static XModifierKeymap
*modmap
;
92 /*! Table of the constant modifier masks */
93 static const int mask_table
[] = {
94 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
95 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
97 static int mask_table_size
;
99 static guint ignore_enter_focus
= 0;
101 static gboolean menu_can_hide
;
104 static void ice_handler(int fd
, gpointer conn
)
107 IceProcessMessages(conn
, NULL
, &b
);
110 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
111 IcePointer
*watch_data
)
116 fd
= IceConnectionNumber(conn
);
117 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
119 ob_main_loop_fd_remove(ob_main_loop
, fd
);
125 void event_startup(gboolean reconfig
)
127 if (reconfig
) return;
129 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
131 /* get lock masks that are defined by the display (not constant) */
132 modmap
= XGetModifierMapping(ob_display
);
134 if (modmap
&& modmap
->max_keypermod
> 0) {
136 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
137 /* get the values of the keyboard lock modifiers
138 Note: Caps lock is not retrieved the same way as Scroll and Num
139 lock since it doesn't need to be. */
140 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
141 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
144 for (cnt
= 0; cnt
< size
; ++cnt
) {
145 if (! modmap
->modifiermap
[cnt
]) continue;
147 if (num_lock
== modmap
->modifiermap
[cnt
])
148 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
149 if (scroll_lock
== modmap
->modifiermap
[cnt
])
150 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
154 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
157 IceAddConnectionWatch(ice_watch
, NULL
);
160 client_add_destructor(focus_delay_client_dest
);
163 void event_shutdown(gboolean reconfig
)
165 if (reconfig
) return;
168 IceRemoveConnectionWatch(ice_watch
, NULL
);
171 client_remove_destructor(focus_delay_client_dest
);
172 XFreeModifiermap(modmap
);
175 static Window
event_get_window(XEvent
*e
)
182 window
= RootWindow(ob_display
, ob_screen
);
185 window
= e
->xmap
.window
;
188 window
= e
->xunmap
.window
;
191 window
= e
->xdestroywindow
.window
;
193 case ConfigureRequest
:
194 window
= e
->xconfigurerequest
.window
;
196 case ConfigureNotify
:
197 window
= e
->xconfigure
.window
;
201 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
202 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
204 window
= ((XkbBellNotifyEvent
*)e
)->window
;
210 window
= e
->xany
.window
;
215 static void event_set_lasttime(XEvent
*e
)
219 /* grab the lasttime and hack up the state */
235 t
= e
->xproperty
.time
;
239 t
= e
->xcrossing
.time
;
242 /* if more event types are anticipated, get their timestamp
247 if (t
> event_lasttime
)
251 #define STRIP_MODS(s) \
252 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
253 /* kill off the Button1Mask etc, only want the modifiers */ \
254 s &= (ControlMask | ShiftMask | Mod1Mask | \
255 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
257 static void event_hack_mods(XEvent *e)
265 STRIP_MODS(e
->xbutton
.state
);
268 STRIP_MODS(e
->xkey
.state
);
271 STRIP_MODS(e
->xkey
.state
);
272 /* remove from the state the mask of the modifier being released, if
273 it is a modifier key being released (this is a little ugly..) */
274 kp
= modmap
->modifiermap
;
275 for (i
= 0; i
< mask_table_size
; ++i
) {
276 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
277 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
278 /* remove the mask for it */
279 e
->xkey
.state
&= ~mask_table
[i
];
280 /* cause the first loop to break; */
282 break; /* get outta here! */
289 STRIP_MODS(e
->xmotion
.state
);
290 /* compress events */
293 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
295 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
296 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
303 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
307 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
308 because of RevertToPointerRoot. If the focus ends up reverting to
309 pointer root on a workspace change, then the FocusIn event that we
310 want will be of type NotifyAncestor. This situation does not occur
311 for FocusOut, so it is safely ignored there.
313 if (INVALID_FOCUSIN(e
) ||
316 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
317 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
319 /* says a client was not found for the event (or a valid FocusIn
322 e
->xfocus
.window
= None
;
327 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
328 e
->xfocus
.mode
, e
->xfocus
.detail
);
332 if (INVALID_FOCUSOUT(e
)) {
334 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
335 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
341 ob_debug("FocusOut on %lx mode %d detail %d\n",
342 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
347 gboolean fallback
= TRUE
;
350 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
352 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
354 if (fe
.type
== FocusOut
) {
356 ob_debug("found pending FocusOut\n");
358 if (!INVALID_FOCUSOUT(&fe
)) {
359 /* if there is a VALID FocusOut still coming, don't
360 fallback focus yet, we'll deal with it then */
361 XPutBackEvent(ob_display
, &fe
);
367 ob_debug("found pending FocusIn\n");
369 /* is the focused window getting a FocusOut/In back to
372 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
373 !event_ignore(&fe
, client
)) {
375 if focus_client is not set, then we can't do
376 this. we need the FocusIn. This happens in the
377 case when the set_focus_client(NULL) in the
378 focus_fallback function fires and then
379 focus_fallback picks the currently focused
380 window (such as on a SendToDesktop-esque action.
384 ob_debug("focused window got an Out/In back to "
385 "itself IGNORED both\n");
389 event_process(&fe
, NULL
);
391 ob_debug("focused window got an Out/In back to "
392 "itself but focus_client was null "
393 "IGNORED just the Out\n");
402 /* once all the FocusOut's have been dealt with, if
403 there is a FocusIn still left and it is valid, then
405 event_process(&fe
, &d
);
408 ob_debug("FocusIn was OK, so don't fallback\n");
418 ob_debug("no valid FocusIn and no FocusOut events found, "
421 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
429 static void event_process(const XEvent
*ec
, gpointer data
)
432 ObGroup
*group
= NULL
;
433 ObClient
*client
= NULL
;
435 ObDockApp
*dockapp
= NULL
;
436 ObWindow
*obwin
= NULL
;
438 ObEventData
*ed
= data
;
440 /* make a copy we can mangle */
444 window
= event_get_window(e
);
445 if (!(e
->type
== PropertyNotify
&&
446 (group
= g_hash_table_lookup(group_map
, &window
))))
447 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
448 switch (obwin
->type
) {
450 dock
= WINDOW_AS_DOCK(obwin
);
453 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
456 client
= WINDOW_AS_CLIENT(obwin
);
459 case Window_Internal
:
460 /* not to be used for events */
461 g_assert_not_reached();
466 event_set_lasttime(e
);
468 if (event_ignore(e
, client
)) {
475 /* deal with it in the kernel */
477 event_handle_group(group
, e
);
479 event_handle_client(client
, e
);
481 event_handle_dockapp(dockapp
, e
);
483 event_handle_dock(dock
, e
);
484 else if (window
== RootWindow(ob_display
, ob_screen
))
485 event_handle_root(e
);
486 else if (e
->type
== MapRequest
)
487 client_manage(window
);
488 else if (e
->type
== ConfigureRequest
) {
489 /* unhandled configure requests must be used to configure the
493 xwc
.x
= e
->xconfigurerequest
.x
;
494 xwc
.y
= e
->xconfigurerequest
.y
;
495 xwc
.width
= e
->xconfigurerequest
.width
;
496 xwc
.height
= e
->xconfigurerequest
.height
;
497 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
498 xwc
.sibling
= e
->xconfigurerequest
.above
;
499 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
501 /* we are not to be held responsible if someone sends us an
503 xerror_set_ignore(TRUE
);
504 XConfigureWindow(ob_display
, window
,
505 e
->xconfigurerequest
.value_mask
, &xwc
);
506 xerror_set_ignore(FALSE
);
509 /* user input (action-bound) events */
510 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
511 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
512 e
->type
== KeyRelease
)
514 if (menu_frame_visible
)
515 event_handle_menu(e
);
517 if (!keyboard_process_interactive_grab(e
, &client
)) {
518 if (moveresize_in_progress
) {
521 /* make further actions work on the client being
523 client
= moveresize_client
;
526 menu_can_hide
= FALSE
;
527 ob_main_loop_timeout_add(ob_main_loop
,
529 menu_hide_delay_func
,
532 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
533 e
->type
== MotionNotify
)
534 mouse_event(client
, e
);
535 else if (e
->type
== KeyPress
)
536 /* when in the middle of a focus cycling action, this
537 causes the window which appears to be focused to be
538 the one on which the actions will be executed */
539 keyboard_event((focus_cycle_target
?
540 focus_cycle_target
: client
), e
);
546 static void event_handle_root(XEvent
*e
)
552 ob_debug("Another WM has requested to replace us. Exiting.\n");
557 if (e
->xclient
.format
!= 32) break;
559 msgtype
= e
->xclient
.message_type
;
560 if (msgtype
== prop_atoms
.net_current_desktop
) {
561 unsigned int d
= e
->xclient
.data
.l
[0];
562 if (d
< screen_num_desktops
)
563 screen_set_desktop(d
);
564 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
565 unsigned int d
= e
->xclient
.data
.l
[0];
567 screen_set_num_desktops(d
);
568 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
569 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
573 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
574 screen_update_desktop_names();
575 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
576 screen_update_layout();
578 case ConfigureNotify
:
580 XRRUpdateConfiguration(e
);
587 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
588 ob_debug("VIDMODE EVENT\n");
594 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
598 g_assert(e
->type
== PropertyNotify
);
600 for (it
= group
->members
; it
; it
= g_slist_next(it
))
601 event_handle_client(it
->data
, e
);
604 void event_enter_client(ObClient
*client
)
606 g_assert(config_focus_follow
);
608 if (client_normal(client
) && client_can_focus(client
)) {
609 if (config_focus_delay
) {
610 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
611 ob_main_loop_timeout_add(ob_main_loop
,
616 focus_delay_func(client
);
620 static void event_handle_client(ObClient
*client
, XEvent
*e
)
628 case VisibilityNotify
:
629 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
633 /* Wheel buttons don't draw because they are an instant click, so it
634 is a waste of resources to go drawing it. */
635 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
636 con
= frame_context(client
, e
->xbutton
.window
);
637 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
639 case OB_FRAME_CONTEXT_MAXIMIZE
:
640 client
->frame
->max_press
= (e
->type
== ButtonPress
);
641 framerender_frame(client
->frame
);
643 case OB_FRAME_CONTEXT_CLOSE
:
644 client
->frame
->close_press
= (e
->type
== ButtonPress
);
645 framerender_frame(client
->frame
);
647 case OB_FRAME_CONTEXT_ICONIFY
:
648 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
649 framerender_frame(client
->frame
);
651 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
652 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
653 framerender_frame(client
->frame
);
655 case OB_FRAME_CONTEXT_SHADE
:
656 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
657 framerender_frame(client
->frame
);
660 /* nothing changes with clicks for any other contexts */
667 ob_debug("FocusIn on client for %lx\n", client
->window
);
669 if (client
!= focus_client
) {
670 focus_set_client(client
);
671 frame_adjust_focus(client
->frame
, TRUE
);
676 ob_debug("FocusOut on client for %lx\n", client
->window
);
678 /* are we a fullscreen window or a transient of one? (checks layer)
679 if we are then we need to be iconified since we are losing focus
681 if (client
->layer
== OB_STACKING_LAYER_FULLSCREEN
&& !client
->iconic
&&
682 !client_search_focus_tree_full(client
))
683 /* iconify fullscreen windows when they and their transients
685 client_iconify(client
, TRUE
, TRUE
);
686 frame_adjust_focus(client
->frame
, FALSE
);
689 con
= frame_context(client
, e
->xcrossing
.window
);
691 case OB_FRAME_CONTEXT_MAXIMIZE
:
692 client
->frame
->max_hover
= FALSE
;
693 frame_adjust_state(client
->frame
);
695 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
696 client
->frame
->desk_hover
= FALSE
;
697 frame_adjust_state(client
->frame
);
699 case OB_FRAME_CONTEXT_SHADE
:
700 client
->frame
->shade_hover
= FALSE
;
701 frame_adjust_state(client
->frame
);
703 case OB_FRAME_CONTEXT_ICONIFY
:
704 client
->frame
->iconify_hover
= FALSE
;
705 frame_adjust_state(client
->frame
);
707 case OB_FRAME_CONTEXT_CLOSE
:
708 client
->frame
->close_hover
= FALSE
;
709 frame_adjust_state(client
->frame
);
711 case OB_FRAME_CONTEXT_FRAME
:
713 if (config_focus_follow && config_focus_delay)
714 ob_main_loop_timeout_remove_data(ob_main_loop,
725 gboolean nofocus
= FALSE
;
727 if (ignore_enter_focus
) {
728 ignore_enter_focus
--;
732 con
= frame_context(client
, e
->xcrossing
.window
);
734 case OB_FRAME_CONTEXT_MAXIMIZE
:
735 client
->frame
->max_hover
= TRUE
;
736 frame_adjust_state(client
->frame
);
738 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
739 client
->frame
->desk_hover
= TRUE
;
740 frame_adjust_state(client
->frame
);
742 case OB_FRAME_CONTEXT_SHADE
:
743 client
->frame
->shade_hover
= TRUE
;
744 frame_adjust_state(client
->frame
);
746 case OB_FRAME_CONTEXT_ICONIFY
:
747 client
->frame
->iconify_hover
= TRUE
;
748 frame_adjust_state(client
->frame
);
750 case OB_FRAME_CONTEXT_CLOSE
:
751 client
->frame
->close_hover
= TRUE
;
752 frame_adjust_state(client
->frame
);
754 case OB_FRAME_CONTEXT_FRAME
:
755 if (e
->xcrossing
.mode
== NotifyGrab
||
756 e
->xcrossing
.detail
== NotifyInferior
||
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, "
769 (e
->type
== EnterNotify
? "Enter" : "Leave"),
771 e
->xcrossing
.detail
, client
?client
->window
:0);
773 if (!nofocus
&& config_focus_follow
)
774 event_enter_client(client
);
782 case ConfigureRequest
:
784 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
785 ConfigureRequest
, &ce
)) {
787 /* XXX if this causes bad things.. we can compress config req's
788 with the same mask. */
789 e
->xconfigurerequest
.value_mask
|=
790 ce
.xconfigurerequest
.value_mask
;
791 if (ce
.xconfigurerequest
.value_mask
& CWX
)
792 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
793 if (ce
.xconfigurerequest
.value_mask
& CWY
)
794 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
795 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
796 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
797 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
798 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
799 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
800 e
->xconfigurerequest
.border_width
=
801 ce
.xconfigurerequest
.border_width
;
802 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
803 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
806 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
807 if (client
->iconic
|| client
->shaded
) return;
809 /* resize, then move, as specified in the EWMH section 7.7 */
810 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
816 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
817 client
->border_width
= e
->xconfigurerequest
.border_width
;
819 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
820 e
->xconfigurerequest
.x
: client
->area
.x
;
821 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
822 e
->xconfigurerequest
.y
: client
->area
.y
;
823 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
824 e
->xconfigurerequest
.width
: client
->area
.width
;
825 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
826 e
->xconfigurerequest
.height
: client
->area
.height
;
832 client
->frame
->size
.left
+ client
->frame
->size
.right
;
834 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
835 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
836 client_normal(client
));
837 if (e
->xconfigurerequest
.value_mask
& CWX
)
839 if (e
->xconfigurerequest
.value_mask
& CWY
)
843 switch (client
->gravity
) {
844 case NorthEastGravity
:
846 corner
= OB_CORNER_TOPRIGHT
;
848 case SouthWestGravity
:
850 corner
= OB_CORNER_BOTTOMLEFT
;
852 case SouthEastGravity
:
853 corner
= OB_CORNER_BOTTOMRIGHT
;
855 default: /* NorthWest, Static, etc */
856 corner
= OB_CORNER_TOPLEFT
;
859 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
863 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
864 switch (e
->xconfigurerequest
.detail
) {
867 stacking_lower(CLIENT_AS_WINDOW(client
));
873 stacking_raise(CLIENT_AS_WINDOW(client
));
879 if (client
->ignore_unmaps
) {
880 client
->ignore_unmaps
--;
883 client_unmanage(client
);
886 client_unmanage(client
);
889 /* this is when the client is first taken captive in the frame */
890 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
893 This event is quite rare and is usually handled in unmapHandler.
894 However, if the window is unmapped when the reparent event occurs,
895 the window manager never sees it because an unmap event is not sent
896 to an already unmapped window.
899 /* we don't want the reparent event, put it back on the stack for the
900 X server to deal with after we unmanage the window */
901 XPutBackEvent(ob_display
, e
);
903 client_unmanage(client
);
906 ob_debug("MapRequest for 0x%lx\n", client
->window
);
907 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
908 does, we don't want it! */
909 if (screen_showing_desktop
)
910 screen_show_desktop(FALSE
);
911 client_iconify(client
, FALSE
, TRUE
);
912 if (!client
->frame
->visible
)
913 /* if its not visible still, then don't mess with it */
916 client_shade(client
, FALSE
);
917 client_focus(client
);
918 stacking_raise(CLIENT_AS_WINDOW(client
));
921 /* validate cuz we query stuff off the client here */
922 if (!client_validate(client
)) break;
924 if (e
->xclient
.format
!= 32) return;
926 msgtype
= e
->xclient
.message_type
;
927 if (msgtype
== prop_atoms
.wm_change_state
) {
928 /* compress changes into a single change */
929 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
931 /* XXX: it would be nice to compress ALL messages of a
932 type, not just messages in a row without other
933 message types between. */
934 if (ce
.xclient
.message_type
!= msgtype
) {
935 XPutBackEvent(ob_display
, &ce
);
938 e
->xclient
= ce
.xclient
;
940 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
941 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
942 /* compress changes into a single change */
943 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
945 /* XXX: it would be nice to compress ALL messages of a
946 type, not just messages in a row without other
947 message types between. */
948 if (ce
.xclient
.message_type
!= msgtype
) {
949 XPutBackEvent(ob_display
, &ce
);
952 e
->xclient
= ce
.xclient
;
954 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
955 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
956 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
958 } else if (msgtype
== prop_atoms
.net_wm_state
) {
959 /* can't compress these */
960 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
961 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
962 e
->xclient
.data
.l
[0] == 1 ? "Add" :
963 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
964 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
966 client_set_state(client
, e
->xclient
.data
.l
[0],
967 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
968 } else if (msgtype
== prop_atoms
.net_close_window
) {
969 ob_debug("net_close_window for 0x%lx\n", client
->window
);
970 client_close(client
);
971 } else if (msgtype
== prop_atoms
.net_active_window
) {
972 ob_debug("net_active_window for 0x%lx\n", client
->window
);
973 client_activate(client
, FALSE
);
974 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
975 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
976 if ((Atom
)e
->xclient
.data
.l
[2] ==
977 prop_atoms
.net_wm_moveresize_size_topleft
||
978 (Atom
)e
->xclient
.data
.l
[2] ==
979 prop_atoms
.net_wm_moveresize_size_top
||
980 (Atom
)e
->xclient
.data
.l
[2] ==
981 prop_atoms
.net_wm_moveresize_size_topright
||
982 (Atom
)e
->xclient
.data
.l
[2] ==
983 prop_atoms
.net_wm_moveresize_size_right
||
984 (Atom
)e
->xclient
.data
.l
[2] ==
985 prop_atoms
.net_wm_moveresize_size_right
||
986 (Atom
)e
->xclient
.data
.l
[2] ==
987 prop_atoms
.net_wm_moveresize_size_bottomright
||
988 (Atom
)e
->xclient
.data
.l
[2] ==
989 prop_atoms
.net_wm_moveresize_size_bottom
||
990 (Atom
)e
->xclient
.data
.l
[2] ==
991 prop_atoms
.net_wm_moveresize_size_bottomleft
||
992 (Atom
)e
->xclient
.data
.l
[2] ==
993 prop_atoms
.net_wm_moveresize_size_left
||
994 (Atom
)e
->xclient
.data
.l
[2] ==
995 prop_atoms
.net_wm_moveresize_move
||
996 (Atom
)e
->xclient
.data
.l
[2] ==
997 prop_atoms
.net_wm_moveresize_size_keyboard
||
998 (Atom
)e
->xclient
.data
.l
[2] ==
999 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1001 moveresize_start(client
, e
->xclient
.data
.l
[0],
1002 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1003 e
->xclient
.data
.l
[2]);
1005 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1006 int oldg
= client
->gravity
;
1007 int tmpg
, x
, y
, w
, h
;
1009 if (e
->xclient
.data
.l
[0] & 0xff)
1010 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
1014 if (e
->xclient
.data
.l
[0] & 1 << 8)
1015 x
= e
->xclient
.data
.l
[1];
1018 if (e
->xclient
.data
.l
[0] & 1 << 9)
1019 y
= e
->xclient
.data
.l
[2];
1022 if (e
->xclient
.data
.l
[0] & 1 << 10)
1023 w
= e
->xclient
.data
.l
[3];
1025 w
= client
->area
.width
;
1026 if (e
->xclient
.data
.l
[0] & 1 << 11)
1027 h
= e
->xclient
.data
.l
[4];
1029 h
= client
->area
.height
;
1030 client
->gravity
= tmpg
;
1036 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1038 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1039 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1040 client_normal(client
));
1041 if (e
->xclient
.data
.l
[0] & 1 << 8)
1043 if (e
->xclient
.data
.l
[0] & 1 << 9)
1047 client_configure(client
, OB_CORNER_TOPLEFT
,
1048 x
, y
, w
, h
, FALSE
, TRUE
);
1050 client
->gravity
= oldg
;
1053 case PropertyNotify
:
1054 /* validate cuz we query stuff off the client here */
1055 if (!client_validate(client
)) break;
1057 /* compress changes to a single property into a single change */
1058 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1062 /* XXX: it would be nice to compress ALL changes to a property,
1063 not just changes in a row without other props between. */
1065 a
= ce
.xproperty
.atom
;
1066 b
= e
->xproperty
.atom
;
1070 if ((a
== prop_atoms
.net_wm_name
||
1071 a
== prop_atoms
.wm_name
||
1072 a
== prop_atoms
.net_wm_icon_name
||
1073 a
== prop_atoms
.wm_icon_name
)
1075 (b
== prop_atoms
.net_wm_name
||
1076 b
== prop_atoms
.wm_name
||
1077 b
== prop_atoms
.net_wm_icon_name
||
1078 b
== prop_atoms
.wm_icon_name
)) {
1081 if ((a
== prop_atoms
.net_wm_icon
||
1082 a
== prop_atoms
.kwm_win_icon
)
1084 (b
== prop_atoms
.net_wm_icon
||
1085 b
== prop_atoms
.kwm_win_icon
))
1088 XPutBackEvent(ob_display
, &ce
);
1092 msgtype
= e
->xproperty
.atom
;
1093 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1094 client_update_normal_hints(client
);
1095 /* normal hints can make a window non-resizable */
1096 client_setup_decor_and_functions(client
);
1097 } else if (msgtype
== XA_WM_HINTS
) {
1098 client_update_wmhints(client
);
1099 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1100 client_update_transient_for(client
);
1101 client_get_type(client
);
1102 /* type may have changed, so update the layer */
1103 client_calc_layer(client
);
1104 client_setup_decor_and_functions(client
);
1105 } else if (msgtype
== prop_atoms
.net_wm_name
||
1106 msgtype
== prop_atoms
.wm_name
||
1107 msgtype
== prop_atoms
.net_wm_icon_name
||
1108 msgtype
== prop_atoms
.wm_icon_name
) {
1109 client_update_title(client
);
1110 } else if (msgtype
== prop_atoms
.wm_class
) {
1111 client_update_class(client
);
1112 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1113 client_update_protocols(client
);
1114 client_setup_decor_and_functions(client
);
1116 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1117 client_update_strut(client
);
1119 else if (msgtype
== prop_atoms
.net_wm_icon
||
1120 msgtype
== prop_atoms
.kwm_win_icon
) {
1121 client_update_icons(client
);
1123 else if (msgtype
== prop_atoms
.sm_client_id
) {
1124 client_update_sm_client_id(client
);
1129 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1130 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1131 frame_adjust_shape(client
->frame
);
1137 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1141 stacking_raise(DOCK_AS_WINDOW(s
));
1152 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1156 dock_app_drag(app
, &e
->xmotion
);
1159 if (app
->ignore_unmaps
) {
1160 app
->ignore_unmaps
--;
1163 dock_remove(app
, TRUE
);
1166 dock_remove(app
, FALSE
);
1168 case ReparentNotify
:
1169 dock_remove(app
, FALSE
);
1171 case ConfigureNotify
:
1172 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1177 ObMenuFrame
* find_active_menu()
1182 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1187 return it
? it
->data
: NULL
;
1190 static void event_handle_menu(XEvent
*ev
)
1193 ObMenuEntryFrame
*e
;
1197 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1198 ev
->xbutton
.y_root
)))
1199 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1200 else if (menu_can_hide
)
1201 menu_frame_hide_all();
1204 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1205 ev
->xmotion
.y_root
))) {
1206 menu_frame_move_on_screen(f
);
1207 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1208 ev
->xmotion
.y_root
)))
1209 menu_frame_select(f
, e
);
1214 a
= find_active_menu();
1216 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1218 menu_frame_select(a
, NULL
);
1223 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1224 menu_frame_hide_all();
1225 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1227 if ((f
= find_active_menu()))
1228 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1229 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1231 if ((f
= find_active_menu()) && f
->parent
)
1232 menu_frame_select(f
, NULL
);
1233 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1235 if ((f
= find_active_menu()) && f
->child
)
1236 menu_frame_select_next(f
->child
);
1237 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1239 if ((f
= find_active_menu()))
1240 menu_frame_select_previous(f
);
1241 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1243 if ((f
= find_active_menu()))
1244 menu_frame_select_next(f
);
1250 static gboolean
menu_hide_delay_func(gpointer data
)
1252 menu_can_hide
= TRUE
;
1253 return FALSE
; /* no repeat */
1256 static gboolean
focus_delay_func(gpointer data
)
1261 if (config_focus_raise
)
1262 stacking_raise(CLIENT_AS_WINDOW(c
));
1263 return FALSE
; /* no repeat */
1266 static void focus_delay_client_dest(gpointer data
)
1270 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
, c
);
1273 void event_ignore_queued_enters()
1275 GSList
*saved
= NULL
, *it
;
1278 XSync(ob_display
, FALSE
);
1280 /* count the events */
1282 e
= g_new(XEvent
, 1);
1283 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1284 saved
= g_slist_append(saved
, e
);
1285 ++ignore_enter_focus
;
1291 /* put the events back */
1292 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1293 XPutBackEvent(ob_display
, it
->data
);
1296 g_slist_free(saved
);