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-2007 Dana 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.
33 #include "menuframe.h"
39 #include "framerender.h"
41 #include "focus_cycle.h"
42 #include "moveresize.h"
45 #include "extensions.h"
46 #include "translate.h"
49 #include <X11/Xatom.h>
52 #ifdef HAVE_SYS_SELECT_H
53 # include <sys/select.h>
59 # include <unistd.h> /* for usleep() */
62 # include <X11/XKBlib.h>
66 #include <X11/ICE/ICElib.h>
80 static void event_process(const XEvent
*e
, gpointer data
);
81 static void event_handle_root(XEvent
*e
);
82 static gboolean
event_handle_menu_keyboard(XEvent
*e
);
83 static gboolean
event_handle_menu(XEvent
*e
);
84 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
85 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
86 static void event_handle_client(ObClient
*c
, XEvent
*e
);
87 static void event_handle_user_time_window_clients(GSList
*l
, XEvent
*e
);
88 static void event_handle_user_input(ObClient
*client
, XEvent
*e
);
89 static gboolean
is_enter_focus_event_ignored(XEvent
*e
);
91 static void focus_delay_dest(gpointer data
);
92 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
);
93 static gboolean
focus_delay_func(gpointer data
);
94 static void focus_delay_client_dest(ObClient
*client
, gpointer data
);
96 static gboolean
menu_hide_delay_func(gpointer data
);
98 /* The time for the current event being processed */
99 Time event_curtime
= CurrentTime
;
101 static guint ignore_enter_focus
= 0;
102 static gboolean menu_can_hide
;
103 static gboolean focus_left_screen
= FALSE
;
106 static void ice_handler(gint fd
, gpointer conn
)
109 IceProcessMessages(conn
, NULL
, &b
);
112 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
113 IcePointer
*watch_data
)
118 fd
= IceConnectionNumber(conn
);
119 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
121 ob_main_loop_fd_remove(ob_main_loop
, fd
);
127 void event_startup(gboolean reconfig
)
129 if (reconfig
) return;
131 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
134 IceAddConnectionWatch(ice_watch
, NULL
);
137 client_add_destroy_notify(focus_delay_client_dest
, NULL
);
140 void event_shutdown(gboolean reconfig
)
142 if (reconfig
) return;
145 IceRemoveConnectionWatch(ice_watch
, NULL
);
148 client_remove_destroy_notify(focus_delay_client_dest
);
151 static Window
event_get_window(XEvent
*e
)
158 window
= RootWindow(ob_display
, ob_screen
);
161 window
= e
->xmap
.window
;
164 window
= e
->xunmap
.window
;
167 window
= e
->xdestroywindow
.window
;
169 case ConfigureRequest
:
170 window
= e
->xconfigurerequest
.window
;
172 case ConfigureNotify
:
173 window
= e
->xconfigure
.window
;
177 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
178 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
180 window
= ((XkbBellNotifyEvent
*)e
)->window
;
187 if (extensions_sync
&&
188 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
193 window
= e
->xany
.window
;
198 static void event_set_curtime(XEvent
*e
)
200 Time t
= CurrentTime
;
202 /* grab the lasttime and hack up the state */
218 t
= e
->xproperty
.time
;
222 t
= e
->xcrossing
.time
;
226 if (extensions_sync
&&
227 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
229 t
= ((XSyncAlarmNotifyEvent
*)e
)->time
;
232 /* if more event types are anticipated, get their timestamp
240 static void event_hack_mods(XEvent
*e
)
243 XkbStateRec xkb_state
;
249 e
->xbutton
.state
= modkeys_only_modifier_masks(e
->xbutton
.state
);
252 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
255 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
257 if (XkbGetState(ob_display
, XkbUseCoreKbd
, &xkb_state
) == Success
) {
258 e
->xkey
.state
= xkb_state
.compat_state
;
262 /* remove from the state the mask of the modifier key being released,
263 if it is a modifier key being released that is */
264 e
->xkey
.state
&= ~modkeys_keycode_to_mask(e
->xkey
.keycode
);
267 e
->xmotion
.state
= modkeys_only_modifier_masks(e
->xmotion
.state
);
268 /* compress events */
271 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
273 e
->xmotion
.x
= ce
.xmotion
.x
;
274 e
->xmotion
.y
= ce
.xmotion
.y
;
275 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
276 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
283 static gboolean
wanted_focusevent(XEvent
*e
, gboolean in_client_only
)
285 gint mode
= e
->xfocus
.mode
;
286 gint detail
= e
->xfocus
.detail
;
287 Window win
= e
->xany
.window
;
289 if (e
->type
== FocusIn
) {
290 /* These are ones we never want.. */
292 /* This means focus was given by a keyboard/mouse grab. */
293 if (mode
== NotifyGrab
)
295 /* This means focus was given back from a keyboard/mouse grab. */
296 if (mode
== NotifyUngrab
)
299 /* These are the ones we want.. */
301 if (win
== RootWindow(ob_display
, ob_screen
)) {
302 /* If looking for a focus in on a client, then always return
303 FALSE for focus in's to the root window */
306 /* This means focus reverted off of a client */
307 else if (detail
== NotifyPointerRoot
||
308 detail
== NotifyDetailNone
||
309 detail
== NotifyInferior
||
310 /* This means focus got here from another screen */
311 detail
== NotifyNonlinear
)
317 /* It was on a client, was it a valid one?
318 It's possible to get a FocusIn event for a client that was managed
321 if (in_client_only
) {
322 ObWindow
*w
= g_hash_table_lookup(window_map
, &e
->xfocus
.window
);
323 if (!w
|| !WINDOW_IS_CLIENT(w
))
327 /* This means focus reverted to parent from the client (this
328 happens often during iconify animation) */
329 if (detail
== NotifyInferior
)
333 /* This means focus moved from the root window to a client */
334 if (detail
== NotifyVirtual
)
336 /* This means focus moved from one client to another */
337 if (detail
== NotifyNonlinearVirtual
)
343 g_assert(e
->type
== FocusOut
);
345 /* These are ones we never want.. */
347 /* This means focus was taken by a keyboard/mouse grab. */
348 if (mode
== NotifyGrab
)
350 /* This means focus was grabbed on a window and it was released. */
351 if (mode
== NotifyUngrab
)
354 /* Focus left the root window revertedto state */
355 if (win
== RootWindow(ob_display
, ob_screen
))
358 /* These are the ones we want.. */
360 /* This means focus moved from a client to the root window */
361 if (detail
== NotifyVirtual
)
363 /* This means focus moved from one client to another */
364 if (detail
== NotifyNonlinearVirtual
)
366 /* This means focus moved off of our frame window.
367 When the client reverts to parent and it lands on our frame window,
368 and they are iconifying (not being unmanaged), then we don't get
369 a focus out from the client but only from the frame window, which
371 if (detail
== NotifyNonlinear
)
379 static Bool
event_look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
381 return e
->type
== FocusIn
&& wanted_focusevent(e
, FALSE
);
384 static Bool
event_look_for_focusin_client(Display
*d
, XEvent
*e
, XPointer arg
)
386 return e
->type
== FocusIn
&& wanted_focusevent(e
, TRUE
);
389 static void print_focusevent(XEvent
*e
)
391 gint mode
= e
->xfocus
.mode
;
392 gint detail
= e
->xfocus
.detail
;
393 Window win
= e
->xany
.window
;
394 const gchar
*modestr
, *detailstr
;
397 case NotifyNormal
: modestr
="NotifyNormal"; break;
398 case NotifyGrab
: modestr
="NotifyGrab"; break;
399 case NotifyUngrab
: modestr
="NotifyUngrab"; break;
400 case NotifyWhileGrabbed
: modestr
="NotifyWhileGrabbed"; break;
403 case NotifyAncestor
: detailstr
="NotifyAncestor"; break;
404 case NotifyVirtual
: detailstr
="NotifyVirtual"; break;
405 case NotifyInferior
: detailstr
="NotifyInferior"; break;
406 case NotifyNonlinear
: detailstr
="NotifyNonlinear"; break;
407 case NotifyNonlinearVirtual
: detailstr
="NotifyNonlinearVirtual"; break;
408 case NotifyPointer
: detailstr
="NotifyPointer"; break;
409 case NotifyPointerRoot
: detailstr
="NotifyPointerRoot"; break;
410 case NotifyDetailNone
: detailstr
="NotifyDetailNone"; break;
413 if (mode
== NotifyGrab
|| mode
== NotifyUngrab
)
418 ob_debug_type(OB_DEBUG_FOCUS
, "Focus%s 0x%x mode=%s detail=%s\n",
419 (e
->xfocus
.type
== FocusIn
? "In" : "Out"),
425 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
430 if (!wanted_focusevent(e
, FALSE
))
435 if (!wanted_focusevent(e
, FALSE
))
442 static void event_process(const XEvent
*ec
, gpointer data
)
445 ObClient
*client
= NULL
;
447 ObDockApp
*dockapp
= NULL
;
448 ObWindow
*obwin
= NULL
;
449 GSList
*timewinclients
= NULL
;
451 ObEventData
*ed
= data
;
453 /* make a copy we can mangle */
457 window
= event_get_window(e
);
458 if (e
->type
!= PropertyNotify
||
459 !(timewinclients
= propwin_get_clients(window
,
460 OB_PROPWIN_USER_TIME
)))
461 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
462 switch (obwin
->type
) {
464 dock
= WINDOW_AS_DOCK(obwin
);
467 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
470 client
= WINDOW_AS_CLIENT(obwin
);
473 case Window_Internal
:
474 /* not to be used for events */
475 g_assert_not_reached();
480 event_set_curtime(e
);
482 if (event_ignore(e
, client
)) {
489 /* deal with it in the kernel */
491 if (menu_frame_visible
&&
492 (e
->type
== EnterNotify
|| e
->type
== LeaveNotify
))
494 /* crossing events for menu */
495 event_handle_menu(e
);
496 } else if (e
->type
== FocusIn
) {
497 if (e
->xfocus
.detail
== NotifyPointerRoot
||
498 e
->xfocus
.detail
== NotifyDetailNone
||
499 e
->xfocus
.detail
== NotifyInferior
||
500 e
->xfocus
.detail
== NotifyNonlinear
)
504 ob_debug_type(OB_DEBUG_FOCUS
, "Focus went to root, "
505 "pointer root/none or "
506 "the frame window\n");
508 if (e
->xfocus
.detail
== NotifyInferior
||
509 e
->xfocus
.detail
== NotifyNonlinear
)
511 focus_left_screen
= FALSE
;
514 /* If another FocusIn is in the queue then don't fallback yet. This
515 fixes the fun case of:
516 window map -> send focusin
517 window unmap -> get focusout
518 window map -> send focusin
519 get first focus out -> fall back to something (new window
520 hasn't received focus yet, so something else) -> send focusin
521 which means the "something else" is the last thing to get a
522 focusin sent to it, so the new window doesn't end up with focus.
524 But if the other focus in is something like PointerRoot then we
525 still want to fall back.
527 if (XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin_client
,
530 XPutBackEvent(ob_display
, &ce
);
531 ob_debug_type(OB_DEBUG_FOCUS
,
532 " but another FocusIn is coming\n");
534 /* Focus has been reverted.
536 FocusOut events come after UnmapNotify, so we don't need to
537 worry about focusing an invalid window
540 if (!focus_left_screen
)
541 focus_fallback(FALSE
, FALSE
);
546 ob_debug_type(OB_DEBUG_FOCUS
,
547 "Focus went to a window that is already gone\n");
549 /* If you send focus to a window and then it disappears, you can
550 get the FocusIn for it, after it is unmanaged.
551 Just wait for the next FocusOut/FocusIn pair. */
553 else if (client
!= focus_client
) {
554 focus_left_screen
= FALSE
;
555 frame_adjust_focus(client
->frame
, TRUE
);
556 focus_set_client(client
);
557 client_calc_layer(client
);
558 client_bring_helper_windows(client
);
560 } else if (e
->type
== FocusOut
) {
563 /* Look for the followup FocusIn */
564 if (!XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin
, NULL
)) {
565 /* There is no FocusIn, this means focus went to a window that
566 is not being managed, or a window on another screen. */
570 xerror_set_ignore(TRUE
);
571 if (XGetInputFocus(ob_display
, &win
, &i
) != 0 &&
572 XGetGeometry(ob_display
, win
, &root
, &i
,&i
,&u
,&u
,&u
,&u
) != 0 &&
573 root
!= RootWindow(ob_display
, ob_screen
))
575 ob_debug_type(OB_DEBUG_FOCUS
,
576 "Focus went to another screen !\n");
577 focus_left_screen
= TRUE
;
580 ob_debug_type(OB_DEBUG_FOCUS
,
581 "Focus went to a black hole !\n");
582 xerror_set_ignore(FALSE
);
583 /* nothing is focused */
584 focus_set_client(NULL
);
586 /* Focus moved, so process the FocusIn event */
587 ObEventData ed
= { .ignored
= FALSE
};
588 event_process(&ce
, &ed
);
590 /* The FocusIn was ignored, this means it was on a window
591 that isn't a client. */
592 ob_debug_type(OB_DEBUG_FOCUS
,
593 "Focus went to an unmanaged window 0x%x !\n",
595 focus_fallback(TRUE
, FALSE
);
599 if (client
&& client
!= focus_client
) {
600 frame_adjust_focus(client
->frame
, FALSE
);
601 /* focus_set_client(NULL) has already been called in this
602 section or by focus_fallback */
603 client_calc_layer(client
);
605 } else if (timewinclients
)
606 event_handle_user_time_window_clients(timewinclients
, e
);
608 event_handle_client(client
, e
);
610 event_handle_dockapp(dockapp
, e
);
612 event_handle_dock(dock
, e
);
613 else if (window
== RootWindow(ob_display
, ob_screen
))
614 event_handle_root(e
);
615 else if (e
->type
== MapRequest
)
616 client_manage(window
);
617 else if (e
->type
== ClientMessage
) {
618 /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
619 windows that are not managed yet. */
620 if (e
->xclient
.message_type
== prop_atoms
.net_request_frame_extents
) {
621 /* Pretend to manage the client, getting information used to
622 determine its decorations */
623 ObClient
*c
= client_fake_manage(e
->xclient
.window
);
626 /* set the frame extents on the window */
627 vals
[0] = c
->frame
->size
.left
;
628 vals
[1] = c
->frame
->size
.right
;
629 vals
[2] = c
->frame
->size
.top
;
630 vals
[3] = c
->frame
->size
.bottom
;
631 PROP_SETA32(e
->xclient
.window
, net_frame_extents
,
634 /* Free the pretend client */
635 client_fake_unmanage(c
);
638 else if (e
->type
== ConfigureRequest
) {
639 /* unhandled configure requests must be used to configure the
643 xwc
.x
= e
->xconfigurerequest
.x
;
644 xwc
.y
= e
->xconfigurerequest
.y
;
645 xwc
.width
= e
->xconfigurerequest
.width
;
646 xwc
.height
= e
->xconfigurerequest
.height
;
647 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
648 xwc
.sibling
= e
->xconfigurerequest
.above
;
649 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
651 /* we are not to be held responsible if someone sends us an
653 xerror_set_ignore(TRUE
);
654 XConfigureWindow(ob_display
, window
,
655 e
->xconfigurerequest
.value_mask
, &xwc
);
656 xerror_set_ignore(FALSE
);
659 else if (extensions_sync
&&
660 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
662 XSyncAlarmNotifyEvent
*se
= (XSyncAlarmNotifyEvent
*)e
;
663 if (se
->alarm
== moveresize_alarm
&& moveresize_in_progress
)
668 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
669 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
670 e
->type
== KeyRelease
)
672 event_handle_user_input(client
, e
);
675 /* if something happens and it's not from an XEvent, then we don't know
677 event_curtime
= CurrentTime
;
680 static void event_handle_root(XEvent
*e
)
686 ob_debug("Another WM has requested to replace us. Exiting.\n");
691 if (e
->xclient
.format
!= 32) break;
693 msgtype
= e
->xclient
.message_type
;
694 if (msgtype
== prop_atoms
.net_current_desktop
) {
695 guint d
= e
->xclient
.data
.l
[0];
696 if (d
< screen_num_desktops
) {
697 event_curtime
= e
->xclient
.data
.l
[1];
698 if (event_curtime
== 0)
699 ob_debug_type(OB_DEBUG_APP_BUGS
,
700 "_NET_CURRENT_DESKTOP message is missing "
702 screen_set_desktop(d
, TRUE
);
704 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
705 guint d
= e
->xclient
.data
.l
[0];
706 if (d
> 0 && d
<= 1000)
707 screen_set_num_desktops(d
);
708 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
709 screen_show_desktop(e
->xclient
.data
.l
[0] != 0, NULL
);
710 } else if (msgtype
== prop_atoms
.ob_control
) {
711 if (e
->xclient
.data
.l
[0] == 1)
713 else if (e
->xclient
.data
.l
[0] == 2)
718 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
719 screen_update_desktop_names();
720 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
721 screen_update_layout();
723 case ConfigureNotify
:
725 XRRUpdateConfiguration(e
);
734 void event_enter_client(ObClient
*client
)
736 g_assert(config_focus_follow
);
738 if (client_enter_focusable(client
) && client_can_focus(client
)) {
739 if (config_focus_delay
) {
740 ObFocusDelayData
*data
;
742 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
744 data
= g_new(ObFocusDelayData
, 1);
745 data
->client
= client
;
746 data
->time
= event_curtime
;
748 ob_main_loop_timeout_add(ob_main_loop
,
751 data
, focus_delay_cmp
, focus_delay_dest
);
753 ObFocusDelayData data
;
754 data
.client
= client
;
755 data
.time
= event_curtime
;
756 focus_delay_func(&data
);
761 static void event_handle_user_time_window_clients(GSList
*l
, XEvent
*e
)
763 g_assert(e
->type
== PropertyNotify
);
764 if (e
->xproperty
.atom
== prop_atoms
.net_wm_user_time
) {
765 for (; l
; l
= g_slist_next(l
))
766 client_update_user_time(l
->data
);
770 static void event_handle_client(ObClient
*client
, XEvent
*e
)
775 static gint px
= -1, py
= -1;
780 /* save where the press occured for the first button pressed */
782 pb
= e
->xbutton
.button
;
787 /* Wheel buttons don't draw because they are an instant click, so it
788 is a waste of resources to go drawing it.
789 if the user is doing an intereactive thing, or has a menu open then
790 the mouse is grabbed (possibly) and if we get these events we don't
791 want to deal with them
793 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5) &&
794 !keyboard_interactively_grabbed() &&
797 /* use where the press occured */
798 con
= frame_context(client
, e
->xbutton
.window
, px
, py
);
799 con
= mouse_button_frame_context(con
, e
->xbutton
.button
,
802 if (e
->type
== ButtonRelease
&& e
->xbutton
.button
== pb
)
803 pb
= 0, px
= py
= -1;
806 case OB_FRAME_CONTEXT_MAXIMIZE
:
807 client
->frame
->max_press
= (e
->type
== ButtonPress
);
808 framerender_frame(client
->frame
);
810 case OB_FRAME_CONTEXT_CLOSE
:
811 client
->frame
->close_press
= (e
->type
== ButtonPress
);
812 framerender_frame(client
->frame
);
814 case OB_FRAME_CONTEXT_ICONIFY
:
815 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
816 framerender_frame(client
->frame
);
818 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
819 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
820 framerender_frame(client
->frame
);
822 case OB_FRAME_CONTEXT_SHADE
:
823 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
824 framerender_frame(client
->frame
);
827 /* nothing changes with clicks for any other contexts */
833 /* when there is a grab on the pointer, we won't get enter/leave
834 notifies, but we still get motion events */
835 if (grab_on_pointer()) break;
837 con
= frame_context(client
, e
->xmotion
.window
,
838 e
->xmotion
.x
, e
->xmotion
.y
);
840 case OB_FRAME_CONTEXT_TITLEBAR
:
841 case OB_FRAME_CONTEXT_TLCORNER
:
842 case OB_FRAME_CONTEXT_TRCORNER
:
843 /* we've left the button area inside the titlebar */
844 if (client
->frame
->max_hover
|| client
->frame
->desk_hover
||
845 client
->frame
->shade_hover
|| client
->frame
->iconify_hover
||
846 client
->frame
->close_hover
)
848 client
->frame
->max_hover
= FALSE
;
849 client
->frame
->desk_hover
= FALSE
;
850 client
->frame
->shade_hover
= FALSE
;
851 client
->frame
->iconify_hover
= FALSE
;
852 client
->frame
->close_hover
= FALSE
;
853 frame_adjust_state(client
->frame
);
856 case OB_FRAME_CONTEXT_MAXIMIZE
:
857 if (!client
->frame
->max_hover
) {
858 client
->frame
->max_hover
= TRUE
;
859 frame_adjust_state(client
->frame
);
862 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
863 if (!client
->frame
->desk_hover
) {
864 client
->frame
->desk_hover
= TRUE
;
865 frame_adjust_state(client
->frame
);
868 case OB_FRAME_CONTEXT_SHADE
:
869 if (!client
->frame
->shade_hover
) {
870 client
->frame
->shade_hover
= TRUE
;
871 frame_adjust_state(client
->frame
);
874 case OB_FRAME_CONTEXT_ICONIFY
:
875 if (!client
->frame
->iconify_hover
) {
876 client
->frame
->iconify_hover
= TRUE
;
877 frame_adjust_state(client
->frame
);
880 case OB_FRAME_CONTEXT_CLOSE
:
881 if (!client
->frame
->close_hover
) {
882 client
->frame
->close_hover
= TRUE
;
883 frame_adjust_state(client
->frame
);
891 con
= frame_context(client
, e
->xcrossing
.window
,
892 e
->xcrossing
.x
, e
->xcrossing
.y
);
894 case OB_FRAME_CONTEXT_TITLEBAR
:
895 case OB_FRAME_CONTEXT_TLCORNER
:
896 case OB_FRAME_CONTEXT_TRCORNER
:
897 /* we've left the button area inside the titlebar */
898 if (client
->frame
->max_hover
|| client
->frame
->desk_hover
||
899 client
->frame
->shade_hover
|| client
->frame
->iconify_hover
||
900 client
->frame
->close_hover
)
902 client
->frame
->max_hover
= FALSE
;
903 client
->frame
->desk_hover
= FALSE
;
904 client
->frame
->shade_hover
= FALSE
;
905 client
->frame
->iconify_hover
= FALSE
;
906 client
->frame
->close_hover
= FALSE
;
907 frame_adjust_state(client
->frame
);
910 case OB_FRAME_CONTEXT_MAXIMIZE
:
911 client
->frame
->max_hover
= FALSE
;
912 frame_adjust_state(client
->frame
);
914 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
915 client
->frame
->desk_hover
= FALSE
;
916 frame_adjust_state(client
->frame
);
918 case OB_FRAME_CONTEXT_SHADE
:
919 client
->frame
->shade_hover
= FALSE
;
920 frame_adjust_state(client
->frame
);
922 case OB_FRAME_CONTEXT_ICONIFY
:
923 client
->frame
->iconify_hover
= FALSE
;
924 frame_adjust_state(client
->frame
);
926 case OB_FRAME_CONTEXT_CLOSE
:
927 client
->frame
->close_hover
= FALSE
;
928 frame_adjust_state(client
->frame
);
930 case OB_FRAME_CONTEXT_FRAME
:
931 /* When the mouse leaves an animating window, don't use the
932 corresponding enter events. Pretend like the animating window
933 doesn't even exist..! */
934 if (frame_iconify_animating(client
->frame
))
935 event_ignore_all_queued_enters();
937 ob_debug_type(OB_DEBUG_FOCUS
,
938 "%sNotify mode %d detail %d on %lx\n",
939 (e
->type
== EnterNotify
? "Enter" : "Leave"),
941 e
->xcrossing
.detail
, (client
?client
->window
:0));
942 if (keyboard_interactively_grabbed())
944 if (config_focus_follow
&& config_focus_delay
&&
945 /* leave inferior events can happen when the mouse goes onto
946 the window's border and then into the window before the
948 e
->xcrossing
.detail
!= NotifyInferior
)
950 ob_main_loop_timeout_remove_data(ob_main_loop
,
961 con
= frame_context(client
, e
->xcrossing
.window
,
962 e
->xcrossing
.x
, e
->xcrossing
.y
);
964 case OB_FRAME_CONTEXT_MAXIMIZE
:
965 client
->frame
->max_hover
= TRUE
;
966 frame_adjust_state(client
->frame
);
968 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
969 client
->frame
->desk_hover
= TRUE
;
970 frame_adjust_state(client
->frame
);
972 case OB_FRAME_CONTEXT_SHADE
:
973 client
->frame
->shade_hover
= TRUE
;
974 frame_adjust_state(client
->frame
);
976 case OB_FRAME_CONTEXT_ICONIFY
:
977 client
->frame
->iconify_hover
= TRUE
;
978 frame_adjust_state(client
->frame
);
980 case OB_FRAME_CONTEXT_CLOSE
:
981 client
->frame
->close_hover
= TRUE
;
982 frame_adjust_state(client
->frame
);
984 case OB_FRAME_CONTEXT_FRAME
:
985 if (keyboard_interactively_grabbed())
987 if (e
->xcrossing
.mode
== NotifyGrab
||
988 e
->xcrossing
.mode
== NotifyUngrab
||
989 /*ignore enters when we're already in the window */
990 e
->xcrossing
.detail
== NotifyInferior
||
991 is_enter_focus_event_ignored(e
))
993 ob_debug_type(OB_DEBUG_FOCUS
,
994 "%sNotify mode %d detail %d on %lx IGNORED\n",
995 (e
->type
== EnterNotify
? "Enter" : "Leave"),
997 e
->xcrossing
.detail
, client
?client
->window
:0);
1000 ob_debug_type(OB_DEBUG_FOCUS
,
1001 "%sNotify mode %d detail %d on %lx, "
1002 "focusing window\n",
1003 (e
->type
== EnterNotify
? "Enter" : "Leave"),
1005 e
->xcrossing
.detail
, (client
?client
->window
:0));
1006 if (config_focus_follow
)
1007 event_enter_client(client
);
1015 case ConfigureRequest
:
1017 /* dont compress these unless you're going to watch for property
1018 notifies in between (these can change what the configure would
1020 also you can't compress stacking events
1024 gboolean move
= FALSE
;
1025 gboolean resize
= FALSE
;
1027 /* get the current area */
1028 RECT_TO_DIMS(client
->area
, x
, y
, w
, h
);
1030 ob_debug("ConfigureRequest for \"%s\" desktop %d wmstate %d "
1032 " x %d y %d w %d h %d b %d\n",
1034 screen_desktop
, client
->wmstate
, client
->frame
->visible
,
1035 x
, y
, w
, h
, client
->border_width
);
1037 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
1038 if (client
->border_width
!= e
->xconfigurerequest
.border_width
) {
1039 client
->border_width
= e
->xconfigurerequest
.border_width
;
1041 /* if the border width is changing then that is the same
1042 as requesting a resize, but we don't actually change
1043 the client's border, so it will change their root
1044 coordiantes (since they include the border width) and
1045 we need to a notify then */
1050 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
1051 ObClient
*sibling
= NULL
;
1053 /* get the sibling */
1054 if (e
->xconfigurerequest
.value_mask
& CWSibling
) {
1056 win
= g_hash_table_lookup(window_map
,
1057 &e
->xconfigurerequest
.above
);
1058 if (WINDOW_IS_CLIENT(win
) && WINDOW_AS_CLIENT(win
) != client
)
1059 sibling
= WINDOW_AS_CLIENT(win
);
1062 /* activate it rather than just focus it */
1063 stacking_restack_request(client
, sibling
,
1064 e
->xconfigurerequest
.detail
, TRUE
);
1066 /* if a stacking change moves the window without resizing */
1070 if ((e
->xconfigurerequest
.value_mask
& CWX
) ||
1071 (e
->xconfigurerequest
.value_mask
& CWY
) ||
1072 (e
->xconfigurerequest
.value_mask
& CWWidth
) ||
1073 (e
->xconfigurerequest
.value_mask
& CWHeight
))
1075 if (e
->xconfigurerequest
.value_mask
& CWX
) {
1076 /* don't allow clients to move shaded windows (fvwm does this)
1078 if (!client
->shaded
)
1079 x
= e
->xconfigurerequest
.x
;
1082 if (e
->xconfigurerequest
.value_mask
& CWY
) {
1083 /* don't allow clients to move shaded windows (fvwm does this)
1085 if (!client
->shaded
)
1086 y
= e
->xconfigurerequest
.y
;
1090 if (e
->xconfigurerequest
.value_mask
& CWWidth
) {
1091 w
= e
->xconfigurerequest
.width
;
1094 if (e
->xconfigurerequest
.value_mask
& CWHeight
) {
1095 h
= e
->xconfigurerequest
.height
;
1100 ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d "
1101 "move %d resize %d\n",
1102 e
->xconfigurerequest
.value_mask
& CWX
, x
,
1103 e
->xconfigurerequest
.value_mask
& CWY
, y
,
1104 e
->xconfigurerequest
.value_mask
& CWWidth
, w
,
1105 e
->xconfigurerequest
.value_mask
& CWHeight
, h
,
1108 /* check for broken apps moving to their root position
1110 XXX remove this some day...that would be nice. right now all
1111 kde apps do this when they try activate themselves on another
1112 desktop. eg. open amarok window on desktop 1, switch to desktop
1113 2, click amarok tray icon. it will move by its decoration size.
1115 if (x
!= client
->area
.x
&&
1116 x
== (client
->frame
->area
.x
+ client
->frame
->size
.left
-
1117 (gint
)client
->border_width
) &&
1118 y
!= client
->area
.y
&&
1119 y
== (client
->frame
->area
.y
+ client
->frame
->size
.top
-
1120 (gint
)client
->border_width
) &&
1121 w
== client
->area
.width
&&
1122 h
== client
->area
.height
)
1124 ob_debug_type(OB_DEBUG_APP_BUGS
,
1125 "Application %s is trying to move via "
1126 "ConfigureRequest to it's root window position "
1127 "but it is not using StaticGravity\n",
1133 /* they still requested a move, so don't change whether a
1134 notify is sent or not */
1137 if (move
|| resize
) {
1140 client_try_configure(client
, &x
, &y
, &w
, &h
, &lw
, &lh
, FALSE
);
1142 /* if x was not given, then use gravity to figure out the new
1143 x. the reference point should not be moved */
1144 if ((e
->xconfigurerequest
.value_mask
& CWWidth
&&
1145 !(e
->xconfigurerequest
.value_mask
& CWX
)))
1146 client_gravity_resize_w(client
, &x
, client
->area
.width
, w
);
1147 /* if y was not given, then use gravity to figure out the new
1148 y. the reference point should not be moved */
1149 if ((e
->xconfigurerequest
.value_mask
& CWHeight
&&
1150 !(e
->xconfigurerequest
.value_mask
& CWY
)))
1151 client_gravity_resize_h(client
, &y
, client
->area
.height
,h
);
1153 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1155 /* if they requested something that moves the window, or if
1156 the window is actually being changed then configure it and
1157 send a configure notify to them */
1158 if (move
|| !RECT_EQUAL_DIMS(client
->area
, x
, y
, w
, h
)) {
1159 ob_debug("Granting ConfigureRequest x %d y %d w %d h %d\n",
1161 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
);
1164 /* ignore enter events caused by these like ob actions do */
1165 event_ignore_all_queued_enters();
1170 if (client
->ignore_unmaps
) {
1171 client
->ignore_unmaps
--;
1174 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
1175 "ignores left %d\n",
1176 client
->window
, e
->xunmap
.event
, e
->xunmap
.from_configure
,
1177 client
->ignore_unmaps
);
1178 client_unmanage(client
);
1181 ob_debug("DestroyNotify for window 0x%x\n", client
->window
);
1182 client_unmanage(client
);
1184 case ReparentNotify
:
1185 /* this is when the client is first taken captive in the frame */
1186 if (e
->xreparent
.parent
== client
->frame
->window
) break;
1189 This event is quite rare and is usually handled in unmapHandler.
1190 However, if the window is unmapped when the reparent event occurs,
1191 the window manager never sees it because an unmap event is not sent
1192 to an already unmapped window.
1195 /* we don't want the reparent event, put it back on the stack for the
1196 X server to deal with after we unmanage the window */
1197 XPutBackEvent(ob_display
, e
);
1199 ob_debug("ReparentNotify for window 0x%x\n", client
->window
);
1200 client_unmanage(client
);
1203 ob_debug("MapRequest for 0x%lx\n", client
->window
);
1204 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
1205 does, we don't want it!
1206 it can happen now when the window is on
1207 another desktop, but we still don't
1209 client_activate(client
, FALSE
, TRUE
);
1212 /* validate cuz we query stuff off the client here */
1213 if (!client_validate(client
)) break;
1215 if (e
->xclient
.format
!= 32) return;
1217 msgtype
= e
->xclient
.message_type
;
1218 if (msgtype
== prop_atoms
.wm_change_state
) {
1219 /* compress changes into a single change */
1220 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1222 /* XXX: it would be nice to compress ALL messages of a
1223 type, not just messages in a row without other
1224 message types between. */
1225 if (ce
.xclient
.message_type
!= msgtype
) {
1226 XPutBackEvent(ob_display
, &ce
);
1229 e
->xclient
= ce
.xclient
;
1231 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
1232 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
1233 /* compress changes into a single change */
1234 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1236 /* XXX: it would be nice to compress ALL messages of a
1237 type, not just messages in a row without other
1238 message types between. */
1239 if (ce
.xclient
.message_type
!= msgtype
) {
1240 XPutBackEvent(ob_display
, &ce
);
1243 e
->xclient
= ce
.xclient
;
1245 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
1246 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
1247 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
1249 } else if (msgtype
== prop_atoms
.net_wm_state
) {
1250 /* can't compress these */
1251 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1252 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
1253 e
->xclient
.data
.l
[0] == 1 ? "Add" :
1254 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1255 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
1257 client_set_state(client
, e
->xclient
.data
.l
[0],
1258 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
1260 /* ignore enter events caused by these like ob actions do */
1261 event_ignore_all_queued_enters();
1262 } else if (msgtype
== prop_atoms
.net_close_window
) {
1263 ob_debug("net_close_window for 0x%lx\n", client
->window
);
1264 client_close(client
);
1265 } else if (msgtype
== prop_atoms
.net_active_window
) {
1266 ob_debug("net_active_window for 0x%lx source=%s\n",
1268 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
1269 (e
->xclient
.data
.l
[0] == 1 ? "application" :
1270 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
1271 /* XXX make use of data.l[2] !? */
1272 if (e
->xclient
.data
.l
[0] == 1 || e
->xclient
.data
.l
[0] == 2) {
1273 event_curtime
= e
->xclient
.data
.l
[1];
1274 if (event_curtime
== 0)
1275 ob_debug_type(OB_DEBUG_APP_BUGS
,
1276 "_NET_ACTIVE_WINDOW message for window %s is"
1277 " missing a timestamp\n", client
->title
);
1279 ob_debug_type(OB_DEBUG_APP_BUGS
,
1280 "_NET_ACTIVE_WINDOW message for window %s is "
1281 "missing source indication\n");
1282 client_activate(client
, FALSE
,
1283 (e
->xclient
.data
.l
[0] == 0 ||
1284 e
->xclient
.data
.l
[0] == 2));
1285 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
1286 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1287 client
->window
, e
->xclient
.data
.l
[2]);
1288 if ((Atom
)e
->xclient
.data
.l
[2] ==
1289 prop_atoms
.net_wm_moveresize_size_topleft
||
1290 (Atom
)e
->xclient
.data
.l
[2] ==
1291 prop_atoms
.net_wm_moveresize_size_top
||
1292 (Atom
)e
->xclient
.data
.l
[2] ==
1293 prop_atoms
.net_wm_moveresize_size_topright
||
1294 (Atom
)e
->xclient
.data
.l
[2] ==
1295 prop_atoms
.net_wm_moveresize_size_right
||
1296 (Atom
)e
->xclient
.data
.l
[2] ==
1297 prop_atoms
.net_wm_moveresize_size_right
||
1298 (Atom
)e
->xclient
.data
.l
[2] ==
1299 prop_atoms
.net_wm_moveresize_size_bottomright
||
1300 (Atom
)e
->xclient
.data
.l
[2] ==
1301 prop_atoms
.net_wm_moveresize_size_bottom
||
1302 (Atom
)e
->xclient
.data
.l
[2] ==
1303 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1304 (Atom
)e
->xclient
.data
.l
[2] ==
1305 prop_atoms
.net_wm_moveresize_size_left
||
1306 (Atom
)e
->xclient
.data
.l
[2] ==
1307 prop_atoms
.net_wm_moveresize_move
||
1308 (Atom
)e
->xclient
.data
.l
[2] ==
1309 prop_atoms
.net_wm_moveresize_size_keyboard
||
1310 (Atom
)e
->xclient
.data
.l
[2] ==
1311 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1313 moveresize_start(client
, e
->xclient
.data
.l
[0],
1314 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1315 e
->xclient
.data
.l
[2]);
1317 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1318 prop_atoms
.net_wm_moveresize_cancel
)
1319 moveresize_end(TRUE
);
1320 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1321 gint ograv
, x
, y
, w
, h
;
1323 ograv
= client
->gravity
;
1325 if (e
->xclient
.data
.l
[0] & 0xff)
1326 client
->gravity
= e
->xclient
.data
.l
[0] & 0xff;
1328 if (e
->xclient
.data
.l
[0] & 1 << 8)
1329 x
= e
->xclient
.data
.l
[1];
1332 if (e
->xclient
.data
.l
[0] & 1 << 9)
1333 y
= e
->xclient
.data
.l
[2];
1337 if (e
->xclient
.data
.l
[0] & 1 << 10) {
1338 w
= e
->xclient
.data
.l
[3];
1340 /* if x was not given, then use gravity to figure out the new
1341 x. the reference point should not be moved */
1342 if (!(e
->xclient
.data
.l
[0] & 1 << 8))
1343 client_gravity_resize_w(client
, &x
, client
->area
.width
, w
);
1346 w
= client
->area
.width
;
1348 if (e
->xclient
.data
.l
[0] & 1 << 11) {
1349 h
= e
->xclient
.data
.l
[4];
1351 /* if y was not given, then use gravity to figure out the new
1352 y. the reference point should not be moved */
1353 if (!(e
->xclient
.data
.l
[0] & 1 << 9))
1354 client_gravity_resize_h(client
, &y
, client
->area
.height
,h
);
1357 h
= client
->area
.height
;
1359 ob_debug("MOVERESIZE x %d %d y %d %d (gravity %d)\n",
1360 e
->xclient
.data
.l
[0] & 1 << 8, x
,
1361 e
->xclient
.data
.l
[0] & 1 << 9, y
,
1364 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1366 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
);
1368 client
->gravity
= ograv
;
1370 /* ignore enter events caused by these like ob actions do */
1371 event_ignore_all_queued_enters();
1372 } else if (msgtype
== prop_atoms
.net_restack_window
) {
1373 if (e
->xclient
.data
.l
[0] != 2) {
1374 ob_debug_type(OB_DEBUG_APP_BUGS
,
1375 "_NET_RESTACK_WINDOW sent for window %s with "
1376 "invalid source indication %ld\n",
1377 client
->title
, e
->xclient
.data
.l
[0]);
1379 ObClient
*sibling
= NULL
;
1380 if (e
->xclient
.data
.l
[1]) {
1381 ObWindow
*win
= g_hash_table_lookup
1382 (window_map
, &e
->xclient
.data
.l
[1]);
1383 if (WINDOW_IS_CLIENT(win
) &&
1384 WINDOW_AS_CLIENT(win
) != client
)
1386 sibling
= WINDOW_AS_CLIENT(win
);
1388 if (sibling
== NULL
)
1389 ob_debug_type(OB_DEBUG_APP_BUGS
,
1390 "_NET_RESTACK_WINDOW sent for window %s "
1391 "with invalid sibling 0x%x\n",
1392 client
->title
, e
->xclient
.data
.l
[1]);
1394 if (e
->xclient
.data
.l
[2] == Below
||
1395 e
->xclient
.data
.l
[2] == BottomIf
||
1396 e
->xclient
.data
.l
[2] == Above
||
1397 e
->xclient
.data
.l
[2] == TopIf
||
1398 e
->xclient
.data
.l
[2] == Opposite
)
1400 /* just raise, don't activate */
1401 stacking_restack_request(client
, sibling
,
1402 e
->xclient
.data
.l
[2], FALSE
);
1403 /* send a synthetic ConfigureNotify, cuz this is supposed
1404 to be like a ConfigureRequest. */
1405 client_reconfigure(client
);
1407 ob_debug_type(OB_DEBUG_APP_BUGS
,
1408 "_NET_RESTACK_WINDOW sent for window %s "
1409 "with invalid detail %d\n",
1410 client
->title
, e
->xclient
.data
.l
[2]);
1414 case PropertyNotify
:
1415 /* validate cuz we query stuff off the client here */
1416 if (!client_validate(client
)) break;
1418 /* compress changes to a single property into a single change */
1419 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1423 /* XXX: it would be nice to compress ALL changes to a property,
1424 not just changes in a row without other props between. */
1426 a
= ce
.xproperty
.atom
;
1427 b
= e
->xproperty
.atom
;
1431 if ((a
== prop_atoms
.net_wm_name
||
1432 a
== prop_atoms
.wm_name
||
1433 a
== prop_atoms
.net_wm_icon_name
||
1434 a
== prop_atoms
.wm_icon_name
)
1436 (b
== prop_atoms
.net_wm_name
||
1437 b
== prop_atoms
.wm_name
||
1438 b
== prop_atoms
.net_wm_icon_name
||
1439 b
== prop_atoms
.wm_icon_name
)) {
1442 if (a
== prop_atoms
.net_wm_icon
&&
1443 b
== prop_atoms
.net_wm_icon
)
1446 XPutBackEvent(ob_display
, &ce
);
1450 msgtype
= e
->xproperty
.atom
;
1451 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1452 client_update_normal_hints(client
);
1453 /* normal hints can make a window non-resizable */
1454 client_setup_decor_and_functions(client
, TRUE
);
1455 } else if (msgtype
== XA_WM_HINTS
) {
1456 client_update_wmhints(client
);
1457 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1458 client_update_transient_for(client
);
1459 client_get_type_and_transientness(client
);
1460 /* type may have changed, so update the layer */
1461 client_calc_layer(client
);
1462 client_setup_decor_and_functions(client
, TRUE
);
1463 } else if (msgtype
== prop_atoms
.net_wm_name
||
1464 msgtype
== prop_atoms
.wm_name
||
1465 msgtype
== prop_atoms
.net_wm_icon_name
||
1466 msgtype
== prop_atoms
.wm_icon_name
) {
1467 client_update_title(client
);
1468 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1469 client_update_protocols(client
);
1470 client_setup_decor_and_functions(client
, TRUE
);
1472 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1473 client_update_strut(client
);
1475 else if (msgtype
== prop_atoms
.net_wm_strut_partial
) {
1476 client_update_strut(client
);
1478 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1479 client_update_icons(client
);
1481 else if (msgtype
== prop_atoms
.net_wm_icon_geometry
) {
1482 client_update_icon_geometry(client
);
1484 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1485 client_update_user_time(client
);
1487 else if (msgtype
== prop_atoms
.net_wm_user_time_window
) {
1488 client_update_user_time_window(client
);
1491 else if (msgtype
== prop_atoms
.net_wm_sync_request_counter
) {
1492 client_update_sync_request_counter(client
);
1496 case ColormapNotify
:
1497 client_update_colormap(client
, e
->xcolormap
.colormap
);
1502 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1503 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1504 frame_adjust_shape(client
->frame
);
1510 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1514 if (e
->xbutton
.button
== 1)
1515 stacking_raise(DOCK_AS_WINDOW(s
));
1516 else if (e
->xbutton
.button
== 2)
1517 stacking_lower(DOCK_AS_WINDOW(s
));
1523 /* don't hide when moving into a dock app */
1524 if (e
->xcrossing
.detail
!= NotifyInferior
)
1530 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1534 dock_app_drag(app
, &e
->xmotion
);
1537 if (app
->ignore_unmaps
) {
1538 app
->ignore_unmaps
--;
1541 dock_remove(app
, TRUE
);
1544 dock_remove(app
, FALSE
);
1546 case ReparentNotify
:
1547 dock_remove(app
, FALSE
);
1549 case ConfigureNotify
:
1550 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1555 static ObMenuFrame
* find_active_menu()
1558 ObMenuFrame
*ret
= NULL
;
1560 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1569 static ObMenuFrame
* find_active_or_last_menu()
1571 ObMenuFrame
*ret
= NULL
;
1573 ret
= find_active_menu();
1574 if (!ret
&& menu_frame_visible
)
1575 ret
= menu_frame_visible
->data
;
1579 static gboolean
event_handle_menu_keyboard(XEvent
*ev
)
1581 guint keycode
, state
;
1584 gboolean ret
= TRUE
;
1586 keycode
= ev
->xkey
.keycode
;
1587 state
= ev
->xkey
.state
;
1588 unikey
= translate_unichar(keycode
);
1590 frame
= find_active_or_last_menu();
1594 else if (keycode
== ob_keycode(OB_KEY_ESCAPE
) && state
== 0)
1595 menu_frame_hide_all();
1597 else if (keycode
== ob_keycode(OB_KEY_RETURN
) && (state
== 0 ||
1598 state
== ControlMask
))
1600 /* Enter runs the active item or goes into the submenu.
1601 Control-Enter runs it without closing the menu. */
1603 menu_frame_select_next(frame
->child
);
1604 else if (frame
->selected
)
1605 menu_entry_frame_execute(frame
->selected
, state
, ev
->xkey
.time
);
1608 else if (keycode
== ob_keycode(OB_KEY_LEFT
) && ev
->xkey
.state
== 0) {
1609 /* Left goes to the parent menu */
1610 menu_frame_select(frame
, NULL
, TRUE
);
1613 else if (keycode
== ob_keycode(OB_KEY_RIGHT
) && ev
->xkey
.state
== 0) {
1614 /* Right goes to the selected submenu */
1615 if (frame
->child
) menu_frame_select_next(frame
->child
);
1618 else if (keycode
== ob_keycode(OB_KEY_UP
) && state
== 0) {
1619 menu_frame_select_previous(frame
);
1622 else if (keycode
== ob_keycode(OB_KEY_DOWN
) && state
== 0) {
1623 menu_frame_select_next(frame
);
1626 /* keyboard accelerator shortcuts. (allow controlmask) */
1627 else if ((ev
->xkey
.state
& ~ControlMask
) == 0 &&
1628 /* was it a valid key? */
1630 /* don't bother if the menu is empty. */
1635 ObMenuEntryFrame
*found
= NULL
;
1636 guint num_found
= 0;
1638 /* start after the selected one */
1639 start
= frame
->entries
;
1640 if (frame
->selected
) {
1641 for (it
= start
; frame
->selected
!= it
->data
; it
= g_list_next(it
))
1642 g_assert(it
!= NULL
); /* nothing was selected? */
1643 /* next with wraparound */
1644 start
= g_list_next(it
);
1645 if (start
== NULL
) start
= frame
->entries
;
1650 ObMenuEntryFrame
*e
= it
->data
;
1651 gunichar entrykey
= 0;
1653 if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
)
1654 entrykey
= e
->entry
->data
.normal
.shortcut
;
1655 else if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_SUBMENU
)
1656 entrykey
= e
->entry
->data
.submenu
.submenu
->shortcut
;
1658 if (unikey
== entrykey
) {
1659 if (found
== NULL
) found
= e
;
1663 /* next with wraparound */
1664 it
= g_list_next(it
);
1665 if (it
== NULL
) it
= frame
->entries
;
1666 } while (it
!= start
);
1669 if (found
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
&&
1672 menu_frame_select(frame
, found
, TRUE
);
1673 usleep(50000); /* highlight the item for a short bit so the
1674 user can see what happened */
1675 menu_entry_frame_execute(found
, state
, ev
->xkey
.time
);
1677 menu_frame_select(frame
, found
, TRUE
);
1679 menu_frame_select_next(frame
->child
);
1690 static gboolean
event_handle_menu(XEvent
*ev
)
1693 ObMenuEntryFrame
*e
;
1694 gboolean ret
= TRUE
;
1698 if ((ev
->xbutton
.button
< 4 || ev
->xbutton
.button
> 5)
1701 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1702 ev
->xbutton
.y_root
)))
1704 menu_frame_select(e
->frame
, e
, TRUE
);
1705 menu_entry_frame_execute(e
, ev
->xbutton
.state
,
1709 menu_frame_hide_all();
1713 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
))) {
1714 if (e
->ignore_enters
)
1716 else if (!(f
= find_active_menu()) ||
1718 f
->parent
== e
->frame
||
1719 f
->child
== e
->frame
)
1720 menu_frame_select(e
->frame
, e
, FALSE
);
1724 /*ignore leaves when we're already in the window */
1725 if (ev
->xcrossing
.detail
== NotifyInferior
)
1728 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
)) &&
1729 (f
= find_active_menu()) && f
->selected
== e
&&
1730 e
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1732 menu_frame_select(e
->frame
, NULL
, FALSE
);
1736 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1737 ev
->xmotion
.y_root
)))
1738 if (!(f
= find_active_menu()) ||
1740 f
->parent
== e
->frame
||
1741 f
->child
== e
->frame
)
1742 menu_frame_select(e
->frame
, e
, FALSE
);
1745 ret
= event_handle_menu_keyboard(ev
);
1751 static void event_handle_user_input(ObClient
*client
, XEvent
*e
)
1753 g_assert(e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
1754 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
1755 e
->type
== KeyRelease
);
1757 if (menu_frame_visible
) {
1758 if (event_handle_menu(e
))
1759 /* don't use the event if the menu used it, but if the menu
1760 didn't use it and it's a keypress that is bound, it will
1761 close the menu and be used */
1765 /* if the keyboard interactive action uses the event then dont
1766 use it for bindings. likewise is moveresize uses the event. */
1767 if (!keyboard_process_interactive_grab(e
, &client
) &&
1768 !(moveresize_in_progress
&& moveresize_event(e
)))
1770 if (moveresize_in_progress
)
1771 /* make further actions work on the client being
1773 client
= moveresize_client
;
1775 menu_can_hide
= FALSE
;
1776 ob_main_loop_timeout_add(ob_main_loop
,
1777 config_menu_hide_delay
* 1000,
1778 menu_hide_delay_func
,
1779 NULL
, g_direct_equal
, NULL
);
1781 if (e
->type
== ButtonPress
||
1782 e
->type
== ButtonRelease
||
1783 e
->type
== MotionNotify
)
1785 /* the frame may not be "visible" but they can still click on it
1786 in the case where it is animating before disappearing */
1787 if (!client
|| !frame_iconify_animating(client
->frame
))
1788 mouse_event(client
, e
);
1789 } else if (e
->type
== KeyPress
) {
1790 keyboard_event((focus_cycle_target
? focus_cycle_target
:
1791 (client
? client
: focus_client
)), e
);
1796 static gboolean
menu_hide_delay_func(gpointer data
)
1798 menu_can_hide
= TRUE
;
1799 return FALSE
; /* no repeat */
1802 static void focus_delay_dest(gpointer data
)
1807 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
)
1809 const ObFocusDelayData
*f1
= d1
;
1810 return f1
->client
== d2
;
1813 static gboolean
focus_delay_func(gpointer data
)
1815 ObFocusDelayData
*d
= data
;
1816 Time old
= event_curtime
;
1818 event_curtime
= d
->time
;
1819 if (focus_client
!= d
->client
) {
1820 if (client_focus(d
->client
) && config_focus_raise
)
1821 stacking_raise(CLIENT_AS_WINDOW(d
->client
));
1823 event_curtime
= old
;
1824 return FALSE
; /* no repeat */
1827 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1829 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1833 void event_halt_focus_delay()
1835 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1838 static Bool
event_look_for_enters(Display
*d
, XEvent
*e
, XPointer arg
)
1840 if (e
->type
== EnterNotify
&&
1841 /* these types aren't used for focusing */
1842 !(e
->xcrossing
.mode
== NotifyGrab
||
1843 e
->xcrossing
.mode
== NotifyUngrab
||
1844 e
->xcrossing
.detail
== NotifyInferior
))
1848 /* found an enter for that leave, ignore it if it's going to
1850 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1851 if (win
&& WINDOW_IS_CLIENT(win
))
1852 ++ignore_enter_focus
;
1854 return False
; /* don't disrupt the queue order, just count them */
1857 void event_ignore_all_queued_enters()
1861 XSync(ob_display
, FALSE
);
1863 /* count the events without disrupting them */
1864 ignore_enter_focus
= 0;
1865 XCheckIfEvent(ob_display
, &e
, event_look_for_enters
, NULL
);
1868 static gboolean
is_enter_focus_event_ignored(XEvent
*e
)
1870 g_assert(e
->type
== EnterNotify
&&
1871 !(e
->xcrossing
.mode
== NotifyGrab
||
1872 e
->xcrossing
.mode
== NotifyUngrab
||
1873 e
->xcrossing
.detail
== NotifyInferior
));
1875 ob_debug_type(OB_DEBUG_FOCUS
, "# enters ignored: %d\n",
1876 ignore_enter_focus
);
1878 if (ignore_enter_focus
) {
1879 --ignore_enter_focus
;
1885 void event_cancel_all_key_grabs()
1887 if (keyboard_interactively_grabbed())
1888 keyboard_interactive_cancel();
1889 else if (menu_frame_visible
)
1890 menu_frame_hide_all();
1891 else if (grab_on_keyboard())
1894 /* If we don't have the keyboard grabbed, then ungrab it with
1895 XUngrabKeyboard, so that there is not a passive grab left
1896 on from the KeyPress. If the grab is left on, and focus
1897 moves during that time, it will be NotifyWhileGrabbed, and
1898 applications like to ignore those! */
1899 if (!keyboard_interactively_grabbed())
1900 XUngrabKeyboard(ob_display
, CurrentTime
);
1904 gboolean
event_time_after(Time t1
, Time t2
)
1906 g_assert(t1
!= CurrentTime
);
1907 g_assert(t2
!= CurrentTime
);
1910 Timestamp values wrap around (after about 49.7 days). The server, given
1911 its current time is represented by timestamp T, always interprets
1912 timestamps from clients by treating half of the timestamp space as being
1913 later in time than T.
1914 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1917 /* TIME_HALF is half of the number space of a Time type variable */
1918 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1920 if (t2
>= TIME_HALF
)
1921 /* t2 is in the second half so t1 might wrap around and be smaller than
1923 return t1
>= t2
|| t1
< (t2
+ TIME_HALF
);
1925 /* t2 is in the first half so t1 has to come after it */
1926 return t1
>= t2
&& t1
< (t2
+ TIME_HALF
);