1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 event.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003 Ben Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
32 #include "menuframe.h"
36 #include "framerender.h"
38 #include "moveresize.h"
41 #include "extensions.h"
44 #include <X11/keysym.h>
45 #include <X11/Xatom.h>
48 #ifdef HAVE_SYS_SELECT_H
49 # include <sys/select.h>
55 # include <X11/XKBlib.h>
59 #include <X11/ICE/ICElib.h>
73 static void event_process(const XEvent
*e
, gpointer data
);
74 static void event_client_dest(ObClient
*client
, gpointer data
);
75 static void event_handle_root(XEvent
*e
);
76 static void event_handle_menu(XEvent
*e
);
77 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
78 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
79 static void event_handle_client(ObClient
*c
, XEvent
*e
);
80 static void event_handle_group(ObGroup
*g
, XEvent
*e
);
82 static gboolean
focus_delay_func(gpointer data
);
83 static void focus_delay_client_dest(ObClient
*client
, gpointer data
);
85 static gboolean
menu_hide_delay_func(gpointer data
);
87 /* The time for the current event being processed */
88 Time event_curtime
= CurrentTime
;
90 /*! The value of the mask for the NumLock modifier */
92 /*! The value of the mask for the ScrollLock modifier */
94 /*! The key codes for the modifier keys */
95 static XModifierKeymap
*modmap
;
96 /*! Table of the constant modifier masks */
97 static const gint mask_table
[] = {
98 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
99 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
101 static gint mask_table_size
;
103 static guint ignore_enter_focus
= 0;
105 static gboolean menu_can_hide
;
107 static ObFocusDelayData focus_delay_data
= { .client
= NULL
,
108 .time
= CurrentTime
};
113 static void ice_handler(gint fd
, gpointer conn
)
116 IceProcessMessages(conn
, NULL
, &b
);
119 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
120 IcePointer
*watch_data
)
125 fd
= IceConnectionNumber(conn
);
126 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
128 ob_main_loop_fd_remove(ob_main_loop
, fd
);
134 void event_startup(gboolean reconfig
)
136 if (reconfig
) return;
138 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
140 /* get lock masks that are defined by the display (not constant) */
141 modmap
= XGetModifierMapping(ob_display
);
143 if (modmap
&& modmap
->max_keypermod
> 0) {
145 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
146 /* get the values of the keyboard lock modifiers
147 Note: Caps lock is not retrieved the same way as Scroll and Num
148 lock since it doesn't need to be. */
149 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
150 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
153 for (cnt
= 0; cnt
< size
; ++cnt
) {
154 if (! modmap
->modifiermap
[cnt
]) continue;
156 if (num_lock
== modmap
->modifiermap
[cnt
])
157 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
158 if (scroll_lock
== modmap
->modifiermap
[cnt
])
159 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
163 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
166 IceAddConnectionWatch(ice_watch
, NULL
);
169 client_add_destructor(focus_delay_client_dest
, NULL
);
170 client_add_destructor(event_client_dest
, NULL
);
173 void event_shutdown(gboolean reconfig
)
175 if (reconfig
) return;
178 IceRemoveConnectionWatch(ice_watch
, NULL
);
181 client_remove_destructor(focus_delay_client_dest
);
182 client_remove_destructor(event_client_dest
);
183 XFreeModifiermap(modmap
);
186 static Window
event_get_window(XEvent
*e
)
193 window
= RootWindow(ob_display
, ob_screen
);
196 window
= e
->xmap
.window
;
199 window
= e
->xunmap
.window
;
202 window
= e
->xdestroywindow
.window
;
204 case ConfigureRequest
:
205 window
= e
->xconfigurerequest
.window
;
207 case ConfigureNotify
:
208 window
= e
->xconfigure
.window
;
212 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
213 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
215 window
= ((XkbBellNotifyEvent
*)e
)->window
;
221 window
= e
->xany
.window
;
226 static void event_set_curtime(XEvent
*e
)
228 Time t
= CurrentTime
;
230 /* grab the lasttime and hack up the state */
246 t
= e
->xproperty
.time
;
250 t
= e
->xcrossing
.time
;
253 /* if more event types are anticipated, get their timestamp
261 #define STRIP_MODS(s) \
262 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
263 /* kill off the Button1Mask etc, only want the modifiers */ \
264 s &= (ControlMask | ShiftMask | Mod1Mask | \
265 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
267 static void event_hack_mods(XEvent *e)
270 XkbStateRec xkb_state
;
278 STRIP_MODS(e
->xbutton
.state
);
281 STRIP_MODS(e
->xkey
.state
);
284 STRIP_MODS(e
->xkey
.state
);
285 /* remove from the state the mask of the modifier being released, if
286 it is a modifier key being released (this is a little ugly..) */
288 if (XkbGetState(ob_display
, XkbUseCoreKbd
, &xkb_state
) == Success
) {
289 e
->xkey
.state
= xkb_state
.compat_state
;
293 kp
= modmap
->modifiermap
;
294 for (i
= 0; i
< mask_table_size
; ++i
) {
295 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
296 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
297 /* remove the mask for it */
298 e
->xkey
.state
&= ~mask_table
[i
];
299 /* cause the first loop to break; */
301 break; /* get outta here! */
308 STRIP_MODS(e
->xmotion
.state
);
309 /* compress events */
312 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
314 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
315 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
322 static gboolean
wanted_focusevent(XEvent
*e
)
324 gint mode
= e
->xfocus
.mode
;
325 gint detail
= e
->xfocus
.detail
;
326 Window win
= e
->xany
.window
;
328 if (e
->type
== FocusIn
) {
330 /* These are ones we never want.. */
332 /* This means focus was given by a keyboard/mouse grab. */
333 if (mode
== NotifyGrab
)
335 /* This means focus was given back from a keyboard/mouse grab. */
336 if (mode
== NotifyUngrab
)
339 /* These are the ones we want.. */
341 if (win
== RootWindow(ob_display
, ob_screen
)) {
342 /* This means focus reverted off of a client */
343 if (detail
== NotifyPointerRoot
|| detail
== NotifyDetailNone
||
344 detail
== NotifyInferior
)
350 /* This means focus moved from the root window to a client */
351 if (detail
== NotifyVirtual
)
353 /* This means focus moved from one client to another */
354 if (detail
== NotifyNonlinearVirtual
)
357 /* This means focus reverted off of a client */
358 if (detail
== NotifyInferior
)
364 g_assert(e
->type
== FocusOut
);
367 /* These are ones we never want.. */
369 /* This means focus was taken by a keyboard/mouse grab. */
370 if (mode
== NotifyGrab
)
373 /* Focus left the root window revertedto state */
374 if (win
== RootWindow(ob_display
, ob_screen
))
377 /* These are the ones we want.. */
379 /* This means focus moved from a client to the root window */
380 if (detail
== NotifyVirtual
)
382 /* This means focus moved from one client to another */
383 if (detail
== NotifyNonlinearVirtual
)
391 static Bool
look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
393 return e
->type
== FocusIn
&& wanted_focusevent(e
);
396 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
401 return keyboard_interactively_grabbed();
404 /* I don't think this should ever happen with our event masks, but
405 if it does, we don't want it. */
408 if (!wanted_focusevent(e
))
415 static void event_process(const XEvent
*ec
, gpointer data
)
418 ObGroup
*group
= NULL
;
419 ObClient
*client
= NULL
;
421 ObDockApp
*dockapp
= NULL
;
422 ObWindow
*obwin
= NULL
;
424 ObEventData
*ed
= data
;
426 /* make a copy we can mangle */
430 window
= event_get_window(e
);
431 if (!(e
->type
== PropertyNotify
&&
432 (group
= g_hash_table_lookup(group_map
, &window
))))
433 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
434 switch (obwin
->type
) {
436 dock
= WINDOW_AS_DOCK(obwin
);
439 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
442 client
= WINDOW_AS_CLIENT(obwin
);
445 case Window_Internal
:
446 /* not to be used for events */
447 g_assert_not_reached();
452 if (e
->type
== FocusIn
|| e
->type
== FocusOut
) {
453 gint mode
= e
->xfocus
.mode
;
454 gint detail
= e
->xfocus
.detail
;
455 Window window
= e
->xfocus
.window
;
456 if (detail
== NotifyVirtual
) {
457 ob_debug_type(OB_DEBUG_FOCUS
,
458 "FOCUS %s NOTIFY VIRTUAL window 0x%x\n",
459 (e
->type
== FocusIn
? "IN" : "OUT"), window
);
462 else if (detail
== NotifyNonlinearVirtual
) {
463 ob_debug_type(OB_DEBUG_FOCUS
,
464 "FOCUS %s NOTIFY NONLINVIRTUAL window 0x%x\n",
465 (e
->type
== FocusIn
? "IN" : "OUT"), window
);
469 ob_debug_type(OB_DEBUG_FOCUS
,
470 "UNKNOWN FOCUS %s (d %d, m %d) window 0x%x\n",
471 (e
->type
== FocusIn
? "IN" : "OUT"),
472 detail
, mode
, window
);
475 event_set_curtime(e
);
477 if (event_ignore(e
, client
)) {
484 /* deal with it in the kernel */
486 if (menu_frame_visible
&&
487 (e
->type
== EnterNotify
|| e
->type
== LeaveNotify
))
489 /* crossing events for menu */
490 event_handle_menu(e
);
492 event_handle_group(group
, e
);
494 event_handle_client(client
, e
);
496 event_handle_dockapp(dockapp
, e
);
498 event_handle_dock(dock
, e
);
499 else if (window
== RootWindow(ob_display
, ob_screen
))
500 event_handle_root(e
);
501 else if (e
->type
== MapRequest
)
502 client_manage(window
);
503 else if (e
->type
== ConfigureRequest
) {
504 /* unhandled configure requests must be used to configure the
508 xwc
.x
= e
->xconfigurerequest
.x
;
509 xwc
.y
= e
->xconfigurerequest
.y
;
510 xwc
.width
= e
->xconfigurerequest
.width
;
511 xwc
.height
= e
->xconfigurerequest
.height
;
512 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
513 xwc
.sibling
= e
->xconfigurerequest
.above
;
514 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
516 /* we are not to be held responsible if someone sends us an
518 xerror_set_ignore(TRUE
);
519 XConfigureWindow(ob_display
, window
,
520 e
->xconfigurerequest
.value_mask
, &xwc
);
521 xerror_set_ignore(FALSE
);
524 /* user input (action-bound) events */
525 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
526 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
527 e
->type
== KeyRelease
)
529 if (menu_frame_visible
)
530 event_handle_menu(e
);
532 if (!keyboard_process_interactive_grab(e
, &client
)) {
533 if (moveresize_in_progress
) {
536 /* make further actions work on the client being
538 client
= moveresize_client
;
541 menu_can_hide
= FALSE
;
542 ob_main_loop_timeout_add(ob_main_loop
,
543 config_menu_hide_delay
* 1000,
544 menu_hide_delay_func
,
547 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
548 e
->type
== MotionNotify
)
549 mouse_event(client
, e
);
550 else if (e
->type
== KeyPress
) {
551 keyboard_event((focus_cycle_target
? focus_cycle_target
:
552 (focus_hilite
? focus_hilite
: client
)),
558 /* if something happens and it's not from an XEvent, then we don't know
560 event_curtime
= CurrentTime
;
563 static void event_handle_root(XEvent
*e
)
569 ob_debug("Another WM has requested to replace us. Exiting.\n");
574 if (e
->xclient
.format
!= 32) break;
576 msgtype
= e
->xclient
.message_type
;
577 if (msgtype
== prop_atoms
.net_current_desktop
) {
578 guint d
= e
->xclient
.data
.l
[0];
579 if (d
< screen_num_desktops
) {
580 event_curtime
= e
->xclient
.data
.l
[1];
581 ob_debug("SWITCH DESKTOP TIME: %d\n", event_curtime
);
582 screen_set_desktop(d
);
584 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
585 guint d
= e
->xclient
.data
.l
[0];
587 screen_set_num_desktops(d
);
588 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
589 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
590 } else if (msgtype
== prop_atoms
.ob_control
) {
591 if (e
->xclient
.data
.l
[0] == 1)
593 else if (e
->xclient
.data
.l
[0] == 2)
598 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
599 screen_update_desktop_names();
600 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
601 screen_update_layout();
603 case ConfigureNotify
:
605 XRRUpdateConfiguration(e
);
614 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
618 g_assert(e
->type
== PropertyNotify
);
620 for (it
= group
->members
; it
; it
= g_slist_next(it
))
621 event_handle_client(it
->data
, e
);
624 void event_enter_client(ObClient
*client
)
626 g_assert(config_focus_follow
);
628 if (client_normal(client
) && client_can_focus(client
)) {
629 if (config_focus_delay
) {
630 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
632 focus_delay_data
.client
= client
;
633 focus_delay_data
.time
= event_curtime
;
635 ob_main_loop_timeout_add(ob_main_loop
,
640 focus_delay_data
.client
= client
;
641 focus_delay_data
.time
= event_curtime
;
642 focus_delay_func(NULL
);
647 static void event_handle_client(ObClient
*client
, XEvent
*e
)
655 case VisibilityNotify
:
656 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
660 /* Wheel buttons don't draw because they are an instant click, so it
661 is a waste of resources to go drawing it. */
662 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
663 con
= frame_context(client
, e
->xbutton
.window
);
664 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
666 case OB_FRAME_CONTEXT_MAXIMIZE
:
667 client
->frame
->max_press
= (e
->type
== ButtonPress
);
668 framerender_frame(client
->frame
);
670 case OB_FRAME_CONTEXT_CLOSE
:
671 client
->frame
->close_press
= (e
->type
== ButtonPress
);
672 framerender_frame(client
->frame
);
674 case OB_FRAME_CONTEXT_ICONIFY
:
675 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
676 framerender_frame(client
->frame
);
678 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
679 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
680 framerender_frame(client
->frame
);
682 case OB_FRAME_CONTEXT_SHADE
:
683 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
684 framerender_frame(client
->frame
);
687 /* nothing changes with clicks for any other contexts */
693 if (client
!= focus_client
) {
694 focus_set_client(client
);
695 frame_adjust_focus(client
->frame
, TRUE
);
696 client_calc_layer(client
);
700 /* Look for the followup FocusIn */
701 if (!XCheckIfEvent(ob_display
, &ce
, look_for_focusin
, NULL
)) {
702 /* There is no FocusIn, this means focus went to a window that
703 is not being managed, or a window on another screen. */
704 ob_debug_type(OB_DEBUG_FOCUS
, "Focus went to a black hole !\n");
705 } else if (ce
.xany
.window
== e
->xany
.window
) {
706 /* If focus didn't actually move anywhere, there is nothing to do*/
708 } else if (ce
.xfocus
.detail
== NotifyPointerRoot
||
709 ce
.xfocus
.detail
== NotifyDetailNone
) {
710 ob_debug_type(OB_DEBUG_FOCUS
, "Focus went to root\n");
711 /* Focus has been reverted to the root window or nothing, so fall
712 back to something other than the window which just had it. */
713 focus_fallback(FALSE
);
714 } else if (ce
.xfocus
.detail
== NotifyInferior
) {
715 ob_debug_type(OB_DEBUG_FOCUS
, "Focus went to parent\n");
716 /* Focus has been reverted to parent, which is our frame window,
717 or the root window, so fall back to something other than the
718 window which had it. */
719 focus_fallback(FALSE
);
721 /* Focus did move, so process the FocusIn event */
722 ObEventData ed
= { .ignored
= FALSE
};
723 event_process(&ce
, &ed
);
725 /* The FocusIn was ignored, this means it was on a window
726 that isn't a client. */
727 ob_debug_type(OB_DEBUG_FOCUS
,
728 "Focus went to an unmanaged window 0x%x !\n",
730 focus_fallback(TRUE
);
734 /* This client is no longer focused, so show that */
736 frame_adjust_focus(client
->frame
, FALSE
);
737 client_calc_layer(client
);
740 con
= frame_context(client
, e
->xcrossing
.window
);
742 case OB_FRAME_CONTEXT_MAXIMIZE
:
743 client
->frame
->max_hover
= FALSE
;
744 frame_adjust_state(client
->frame
);
746 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
747 client
->frame
->desk_hover
= FALSE
;
748 frame_adjust_state(client
->frame
);
750 case OB_FRAME_CONTEXT_SHADE
:
751 client
->frame
->shade_hover
= FALSE
;
752 frame_adjust_state(client
->frame
);
754 case OB_FRAME_CONTEXT_ICONIFY
:
755 client
->frame
->iconify_hover
= FALSE
;
756 frame_adjust_state(client
->frame
);
758 case OB_FRAME_CONTEXT_CLOSE
:
759 client
->frame
->close_hover
= FALSE
;
760 frame_adjust_state(client
->frame
);
762 case OB_FRAME_CONTEXT_FRAME
:
763 if (config_focus_follow
&& config_focus_delay
&&
764 focus_delay_data
.client
== client
)
766 event_halt_focus_delay();
775 gboolean nofocus
= FALSE
;
777 if (ignore_enter_focus
) {
778 ignore_enter_focus
--;
782 con
= frame_context(client
, e
->xcrossing
.window
);
784 case OB_FRAME_CONTEXT_MAXIMIZE
:
785 client
->frame
->max_hover
= TRUE
;
786 frame_adjust_state(client
->frame
);
788 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
789 client
->frame
->desk_hover
= TRUE
;
790 frame_adjust_state(client
->frame
);
792 case OB_FRAME_CONTEXT_SHADE
:
793 client
->frame
->shade_hover
= TRUE
;
794 frame_adjust_state(client
->frame
);
796 case OB_FRAME_CONTEXT_ICONIFY
:
797 client
->frame
->iconify_hover
= TRUE
;
798 frame_adjust_state(client
->frame
);
800 case OB_FRAME_CONTEXT_CLOSE
:
801 client
->frame
->close_hover
= TRUE
;
802 frame_adjust_state(client
->frame
);
804 case OB_FRAME_CONTEXT_FRAME
:
805 if (e
->xcrossing
.mode
== NotifyGrab
||
806 e
->xcrossing
.mode
== NotifyUngrab
)
808 ob_debug_type(OB_DEBUG_FOCUS
,
809 "%sNotify mode %d detail %d on %lx IGNORED\n",
810 (e
->type
== EnterNotify
? "Enter" : "Leave"),
812 e
->xcrossing
.detail
, client
?client
->window
:0);
814 ob_debug_type(OB_DEBUG_FOCUS
,
815 "%sNotify mode %d detail %d on %lx, "
816 "focusing window: %d\n",
817 (e
->type
== EnterNotify
? "Enter" : "Leave"),
819 e
->xcrossing
.detail
, (client
?client
->window
:0),
821 if (!nofocus
&& config_focus_follow
)
822 event_enter_client(client
);
830 case ConfigureRequest
:
832 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
833 ConfigureRequest
, &ce
)) {
835 /* XXX if this causes bad things.. we can compress config req's
836 with the same mask. */
837 e
->xconfigurerequest
.value_mask
|=
838 ce
.xconfigurerequest
.value_mask
;
839 if (ce
.xconfigurerequest
.value_mask
& CWX
)
840 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
841 if (ce
.xconfigurerequest
.value_mask
& CWY
)
842 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
843 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
844 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
845 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
846 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
847 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
848 e
->xconfigurerequest
.border_width
=
849 ce
.xconfigurerequest
.border_width
;
850 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
851 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
854 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
855 if (client
->iconic
|| client
->shaded
) return;
857 /* resize, then move, as specified in the EWMH section 7.7 */
858 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
864 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
865 client
->border_width
= e
->xconfigurerequest
.border_width
;
867 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
868 e
->xconfigurerequest
.x
: client
->area
.x
;
869 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
870 e
->xconfigurerequest
.y
: client
->area
.y
;
871 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
872 e
->xconfigurerequest
.width
: client
->area
.width
;
873 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
874 e
->xconfigurerequest
.height
: client
->area
.height
;
880 client
->frame
->size
.left
+ client
->frame
->size
.right
;
882 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
883 /* make this rude for size-only changes but not for position
885 gboolean moving
= ((e
->xconfigurerequest
.value_mask
& CWX
) ||
886 (e
->xconfigurerequest
.value_mask
& CWY
));
888 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
890 if (e
->xconfigurerequest
.value_mask
& CWX
)
892 if (e
->xconfigurerequest
.value_mask
& CWY
)
896 switch (client
->gravity
) {
897 case NorthEastGravity
:
899 corner
= OB_CORNER_TOPRIGHT
;
901 case SouthWestGravity
:
903 corner
= OB_CORNER_BOTTOMLEFT
;
905 case SouthEastGravity
:
906 corner
= OB_CORNER_BOTTOMRIGHT
;
908 default: /* NorthWest, Static, etc */
909 corner
= OB_CORNER_TOPLEFT
;
912 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
916 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
917 switch (e
->xconfigurerequest
.detail
) {
920 /* Apps are so rude. And this is totally disconnected from
921 activation/focus. Bleh. */
922 /*client_lower(client);*/
928 /* Apps are so rude. And this is totally disconnected from
929 activation/focus. Bleh. */
930 /*client_raise(client);*/
936 if (client
->ignore_unmaps
) {
937 client
->ignore_unmaps
--;
940 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
942 client
->window
, e
->xunmap
.event
, e
->xunmap
.from_configure
,
943 client
->ignore_unmaps
);
944 client_unmanage(client
);
947 ob_debug("DestroyNotify for window 0x%x\n", client
->window
);
948 client_unmanage(client
);
951 /* this is when the client is first taken captive in the frame */
952 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
955 This event is quite rare and is usually handled in unmapHandler.
956 However, if the window is unmapped when the reparent event occurs,
957 the window manager never sees it because an unmap event is not sent
958 to an already unmapped window.
961 /* we don't want the reparent event, put it back on the stack for the
962 X server to deal with after we unmanage the window */
963 XPutBackEvent(ob_display
, e
);
965 ob_debug("ReparentNotify for window 0x%x\n", client
->window
);
966 client_unmanage(client
);
969 ob_debug("MapRequest for 0x%lx\n", client
->window
);
970 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
971 does, we don't want it!
972 it can happen now when the window is on
973 another desktop, but we still don't
975 client_activate(client
, FALSE
, TRUE
);
978 /* validate cuz we query stuff off the client here */
979 if (!client_validate(client
)) break;
981 if (e
->xclient
.format
!= 32) return;
983 msgtype
= e
->xclient
.message_type
;
984 if (msgtype
== prop_atoms
.wm_change_state
) {
985 /* compress changes into a single change */
986 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
988 /* XXX: it would be nice to compress ALL messages of a
989 type, not just messages in a row without other
990 message types between. */
991 if (ce
.xclient
.message_type
!= msgtype
) {
992 XPutBackEvent(ob_display
, &ce
);
995 e
->xclient
= ce
.xclient
;
997 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
998 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
999 /* compress changes into a single change */
1000 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1002 /* XXX: it would be nice to compress ALL messages of a
1003 type, not just messages in a row without other
1004 message types between. */
1005 if (ce
.xclient
.message_type
!= msgtype
) {
1006 XPutBackEvent(ob_display
, &ce
);
1009 e
->xclient
= ce
.xclient
;
1011 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
1012 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
1013 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
1015 } else if (msgtype
== prop_atoms
.net_wm_state
) {
1016 /* can't compress these */
1017 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1018 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
1019 e
->xclient
.data
.l
[0] == 1 ? "Add" :
1020 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1021 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
1023 client_set_state(client
, e
->xclient
.data
.l
[0],
1024 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
1025 } else if (msgtype
== prop_atoms
.net_close_window
) {
1026 ob_debug("net_close_window for 0x%lx\n", client
->window
);
1027 client_close(client
);
1028 } else if (msgtype
== prop_atoms
.net_active_window
) {
1029 ob_debug("net_active_window for 0x%lx source=%s\n",
1031 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
1032 (e
->xclient
.data
.l
[0] == 1 ? "application" :
1033 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
1034 /* XXX make use of data.l[2] ! */
1035 event_curtime
= e
->xclient
.data
.l
[1];
1036 client_activate(client
, FALSE
,
1037 (e
->xclient
.data
.l
[0] == 0 ||
1038 e
->xclient
.data
.l
[0] == 2));
1039 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
1040 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1041 client
->window
, e
->xclient
.data
.l
[2]);
1042 if ((Atom
)e
->xclient
.data
.l
[2] ==
1043 prop_atoms
.net_wm_moveresize_size_topleft
||
1044 (Atom
)e
->xclient
.data
.l
[2] ==
1045 prop_atoms
.net_wm_moveresize_size_top
||
1046 (Atom
)e
->xclient
.data
.l
[2] ==
1047 prop_atoms
.net_wm_moveresize_size_topright
||
1048 (Atom
)e
->xclient
.data
.l
[2] ==
1049 prop_atoms
.net_wm_moveresize_size_right
||
1050 (Atom
)e
->xclient
.data
.l
[2] ==
1051 prop_atoms
.net_wm_moveresize_size_right
||
1052 (Atom
)e
->xclient
.data
.l
[2] ==
1053 prop_atoms
.net_wm_moveresize_size_bottomright
||
1054 (Atom
)e
->xclient
.data
.l
[2] ==
1055 prop_atoms
.net_wm_moveresize_size_bottom
||
1056 (Atom
)e
->xclient
.data
.l
[2] ==
1057 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1058 (Atom
)e
->xclient
.data
.l
[2] ==
1059 prop_atoms
.net_wm_moveresize_size_left
||
1060 (Atom
)e
->xclient
.data
.l
[2] ==
1061 prop_atoms
.net_wm_moveresize_move
||
1062 (Atom
)e
->xclient
.data
.l
[2] ==
1063 prop_atoms
.net_wm_moveresize_size_keyboard
||
1064 (Atom
)e
->xclient
.data
.l
[2] ==
1065 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1067 moveresize_start(client
, e
->xclient
.data
.l
[0],
1068 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1069 e
->xclient
.data
.l
[2]);
1071 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1072 prop_atoms
.net_wm_moveresize_cancel
)
1073 moveresize_end(TRUE
);
1074 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1075 gint oldg
= client
->gravity
;
1076 gint tmpg
, x
, y
, w
, h
;
1078 if (e
->xclient
.data
.l
[0] & 0xff)
1079 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
1083 if (e
->xclient
.data
.l
[0] & 1 << 8)
1084 x
= e
->xclient
.data
.l
[1];
1087 if (e
->xclient
.data
.l
[0] & 1 << 9)
1088 y
= e
->xclient
.data
.l
[2];
1091 if (e
->xclient
.data
.l
[0] & 1 << 10)
1092 w
= e
->xclient
.data
.l
[3];
1094 w
= client
->area
.width
;
1095 if (e
->xclient
.data
.l
[0] & 1 << 11)
1096 h
= e
->xclient
.data
.l
[4];
1098 h
= client
->area
.height
;
1099 client
->gravity
= tmpg
;
1105 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1107 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1108 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1109 client_normal(client
));
1110 if (e
->xclient
.data
.l
[0] & 1 << 8)
1112 if (e
->xclient
.data
.l
[0] & 1 << 9)
1116 client_configure(client
, OB_CORNER_TOPLEFT
,
1117 x
, y
, w
, h
, FALSE
, TRUE
);
1119 client
->gravity
= oldg
;
1122 case PropertyNotify
:
1123 /* validate cuz we query stuff off the client here */
1124 if (!client_validate(client
)) break;
1126 /* compress changes to a single property into a single change */
1127 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1131 /* XXX: it would be nice to compress ALL changes to a property,
1132 not just changes in a row without other props between. */
1134 a
= ce
.xproperty
.atom
;
1135 b
= e
->xproperty
.atom
;
1139 if ((a
== prop_atoms
.net_wm_name
||
1140 a
== prop_atoms
.wm_name
||
1141 a
== prop_atoms
.net_wm_icon_name
||
1142 a
== prop_atoms
.wm_icon_name
)
1144 (b
== prop_atoms
.net_wm_name
||
1145 b
== prop_atoms
.wm_name
||
1146 b
== prop_atoms
.net_wm_icon_name
||
1147 b
== prop_atoms
.wm_icon_name
)) {
1150 if (a
== prop_atoms
.net_wm_icon
&&
1151 b
== prop_atoms
.net_wm_icon
)
1154 XPutBackEvent(ob_display
, &ce
);
1158 msgtype
= e
->xproperty
.atom
;
1159 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1160 client_update_normal_hints(client
);
1161 /* normal hints can make a window non-resizable */
1162 client_setup_decor_and_functions(client
);
1163 } else if (msgtype
== XA_WM_HINTS
) {
1164 client_update_wmhints(client
);
1165 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1166 client_update_transient_for(client
);
1167 client_get_type(client
);
1168 /* type may have changed, so update the layer */
1169 client_calc_layer(client
);
1170 client_setup_decor_and_functions(client
);
1171 } else if (msgtype
== prop_atoms
.net_wm_name
||
1172 msgtype
== prop_atoms
.wm_name
||
1173 msgtype
== prop_atoms
.net_wm_icon_name
||
1174 msgtype
== prop_atoms
.wm_icon_name
) {
1175 client_update_title(client
);
1176 } else if (msgtype
== prop_atoms
.wm_class
) {
1177 client_update_class(client
);
1178 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1179 client_update_protocols(client
);
1180 client_setup_decor_and_functions(client
);
1182 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1183 client_update_strut(client
);
1185 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1186 client_update_icons(client
);
1188 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1189 client_update_user_time(client
);
1191 else if (msgtype
== prop_atoms
.sm_client_id
) {
1192 client_update_sm_client_id(client
);
1197 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1198 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1199 frame_adjust_shape(client
->frame
);
1205 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1209 if (e
->xbutton
.button
== 1)
1210 stacking_raise(DOCK_AS_WINDOW(s
));
1211 else if (e
->xbutton
.button
== 2)
1212 stacking_lower(DOCK_AS_WINDOW(s
));
1223 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1227 dock_app_drag(app
, &e
->xmotion
);
1230 if (app
->ignore_unmaps
) {
1231 app
->ignore_unmaps
--;
1234 dock_remove(app
, TRUE
);
1237 dock_remove(app
, FALSE
);
1239 case ReparentNotify
:
1240 dock_remove(app
, FALSE
);
1242 case ConfigureNotify
:
1243 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1248 ObMenuFrame
* find_active_menu()
1251 ObMenuFrame
*ret
= NULL
;
1253 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1262 ObMenuFrame
* find_active_or_last_menu()
1264 ObMenuFrame
*ret
= NULL
;
1266 ret
= find_active_menu();
1267 if (!ret
&& menu_frame_visible
)
1268 ret
= menu_frame_visible
->data
;
1272 static void event_handle_menu(XEvent
*ev
)
1275 ObMenuEntryFrame
*e
;
1279 if (menu_can_hide
) {
1280 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1281 ev
->xbutton
.y_root
)))
1282 menu_entry_frame_execute(e
, ev
->xbutton
.state
,
1285 menu_frame_hide_all();
1289 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
))) {
1290 if (e
->ignore_enters
)
1293 menu_frame_select(e
->frame
, e
);
1297 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
)) &&
1298 (f
= find_active_menu()) && f
->selected
== e
&&
1299 e
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1301 menu_frame_select(e
->frame
, NULL
);
1304 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1305 menu_frame_hide_all();
1306 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1308 if ((f
= find_active_menu()))
1309 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
,
1311 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1313 if ((f
= find_active_or_last_menu()) && f
->parent
)
1314 menu_frame_select(f
, NULL
);
1315 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1317 if ((f
= find_active_or_last_menu()) && f
->child
)
1318 menu_frame_select_next(f
->child
);
1319 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1321 if ((f
= find_active_or_last_menu()))
1322 menu_frame_select_previous(f
);
1323 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1325 if ((f
= find_active_or_last_menu()))
1326 menu_frame_select_next(f
);
1332 static gboolean
menu_hide_delay_func(gpointer data
)
1334 menu_can_hide
= TRUE
;
1335 return FALSE
; /* no repeat */
1338 static gboolean
focus_delay_func(gpointer data
)
1340 Time old
= event_curtime
;
1341 event_curtime
= focus_delay_data
.time
;
1342 if (focus_client
!= focus_delay_data
.client
) {
1343 if (client_focus(focus_delay_data
.client
) && config_focus_raise
)
1344 client_raise(focus_delay_data
.client
);
1346 event_curtime
= old
;
1347 return FALSE
; /* no repeat */
1350 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1352 if (focus_delay_data
.client
== client
)
1353 event_halt_focus_delay();
1356 static void event_client_dest(ObClient
*client
, gpointer data
)
1358 if (client
== focus_hilite
)
1359 focus_hilite
= NULL
;
1362 void event_halt_focus_delay()
1364 focus_delay_data
.client
= NULL
;
1365 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1368 void event_ignore_queued_enters()
1370 GSList
*saved
= NULL
, *it
;
1373 XSync(ob_display
, FALSE
);
1375 /* count the events */
1377 e
= g_new(XEvent
, 1);
1378 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1381 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1382 if (win
&& WINDOW_IS_CLIENT(win
))
1383 ++ignore_enter_focus
;
1385 saved
= g_slist_append(saved
, e
);
1391 /* put the events back */
1392 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1393 XPutBackEvent(ob_display
, it
->data
);
1396 g_slist_free(saved
);
1399 gboolean
event_time_after(Time t1
, Time t2
)
1401 g_assert(t1
!= CurrentTime
);
1402 g_assert(t2
!= CurrentTime
);
1405 Timestamp values wrap around (after about 49.7 days). The server, given
1406 its current time is represented by timestamp T, always interprets
1407 timestamps from clients by treating half of the timestamp space as being
1408 later in time than T.
1409 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1412 /* TIME_HALF is half of the number space of a Time type variable */
1413 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1415 if (t2
>= TIME_HALF
)
1416 /* t2 is in the second half so t1 might wrap around and be smaller than
1418 return t1
>= t2
|| t1
< (t2
+ TIME_HALF
);
1420 /* t2 is in the first half so t1 has to come after it */
1421 return t1
>= t2
&& t1
< (t2
+ TIME_HALF
);