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_root
= ce
.xmotion
.x_root
;
274 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
281 static gboolean
wanted_focusevent(XEvent
*e
, gboolean in_client_only
)
283 gint mode
= e
->xfocus
.mode
;
284 gint detail
= e
->xfocus
.detail
;
285 Window win
= e
->xany
.window
;
287 if (e
->type
== FocusIn
) {
288 /* These are ones we never want.. */
290 /* This means focus was given by a keyboard/mouse grab. */
291 if (mode
== NotifyGrab
)
293 /* This means focus was given back from a keyboard/mouse grab. */
294 if (mode
== NotifyUngrab
)
297 /* These are the ones we want.. */
299 if (win
== RootWindow(ob_display
, ob_screen
)) {
300 /* If looking for a focus in on a client, then always return
301 FALSE for focus in's to the root window */
304 /* This means focus reverted off of a client */
305 else if (detail
== NotifyPointerRoot
||
306 detail
== NotifyDetailNone
||
307 detail
== NotifyInferior
)
313 /* It was on a client, was it a valid one?
314 It's possible to get a FocusIn event for a client that was managed
317 if (in_client_only
) {
318 ObWindow
*w
= g_hash_table_lookup(window_map
, &e
->xfocus
.window
);
319 if (!w
|| !WINDOW_IS_CLIENT(w
))
323 /* This means focus reverted to parent from the client (this
324 happens often during iconify animation) */
325 if (detail
== NotifyInferior
)
329 /* This means focus moved from the root window to a client */
330 if (detail
== NotifyVirtual
)
332 /* This means focus moved from one client to another */
333 if (detail
== NotifyNonlinearVirtual
)
339 g_assert(e
->type
== FocusOut
);
341 /* These are ones we never want.. */
343 /* This means focus was taken by a keyboard/mouse grab. */
344 if (mode
== NotifyGrab
)
346 /* This means focus was grabbed on a window and it was released. */
347 if (mode
== NotifyUngrab
)
350 /* Focus left the root window revertedto state */
351 if (win
== RootWindow(ob_display
, ob_screen
))
354 /* These are the ones we want.. */
356 /* This means focus moved from a client to the root window */
357 if (detail
== NotifyVirtual
)
359 /* This means focus moved from one client to another */
360 if (detail
== NotifyNonlinearVirtual
)
368 static Bool
event_look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
370 return e
->type
== FocusIn
&& wanted_focusevent(e
, FALSE
);
373 static Bool
event_look_for_focusin_client(Display
*d
, XEvent
*e
, XPointer arg
)
375 return e
->type
== FocusIn
&& wanted_focusevent(e
, TRUE
);
378 static void print_focusevent(XEvent
*e
)
380 gint mode
= e
->xfocus
.mode
;
381 gint detail
= e
->xfocus
.detail
;
382 Window win
= e
->xany
.window
;
383 const gchar
*modestr
, *detailstr
;
386 case NotifyNormal
: modestr
="NotifyNormal"; break;
387 case NotifyGrab
: modestr
="NotifyGrab"; break;
388 case NotifyUngrab
: modestr
="NotifyUngrab"; break;
389 case NotifyWhileGrabbed
: modestr
="NotifyWhileGrabbed"; break;
392 case NotifyAncestor
: detailstr
="NotifyAncestor"; break;
393 case NotifyVirtual
: detailstr
="NotifyVirtual"; break;
394 case NotifyInferior
: detailstr
="NotifyInferior"; break;
395 case NotifyNonlinear
: detailstr
="NotifyNonlinear"; break;
396 case NotifyNonlinearVirtual
: detailstr
="NotifyNonlinearVirtual"; break;
397 case NotifyPointer
: detailstr
="NotifyPointer"; break;
398 case NotifyPointerRoot
: detailstr
="NotifyPointerRoot"; break;
399 case NotifyDetailNone
: detailstr
="NotifyDetailNone"; break;
402 if (mode
== NotifyGrab
|| mode
== NotifyUngrab
)
407 ob_debug_type(OB_DEBUG_FOCUS
, "Focus%s 0x%x mode=%s detail=%s\n",
408 (e
->xfocus
.type
== FocusIn
? "In" : "Out"),
414 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
419 if (!wanted_focusevent(e
, FALSE
))
424 if (!wanted_focusevent(e
, FALSE
))
431 static void event_process(const XEvent
*ec
, gpointer data
)
434 ObClient
*client
= NULL
;
436 ObDockApp
*dockapp
= NULL
;
437 ObWindow
*obwin
= NULL
;
438 GSList
*timewinclients
= NULL
;
440 ObEventData
*ed
= data
;
442 /* make a copy we can mangle */
446 window
= event_get_window(e
);
447 if (e
->type
!= PropertyNotify
||
448 !(timewinclients
= propwin_get_clients(window
,
449 OB_PROPWIN_USER_TIME
)))
450 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
451 switch (obwin
->type
) {
453 dock
= WINDOW_AS_DOCK(obwin
);
456 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
459 client
= WINDOW_AS_CLIENT(obwin
);
462 case Window_Internal
:
463 /* not to be used for events */
464 g_assert_not_reached();
469 event_set_curtime(e
);
471 if (event_ignore(e
, client
)) {
478 /* deal with it in the kernel */
480 if (menu_frame_visible
&&
481 (e
->type
== EnterNotify
|| e
->type
== LeaveNotify
))
483 /* crossing events for menu */
484 event_handle_menu(e
);
485 } else if (e
->type
== FocusIn
) {
486 if (e
->xfocus
.detail
== NotifyPointerRoot
||
487 e
->xfocus
.detail
== NotifyDetailNone
||
488 e
->xfocus
.detail
== NotifyInferior
)
492 ob_debug_type(OB_DEBUG_FOCUS
, "Focus went to pointer root/none or"
493 " the frame window\n");
495 /* If another FocusIn is in the queue then don't fallback yet. This
496 fixes the fun case of:
497 window map -> send focusin
498 window unmap -> get focusout
499 window map -> send focusin
500 get first focus out -> fall back to something (new window
501 hasn't received focus yet, so something else) -> send focusin
502 which means the "something else" is the last thing to get a
503 focusin sent to it, so the new window doesn't end up with focus.
505 But if the other focus in is something like PointerRoot then we
506 still want to fall back.
508 if (XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin_client
,
511 XPutBackEvent(ob_display
, &ce
);
512 ob_debug_type(OB_DEBUG_FOCUS
,
513 " but another FocusIn is coming\n");
515 /* Focus has been reverted.
517 FocusOut events come after UnmapNotify, so we don't need to
518 worry about focusing an invalid window
521 if (!focus_left_screen
)
522 focus_fallback(TRUE
, FALSE
);
527 ob_debug_type(OB_DEBUG_FOCUS
,
528 "Focus went to a window that is already gone\n");
530 /* If you send focus to a window and then it disappears, you can
531 get the FocusIn for it, after it is unmanaged.
532 Just wait for the next FocusOut/FocusIn pair. */
534 else if (client
!= focus_client
) {
535 focus_left_screen
= FALSE
;
536 frame_adjust_focus(client
->frame
, TRUE
);
537 focus_set_client(client
);
538 client_calc_layer(client
);
539 client_bring_helper_windows(client
);
541 } else if (e
->type
== FocusOut
) {
544 /* Look for the followup FocusIn */
545 if (!XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin
, NULL
)) {
546 /* There is no FocusIn, this means focus went to a window that
547 is not being managed, or a window on another screen. */
551 xerror_set_ignore(TRUE
);
552 if (XGetInputFocus(ob_display
, &win
, &i
) != 0 &&
553 XGetGeometry(ob_display
, win
, &root
, &i
,&i
,&u
,&u
,&u
,&u
) != 0 &&
554 root
!= RootWindow(ob_display
, ob_screen
))
556 ob_debug_type(OB_DEBUG_FOCUS
,
557 "Focus went to another screen !\n");
558 focus_left_screen
= TRUE
;
561 ob_debug_type(OB_DEBUG_FOCUS
,
562 "Focus went to a black hole !\n");
563 xerror_set_ignore(FALSE
);
564 /* nothing is focused */
565 focus_set_client(NULL
);
567 /* Focus moved, so process the FocusIn event */
568 ObEventData ed
= { .ignored
= FALSE
};
569 event_process(&ce
, &ed
);
571 /* The FocusIn was ignored, this means it was on a window
572 that isn't a client. */
573 ob_debug_type(OB_DEBUG_FOCUS
,
574 "Focus went to an unmanaged window 0x%x !\n",
576 focus_fallback(TRUE
, FALSE
);
580 if (client
&& client
!= focus_client
) {
581 frame_adjust_focus(client
->frame
, FALSE
);
582 /* focus_set_client(NULL) has already been called in this
583 section or by focus_fallback */
584 client_calc_layer(client
);
586 } else if (timewinclients
)
587 event_handle_user_time_window_clients(timewinclients
, e
);
589 event_handle_client(client
, e
);
591 event_handle_dockapp(dockapp
, e
);
593 event_handle_dock(dock
, e
);
594 else if (window
== RootWindow(ob_display
, ob_screen
))
595 event_handle_root(e
);
596 else if (e
->type
== MapRequest
)
597 client_manage(window
);
598 else if (e
->type
== ClientMessage
) {
599 /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
600 windows that are not managed yet. */
601 if (e
->xclient
.message_type
== prop_atoms
.net_request_frame_extents
) {
602 /* Pretend to manage the client, getting information used to
603 determine its decorations */
604 ObClient
*c
= client_fake_manage(e
->xclient
.window
);
607 /* set the frame extents on the window */
608 vals
[0] = c
->frame
->size
.left
;
609 vals
[1] = c
->frame
->size
.right
;
610 vals
[2] = c
->frame
->size
.top
;
611 vals
[3] = c
->frame
->size
.bottom
;
612 PROP_SETA32(e
->xclient
.window
, net_frame_extents
,
615 /* Free the pretend client */
616 client_fake_unmanage(c
);
619 else if (e
->type
== ConfigureRequest
) {
620 /* unhandled configure requests must be used to configure the
624 xwc
.x
= e
->xconfigurerequest
.x
;
625 xwc
.y
= e
->xconfigurerequest
.y
;
626 xwc
.width
= e
->xconfigurerequest
.width
;
627 xwc
.height
= e
->xconfigurerequest
.height
;
628 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
629 xwc
.sibling
= e
->xconfigurerequest
.above
;
630 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
632 /* we are not to be held responsible if someone sends us an
634 xerror_set_ignore(TRUE
);
635 XConfigureWindow(ob_display
, window
,
636 e
->xconfigurerequest
.value_mask
, &xwc
);
637 xerror_set_ignore(FALSE
);
640 else if (extensions_sync
&&
641 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
643 XSyncAlarmNotifyEvent
*se
= (XSyncAlarmNotifyEvent
*)e
;
644 if (se
->alarm
== moveresize_alarm
&& moveresize_in_progress
)
649 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
650 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
651 e
->type
== KeyRelease
)
653 event_handle_user_input(client
, e
);
656 /* if something happens and it's not from an XEvent, then we don't know
658 event_curtime
= CurrentTime
;
661 static void event_handle_root(XEvent
*e
)
667 ob_debug("Another WM has requested to replace us. Exiting.\n");
672 if (e
->xclient
.format
!= 32) break;
674 msgtype
= e
->xclient
.message_type
;
675 if (msgtype
== prop_atoms
.net_current_desktop
) {
676 guint d
= e
->xclient
.data
.l
[0];
677 if (d
< screen_num_desktops
) {
678 event_curtime
= e
->xclient
.data
.l
[1];
679 if (event_curtime
== 0)
680 ob_debug_type(OB_DEBUG_APP_BUGS
,
681 "_NET_CURRENT_DESKTOP message is missing "
683 screen_set_desktop(d
, TRUE
);
685 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
686 guint d
= e
->xclient
.data
.l
[0];
687 if (d
> 0 && d
<= 1000)
688 screen_set_num_desktops(d
);
689 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
690 screen_show_desktop(e
->xclient
.data
.l
[0] != 0, NULL
);
691 } else if (msgtype
== prop_atoms
.ob_control
) {
692 if (e
->xclient
.data
.l
[0] == 1)
694 else if (e
->xclient
.data
.l
[0] == 2)
699 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
700 screen_update_desktop_names();
701 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
702 screen_update_layout();
704 case ConfigureNotify
:
706 XRRUpdateConfiguration(e
);
715 void event_enter_client(ObClient
*client
)
717 g_assert(config_focus_follow
);
719 if (client_enter_focusable(client
) && client_can_focus(client
)) {
720 if (config_focus_delay
) {
721 ObFocusDelayData
*data
;
723 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
725 data
= g_new(ObFocusDelayData
, 1);
726 data
->client
= client
;
727 data
->time
= event_curtime
;
729 ob_main_loop_timeout_add(ob_main_loop
,
732 data
, focus_delay_cmp
, focus_delay_dest
);
734 ObFocusDelayData data
;
735 data
.client
= client
;
736 data
.time
= event_curtime
;
737 focus_delay_func(&data
);
742 static void event_handle_user_time_window_clients(GSList
*l
, XEvent
*e
)
744 g_assert(e
->type
== PropertyNotify
);
745 if (e
->xproperty
.atom
== prop_atoms
.net_wm_user_time
) {
746 for (; l
; l
= g_slist_next(l
))
747 client_update_user_time(l
->data
);
751 static void event_handle_client(ObClient
*client
, XEvent
*e
)
756 static gint px
= -1, py
= -1;
761 /* save where the press occured for the first button pressed */
763 pb
= e
->xbutton
.button
;
768 /* Wheel buttons don't draw because they are an instant click, so it
769 is a waste of resources to go drawing it.
770 if the user is doing an intereactive thing, or has a menu open then
771 the mouse is grabbed (possibly) and if we get these events we don't
772 want to deal with them
774 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5) &&
775 !keyboard_interactively_grabbed() &&
778 /* use where the press occured */
779 con
= frame_context(client
, e
->xbutton
.window
, px
, py
);
780 con
= mouse_button_frame_context(con
, e
->xbutton
.button
,
783 if (e
->type
== ButtonRelease
&& e
->xbutton
.button
== pb
)
784 pb
= 0, px
= py
= -1;
787 case OB_FRAME_CONTEXT_MAXIMIZE
:
788 client
->frame
->max_press
= (e
->type
== ButtonPress
);
789 framerender_frame(client
->frame
);
791 case OB_FRAME_CONTEXT_CLOSE
:
792 client
->frame
->close_press
= (e
->type
== ButtonPress
);
793 framerender_frame(client
->frame
);
795 case OB_FRAME_CONTEXT_ICONIFY
:
796 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
797 framerender_frame(client
->frame
);
799 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
800 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
801 framerender_frame(client
->frame
);
803 case OB_FRAME_CONTEXT_SHADE
:
804 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
805 framerender_frame(client
->frame
);
808 /* nothing changes with clicks for any other contexts */
814 con
= frame_context(client
, e
->xmotion
.window
,
815 e
->xmotion
.x
, e
->xmotion
.y
);
817 case OB_FRAME_CONTEXT_TITLEBAR
:
818 case OB_FRAME_CONTEXT_TLCORNER
:
819 case OB_FRAME_CONTEXT_TRCORNER
:
820 /* we've left the button area inside the titlebar */
821 if (client
->frame
->max_hover
|| client
->frame
->desk_hover
||
822 client
->frame
->shade_hover
|| client
->frame
->iconify_hover
||
823 client
->frame
->close_hover
)
825 client
->frame
->max_hover
= FALSE
;
826 client
->frame
->desk_hover
= FALSE
;
827 client
->frame
->shade_hover
= FALSE
;
828 client
->frame
->iconify_hover
= FALSE
;
829 client
->frame
->close_hover
= FALSE
;
830 frame_adjust_state(client
->frame
);
833 case OB_FRAME_CONTEXT_MAXIMIZE
:
834 if (!client
->frame
->max_hover
) {
835 client
->frame
->max_hover
= TRUE
;
836 frame_adjust_state(client
->frame
);
839 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
840 if (!client
->frame
->desk_hover
) {
841 client
->frame
->desk_hover
= TRUE
;
842 frame_adjust_state(client
->frame
);
845 case OB_FRAME_CONTEXT_SHADE
:
846 if (!client
->frame
->shade_hover
) {
847 client
->frame
->shade_hover
= TRUE
;
848 frame_adjust_state(client
->frame
);
851 case OB_FRAME_CONTEXT_ICONIFY
:
852 if (!client
->frame
->iconify_hover
) {
853 client
->frame
->iconify_hover
= TRUE
;
854 frame_adjust_state(client
->frame
);
857 case OB_FRAME_CONTEXT_CLOSE
:
858 if (!client
->frame
->close_hover
) {
859 client
->frame
->close_hover
= TRUE
;
860 frame_adjust_state(client
->frame
);
868 con
= frame_context(client
, e
->xcrossing
.window
,
869 e
->xcrossing
.x
, e
->xcrossing
.y
);
871 case OB_FRAME_CONTEXT_TITLEBAR
:
872 case OB_FRAME_CONTEXT_TLCORNER
:
873 case OB_FRAME_CONTEXT_TRCORNER
:
874 /* we've left the button area inside the titlebar */
875 if (client
->frame
->max_hover
|| client
->frame
->desk_hover
||
876 client
->frame
->shade_hover
|| client
->frame
->iconify_hover
||
877 client
->frame
->close_hover
)
879 client
->frame
->max_hover
= FALSE
;
880 client
->frame
->desk_hover
= FALSE
;
881 client
->frame
->shade_hover
= FALSE
;
882 client
->frame
->iconify_hover
= FALSE
;
883 client
->frame
->close_hover
= FALSE
;
884 frame_adjust_state(client
->frame
);
887 case OB_FRAME_CONTEXT_MAXIMIZE
:
888 client
->frame
->max_hover
= FALSE
;
889 frame_adjust_state(client
->frame
);
891 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
892 client
->frame
->desk_hover
= FALSE
;
893 frame_adjust_state(client
->frame
);
895 case OB_FRAME_CONTEXT_SHADE
:
896 client
->frame
->shade_hover
= FALSE
;
897 frame_adjust_state(client
->frame
);
899 case OB_FRAME_CONTEXT_ICONIFY
:
900 client
->frame
->iconify_hover
= FALSE
;
901 frame_adjust_state(client
->frame
);
903 case OB_FRAME_CONTEXT_CLOSE
:
904 client
->frame
->close_hover
= FALSE
;
905 frame_adjust_state(client
->frame
);
907 case OB_FRAME_CONTEXT_FRAME
:
908 /* When the mouse leaves an animating window, don't use the
909 corresponding enter events. Pretend like the animating window
910 doesn't even exist..! */
911 if (frame_iconify_animating(client
->frame
))
912 event_ignore_all_queued_enters();
914 ob_debug_type(OB_DEBUG_FOCUS
,
915 "%sNotify mode %d detail %d on %lx\n",
916 (e
->type
== EnterNotify
? "Enter" : "Leave"),
918 e
->xcrossing
.detail
, (client
?client
->window
:0));
919 if (keyboard_interactively_grabbed())
921 if (config_focus_follow
&& config_focus_delay
&&
922 /* leave inferior events can happen when the mouse goes onto
923 the window's border and then into the window before the
925 e
->xcrossing
.detail
!= NotifyInferior
)
927 ob_main_loop_timeout_remove_data(ob_main_loop
,
938 con
= frame_context(client
, e
->xcrossing
.window
,
939 e
->xcrossing
.x
, e
->xcrossing
.y
);
941 case OB_FRAME_CONTEXT_MAXIMIZE
:
942 client
->frame
->max_hover
= TRUE
;
943 frame_adjust_state(client
->frame
);
945 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
946 client
->frame
->desk_hover
= TRUE
;
947 frame_adjust_state(client
->frame
);
949 case OB_FRAME_CONTEXT_SHADE
:
950 client
->frame
->shade_hover
= TRUE
;
951 frame_adjust_state(client
->frame
);
953 case OB_FRAME_CONTEXT_ICONIFY
:
954 client
->frame
->iconify_hover
= TRUE
;
955 frame_adjust_state(client
->frame
);
957 case OB_FRAME_CONTEXT_CLOSE
:
958 client
->frame
->close_hover
= TRUE
;
959 frame_adjust_state(client
->frame
);
961 case OB_FRAME_CONTEXT_FRAME
:
962 if (keyboard_interactively_grabbed())
964 if (e
->xcrossing
.mode
== NotifyGrab
||
965 e
->xcrossing
.mode
== NotifyUngrab
||
966 /*ignore enters when we're already in the window */
967 e
->xcrossing
.detail
== NotifyInferior
||
968 is_enter_focus_event_ignored(e
))
970 ob_debug_type(OB_DEBUG_FOCUS
,
971 "%sNotify mode %d detail %d on %lx IGNORED\n",
972 (e
->type
== EnterNotify
? "Enter" : "Leave"),
974 e
->xcrossing
.detail
, client
?client
->window
:0);
977 ob_debug_type(OB_DEBUG_FOCUS
,
978 "%sNotify mode %d detail %d on %lx, "
980 (e
->type
== EnterNotify
? "Enter" : "Leave"),
982 e
->xcrossing
.detail
, (client
?client
->window
:0));
983 if (config_focus_follow
)
984 event_enter_client(client
);
992 case ConfigureRequest
:
994 /* dont compress these unless you're going to watch for property
995 notifies in between (these can change what the configure would
997 also you can't compress stacking events
1001 gboolean move
= FALSE
;
1002 gboolean resize
= FALSE
;
1003 gboolean border
= FALSE
;
1005 /* get the current area */
1006 RECT_TO_DIMS(client
->area
, x
, y
, w
, h
);
1007 b
= client
->border_width
;
1009 ob_debug("ConfigureRequest desktop %d wmstate %d visibile %d\n",
1010 screen_desktop
, client
->wmstate
, client
->frame
->visible
);
1012 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
1013 if (client
->border_width
!= e
->xconfigurerequest
.border_width
) {
1014 b
= e
->xconfigurerequest
.border_width
;
1019 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
1020 ObClient
*sibling
= NULL
;
1022 /* get the sibling */
1023 if (e
->xconfigurerequest
.value_mask
& CWSibling
) {
1025 win
= g_hash_table_lookup(window_map
,
1026 &e
->xconfigurerequest
.above
);
1027 if (WINDOW_IS_CLIENT(win
) && WINDOW_AS_CLIENT(win
) != client
)
1028 sibling
= WINDOW_AS_CLIENT(win
);
1031 /* activate it rather than just focus it */
1032 stacking_restack_request(client
, sibling
,
1033 e
->xconfigurerequest
.detail
, TRUE
);
1035 /* if a stacking change moves the window without resizing */
1039 if (e
->xconfigurerequest
.value_mask
& CWX
||
1040 e
->xconfigurerequest
.value_mask
& CWY
||
1041 e
->xconfigurerequest
.value_mask
& CWWidth
||
1042 e
->xconfigurerequest
.value_mask
& CWHeight
)
1044 if (e
->xconfigurerequest
.value_mask
& CWX
) {
1045 /* don't allow clients to move shaded windows (fvwm does this)
1047 if (!client
->shaded
)
1048 x
= e
->xconfigurerequest
.x
;
1051 if (e
->xconfigurerequest
.value_mask
& CWY
) {
1052 /* don't allow clients to move shaded windows (fvwm does this)
1054 if (!client
->shaded
)
1055 y
= e
->xconfigurerequest
.y
;
1059 if (e
->xconfigurerequest
.value_mask
& CWWidth
) {
1060 w
= e
->xconfigurerequest
.width
;
1063 /* if x was not given, then use gravity to figure out the new
1064 x. the reference point should not be moved */
1065 if (!(e
->xconfigurerequest
.value_mask
& CWX
))
1066 client_gravity_resize_w(client
, &x
, client
->area
.width
, w
);
1068 if (e
->xconfigurerequest
.value_mask
& CWHeight
) {
1069 h
= e
->xconfigurerequest
.height
;
1072 /* if y was not given, then use gravity to figure out the new
1073 y. the reference point should not be moved */
1074 if (!(e
->xconfigurerequest
.value_mask
& CWY
))
1075 client_gravity_resize_h(client
, &y
, client
->area
.height
,h
);
1079 ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d "
1080 "move %d resize %d\n",
1081 e
->xconfigurerequest
.value_mask
& CWX
, x
,
1082 e
->xconfigurerequest
.value_mask
& CWY
, y
,
1083 e
->xconfigurerequest
.value_mask
& CWWidth
, w
,
1084 e
->xconfigurerequest
.value_mask
& CWHeight
, h
,
1087 /* check for broken apps moving to their root position
1089 XXX remove this some day...that would be nice. right now all
1090 kde apps do this when they try activate themselves on another
1091 desktop. eg. open amarok window on desktop 1, switch to desktop
1092 2, click amarok tray icon. it will move by its decoration size.
1094 if (move
&& !resize
&&
1095 x
!= client
->area
.x
&&
1096 x
== (client
->frame
->area
.x
+ client
->frame
->size
.left
-
1097 (gint
)client
->border_width
) &&
1098 y
!= client
->area
.y
&&
1099 y
== (client
->frame
->area
.y
+ client
->frame
->size
.top
-
1100 (gint
)client
->border_width
))
1102 ob_debug_type(OB_DEBUG_APP_BUGS
,
1103 "Application %s is trying to move via "
1104 "ConfigureRequest to it's root window position "
1105 "but it is not using StaticGravity\n",
1111 /* they still requested a move, so don't change whether a
1112 notify is sent or not */
1115 if (move
|| resize
|| border
) {
1118 if (move
|| resize
) {
1119 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1120 client_try_configure(client
, &x
, &y
, &w
, &h
, &lw
, &lh
, FALSE
);
1122 /* if they requested something that moves the window, or if
1123 the window is actually being changed then configure it and
1124 send a configure notify to them */
1125 if (move
|| !RECT_EQUAL_DIMS(client
->area
, x
, y
, w
, h
) ||
1128 ob_debug("Doing configure\n");
1129 client_configure(client
, x
, y
, w
, h
, b
, FALSE
, TRUE
);
1132 /* ignore enter events caused by these like ob actions do */
1133 event_ignore_all_queued_enters();
1138 if (client
->ignore_unmaps
) {
1139 client
->ignore_unmaps
--;
1142 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
1143 "ignores left %d\n",
1144 client
->window
, e
->xunmap
.event
, e
->xunmap
.from_configure
,
1145 client
->ignore_unmaps
);
1146 client_unmanage(client
);
1149 ob_debug("DestroyNotify for window 0x%x\n", client
->window
);
1150 client_unmanage(client
);
1152 case ReparentNotify
:
1153 /* this is when the client is first taken captive in the frame */
1154 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
1157 This event is quite rare and is usually handled in unmapHandler.
1158 However, if the window is unmapped when the reparent event occurs,
1159 the window manager never sees it because an unmap event is not sent
1160 to an already unmapped window.
1163 /* we don't want the reparent event, put it back on the stack for the
1164 X server to deal with after we unmanage the window */
1165 XPutBackEvent(ob_display
, e
);
1167 ob_debug("ReparentNotify for window 0x%x\n", client
->window
);
1168 client_unmanage(client
);
1171 ob_debug("MapRequest for 0x%lx\n", client
->window
);
1172 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
1173 does, we don't want it!
1174 it can happen now when the window is on
1175 another desktop, but we still don't
1177 client_activate(client
, FALSE
, TRUE
);
1180 /* validate cuz we query stuff off the client here */
1181 if (!client_validate(client
)) break;
1183 if (e
->xclient
.format
!= 32) return;
1185 msgtype
= e
->xclient
.message_type
;
1186 if (msgtype
== prop_atoms
.wm_change_state
) {
1187 /* compress changes into a single change */
1188 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1190 /* XXX: it would be nice to compress ALL messages of a
1191 type, not just messages in a row without other
1192 message types between. */
1193 if (ce
.xclient
.message_type
!= msgtype
) {
1194 XPutBackEvent(ob_display
, &ce
);
1197 e
->xclient
= ce
.xclient
;
1199 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
1200 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
1201 /* compress changes into a single change */
1202 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1204 /* XXX: it would be nice to compress ALL messages of a
1205 type, not just messages in a row without other
1206 message types between. */
1207 if (ce
.xclient
.message_type
!= msgtype
) {
1208 XPutBackEvent(ob_display
, &ce
);
1211 e
->xclient
= ce
.xclient
;
1213 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
1214 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
1215 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
1217 } else if (msgtype
== prop_atoms
.net_wm_state
) {
1218 /* can't compress these */
1219 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1220 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
1221 e
->xclient
.data
.l
[0] == 1 ? "Add" :
1222 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1223 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
1225 client_set_state(client
, e
->xclient
.data
.l
[0],
1226 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
1228 /* ignore enter events caused by these like ob actions do */
1229 event_ignore_all_queued_enters();
1230 } else if (msgtype
== prop_atoms
.net_close_window
) {
1231 ob_debug("net_close_window for 0x%lx\n", client
->window
);
1232 client_close(client
);
1233 } else if (msgtype
== prop_atoms
.net_active_window
) {
1234 ob_debug("net_active_window for 0x%lx source=%s\n",
1236 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
1237 (e
->xclient
.data
.l
[0] == 1 ? "application" :
1238 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
1239 /* XXX make use of data.l[2] !? */
1240 if (e
->xclient
.data
.l
[0] == 1 || e
->xclient
.data
.l
[0] == 2) {
1241 event_curtime
= e
->xclient
.data
.l
[1];
1242 if (event_curtime
== 0)
1243 ob_debug_type(OB_DEBUG_APP_BUGS
,
1244 "_NET_ACTIVE_WINDOW message for window %s is"
1245 " missing a timestamp\n", client
->title
);
1247 ob_debug_type(OB_DEBUG_APP_BUGS
,
1248 "_NET_ACTIVE_WINDOW message for window %s is "
1249 "missing source indication\n");
1250 client_activate(client
, FALSE
,
1251 (e
->xclient
.data
.l
[0] == 0 ||
1252 e
->xclient
.data
.l
[0] == 2));
1253 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
1254 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1255 client
->window
, e
->xclient
.data
.l
[2]);
1256 if ((Atom
)e
->xclient
.data
.l
[2] ==
1257 prop_atoms
.net_wm_moveresize_size_topleft
||
1258 (Atom
)e
->xclient
.data
.l
[2] ==
1259 prop_atoms
.net_wm_moveresize_size_top
||
1260 (Atom
)e
->xclient
.data
.l
[2] ==
1261 prop_atoms
.net_wm_moveresize_size_topright
||
1262 (Atom
)e
->xclient
.data
.l
[2] ==
1263 prop_atoms
.net_wm_moveresize_size_right
||
1264 (Atom
)e
->xclient
.data
.l
[2] ==
1265 prop_atoms
.net_wm_moveresize_size_right
||
1266 (Atom
)e
->xclient
.data
.l
[2] ==
1267 prop_atoms
.net_wm_moveresize_size_bottomright
||
1268 (Atom
)e
->xclient
.data
.l
[2] ==
1269 prop_atoms
.net_wm_moveresize_size_bottom
||
1270 (Atom
)e
->xclient
.data
.l
[2] ==
1271 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1272 (Atom
)e
->xclient
.data
.l
[2] ==
1273 prop_atoms
.net_wm_moveresize_size_left
||
1274 (Atom
)e
->xclient
.data
.l
[2] ==
1275 prop_atoms
.net_wm_moveresize_move
||
1276 (Atom
)e
->xclient
.data
.l
[2] ==
1277 prop_atoms
.net_wm_moveresize_size_keyboard
||
1278 (Atom
)e
->xclient
.data
.l
[2] ==
1279 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1281 moveresize_start(client
, e
->xclient
.data
.l
[0],
1282 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1283 e
->xclient
.data
.l
[2]);
1285 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1286 prop_atoms
.net_wm_moveresize_cancel
)
1287 moveresize_end(TRUE
);
1288 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1289 gint ograv
, x
, y
, w
, h
;
1291 ograv
= client
->gravity
;
1293 if (e
->xclient
.data
.l
[0] & 0xff)
1294 client
->gravity
= e
->xclient
.data
.l
[0] & 0xff;
1296 if (e
->xclient
.data
.l
[0] & 1 << 8)
1297 x
= e
->xclient
.data
.l
[1];
1300 if (e
->xclient
.data
.l
[0] & 1 << 9)
1301 y
= e
->xclient
.data
.l
[2];
1305 if (e
->xclient
.data
.l
[0] & 1 << 10) {
1306 w
= e
->xclient
.data
.l
[3];
1308 /* if x was not given, then use gravity to figure out the new
1309 x. the reference point should not be moved */
1310 if (!(e
->xclient
.data
.l
[0] & 1 << 8))
1311 client_gravity_resize_w(client
, &x
, client
->area
.width
, w
);
1314 w
= client
->area
.width
;
1316 if (e
->xclient
.data
.l
[0] & 1 << 11) {
1317 h
= e
->xclient
.data
.l
[4];
1319 /* if y was not given, then use gravity to figure out the new
1320 y. the reference point should not be moved */
1321 if (!(e
->xclient
.data
.l
[0] & 1 << 9))
1322 client_gravity_resize_h(client
, &y
, client
->area
.height
,h
);
1325 h
= client
->area
.height
;
1327 ob_debug("MOVERESIZE x %d %d y %d %d (gravity %d)\n",
1328 e
->xclient
.data
.l
[0] & 1 << 8, x
,
1329 e
->xclient
.data
.l
[0] & 1 << 9, y
,
1332 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1334 client_configure(client
, x
, y
, w
, h
, client
->border_width
,
1337 client
->gravity
= ograv
;
1339 /* ignore enter events caused by these like ob actions do */
1340 event_ignore_all_queued_enters();
1341 } else if (msgtype
== prop_atoms
.net_restack_window
) {
1342 if (e
->xclient
.data
.l
[0] != 2) {
1343 ob_debug_type(OB_DEBUG_APP_BUGS
,
1344 "_NET_RESTACK_WINDOW sent for window %s with "
1345 "invalid source indication %ld\n",
1346 client
->title
, e
->xclient
.data
.l
[0]);
1348 ObClient
*sibling
= NULL
;
1349 if (e
->xclient
.data
.l
[1]) {
1350 ObWindow
*win
= g_hash_table_lookup
1351 (window_map
, &e
->xclient
.data
.l
[1]);
1352 if (WINDOW_IS_CLIENT(win
) &&
1353 WINDOW_AS_CLIENT(win
) != client
)
1355 sibling
= WINDOW_AS_CLIENT(win
);
1357 if (sibling
== NULL
)
1358 ob_debug_type(OB_DEBUG_APP_BUGS
,
1359 "_NET_RESTACK_WINDOW sent for window %s "
1360 "with invalid sibling 0x%x\n",
1361 client
->title
, e
->xclient
.data
.l
[1]);
1363 if (e
->xclient
.data
.l
[2] == Below
||
1364 e
->xclient
.data
.l
[2] == BottomIf
||
1365 e
->xclient
.data
.l
[2] == Above
||
1366 e
->xclient
.data
.l
[2] == TopIf
||
1367 e
->xclient
.data
.l
[2] == Opposite
)
1369 /* just raise, don't activate */
1370 stacking_restack_request(client
, sibling
,
1371 e
->xclient
.data
.l
[2], FALSE
);
1372 /* send a synthetic ConfigureNotify, cuz this is supposed
1373 to be like a ConfigureRequest. */
1374 client_reconfigure(client
);
1376 ob_debug_type(OB_DEBUG_APP_BUGS
,
1377 "_NET_RESTACK_WINDOW sent for window %s "
1378 "with invalid detail %d\n",
1379 client
->title
, e
->xclient
.data
.l
[2]);
1383 case PropertyNotify
:
1384 /* validate cuz we query stuff off the client here */
1385 if (!client_validate(client
)) break;
1387 /* compress changes to a single property into a single change */
1388 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1392 /* XXX: it would be nice to compress ALL changes to a property,
1393 not just changes in a row without other props between. */
1395 a
= ce
.xproperty
.atom
;
1396 b
= e
->xproperty
.atom
;
1400 if ((a
== prop_atoms
.net_wm_name
||
1401 a
== prop_atoms
.wm_name
||
1402 a
== prop_atoms
.net_wm_icon_name
||
1403 a
== prop_atoms
.wm_icon_name
)
1405 (b
== prop_atoms
.net_wm_name
||
1406 b
== prop_atoms
.wm_name
||
1407 b
== prop_atoms
.net_wm_icon_name
||
1408 b
== prop_atoms
.wm_icon_name
)) {
1411 if (a
== prop_atoms
.net_wm_icon
&&
1412 b
== prop_atoms
.net_wm_icon
)
1415 XPutBackEvent(ob_display
, &ce
);
1419 msgtype
= e
->xproperty
.atom
;
1420 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1421 client_update_normal_hints(client
);
1422 /* normal hints can make a window non-resizable */
1423 client_setup_decor_and_functions(client
);
1424 } else if (msgtype
== XA_WM_HINTS
) {
1425 client_update_wmhints(client
);
1426 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1427 client_update_transient_for(client
);
1428 client_get_type_and_transientness(client
);
1429 /* type may have changed, so update the layer */
1430 client_calc_layer(client
);
1431 client_setup_decor_and_functions(client
);
1432 } else if (msgtype
== prop_atoms
.net_wm_name
||
1433 msgtype
== prop_atoms
.wm_name
||
1434 msgtype
== prop_atoms
.net_wm_icon_name
||
1435 msgtype
== prop_atoms
.wm_icon_name
) {
1436 client_update_title(client
);
1437 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1438 client_update_protocols(client
);
1439 client_setup_decor_and_functions(client
);
1441 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1442 client_update_strut(client
);
1444 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1445 client_update_icons(client
);
1447 else if (msgtype
== prop_atoms
.net_wm_icon_geometry
) {
1448 client_update_icon_geometry(client
);
1450 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1451 client_update_user_time(client
);
1453 else if (msgtype
== prop_atoms
.net_wm_user_time_window
) {
1454 client_update_user_time_window(client
);
1457 else if (msgtype
== prop_atoms
.net_wm_sync_request_counter
) {
1458 client_update_sync_request_counter(client
);
1462 case ColormapNotify
:
1463 client_update_colormap(client
, e
->xcolormap
.colormap
);
1468 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1469 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1470 frame_adjust_shape(client
->frame
);
1476 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1480 if (e
->xbutton
.button
== 1)
1481 stacking_raise(DOCK_AS_WINDOW(s
));
1482 else if (e
->xbutton
.button
== 2)
1483 stacking_lower(DOCK_AS_WINDOW(s
));
1494 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1498 dock_app_drag(app
, &e
->xmotion
);
1501 if (app
->ignore_unmaps
) {
1502 app
->ignore_unmaps
--;
1505 dock_remove(app
, TRUE
);
1508 dock_remove(app
, FALSE
);
1510 case ReparentNotify
:
1511 dock_remove(app
, FALSE
);
1513 case ConfigureNotify
:
1514 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1519 static ObMenuFrame
* find_active_menu()
1522 ObMenuFrame
*ret
= NULL
;
1524 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1533 static ObMenuFrame
* find_active_or_last_menu()
1535 ObMenuFrame
*ret
= NULL
;
1537 ret
= find_active_menu();
1538 if (!ret
&& menu_frame_visible
)
1539 ret
= menu_frame_visible
->data
;
1543 static gboolean
event_handle_menu_keyboard(XEvent
*ev
)
1545 guint keycode
, state
;
1548 gboolean ret
= TRUE
;
1550 keycode
= ev
->xkey
.keycode
;
1551 state
= ev
->xkey
.state
;
1552 unikey
= translate_unichar(keycode
);
1554 frame
= find_active_or_last_menu();
1558 else if (keycode
== ob_keycode(OB_KEY_ESCAPE
) && state
== 0) {
1559 /* Escape goes to the parent menu or closes the last one */
1561 menu_frame_select(frame
, NULL
, TRUE
);
1563 menu_frame_hide_all();
1566 else if (keycode
== ob_keycode(OB_KEY_RETURN
) && (state
== 0 ||
1567 state
== ControlMask
))
1569 /* Enter runs the active item or goes into the submenu.
1570 Control-Enter runs it without closing the menu. */
1572 menu_frame_select_next(frame
->child
);
1573 else if (frame
->selected
)
1574 menu_entry_frame_execute(frame
->selected
, state
, ev
->xkey
.time
);
1577 else if (keycode
== ob_keycode(OB_KEY_LEFT
) && ev
->xkey
.state
== 0) {
1578 /* Left goes to the parent menu */
1579 menu_frame_select(frame
, NULL
, TRUE
);
1582 else if (keycode
== ob_keycode(OB_KEY_RIGHT
) && ev
->xkey
.state
== 0) {
1583 /* Right goes to the selected submenu */
1584 if (frame
->child
) menu_frame_select_next(frame
->child
);
1587 else if (keycode
== ob_keycode(OB_KEY_UP
) && state
== 0) {
1588 menu_frame_select_previous(frame
);
1591 else if (keycode
== ob_keycode(OB_KEY_DOWN
) && state
== 0) {
1592 menu_frame_select_next(frame
);
1595 /* keyboard accelerator shortcuts. (allow controlmask) */
1596 else if ((ev
->xkey
.state
& ~ControlMask
) == 0 &&
1597 /* was it a valid key? */
1599 /* don't bother if the menu is empty. */
1604 ObMenuEntryFrame
*found
= NULL
;
1605 guint num_found
= 0;
1607 /* start after the selected one */
1608 start
= frame
->entries
;
1609 if (frame
->selected
) {
1610 for (it
= start
; frame
->selected
!= it
->data
; it
= g_list_next(it
))
1611 g_assert(it
!= NULL
); /* nothing was selected? */
1612 /* next with wraparound */
1613 start
= g_list_next(it
);
1614 if (start
== NULL
) start
= frame
->entries
;
1619 ObMenuEntryFrame
*e
= it
->data
;
1620 gunichar entrykey
= 0;
1622 if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
)
1623 entrykey
= e
->entry
->data
.normal
.shortcut
;
1624 else if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_SUBMENU
)
1625 entrykey
= e
->entry
->data
.submenu
.submenu
->shortcut
;
1627 if (unikey
== entrykey
) {
1628 if (found
== NULL
) found
= e
;
1632 /* next with wraparound */
1633 it
= g_list_next(it
);
1634 if (it
== NULL
) it
= frame
->entries
;
1635 } while (it
!= start
);
1638 if (found
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
&&
1641 menu_frame_select(frame
, found
, TRUE
);
1642 usleep(50000); /* highlight the item for a short bit so the
1643 user can see what happened */
1644 menu_entry_frame_execute(found
, state
, ev
->xkey
.time
);
1646 menu_frame_select(frame
, found
, TRUE
);
1648 menu_frame_select_next(frame
->child
);
1659 static gboolean
event_handle_menu(XEvent
*ev
)
1662 ObMenuEntryFrame
*e
;
1663 gboolean ret
= TRUE
;
1667 if ((ev
->xbutton
.button
< 4 || ev
->xbutton
.button
> 5)
1670 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1671 ev
->xbutton
.y_root
)))
1672 menu_entry_frame_execute(e
, ev
->xbutton
.state
,
1675 menu_frame_hide_all();
1679 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
))) {
1680 if (e
->ignore_enters
)
1683 menu_frame_select(e
->frame
, e
, FALSE
);
1687 /*ignore leaves when we're already in the window */
1688 if (ev
->xcrossing
.detail
== NotifyInferior
)
1691 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
)) &&
1692 (f
= find_active_menu()) && f
->selected
== e
&&
1693 e
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1695 menu_frame_select(e
->frame
, NULL
, FALSE
);
1699 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1700 ev
->xmotion
.y_root
)))
1701 menu_frame_select(e
->frame
, e
, FALSE
);
1704 ret
= event_handle_menu_keyboard(ev
);
1710 static void event_handle_user_input(ObClient
*client
, XEvent
*e
)
1712 g_assert(e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
1713 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
1714 e
->type
== KeyRelease
);
1716 if (menu_frame_visible
) {
1717 if (event_handle_menu(e
))
1718 /* don't use the event if the menu used it, but if the menu
1719 didn't use it and it's a keypress that is bound, it will
1720 close the menu and be used */
1724 /* if the keyboard interactive action uses the event then dont
1725 use it for bindings. likewise is moveresize uses the event. */
1726 if (!keyboard_process_interactive_grab(e
, &client
) &&
1727 !(moveresize_in_progress
&& moveresize_event(e
)))
1729 if (moveresize_in_progress
)
1730 /* make further actions work on the client being
1732 client
= moveresize_client
;
1734 menu_can_hide
= FALSE
;
1735 ob_main_loop_timeout_add(ob_main_loop
,
1736 config_menu_hide_delay
* 1000,
1737 menu_hide_delay_func
,
1738 NULL
, g_direct_equal
, NULL
);
1740 if (e
->type
== ButtonPress
||
1741 e
->type
== ButtonRelease
||
1742 e
->type
== MotionNotify
)
1744 /* the frame may not be "visible" but they can still click on it
1745 in the case where it is animating before disappearing */
1746 if (!client
|| !frame_iconify_animating(client
->frame
))
1747 mouse_event(client
, e
);
1748 } else if (e
->type
== KeyPress
) {
1749 keyboard_event((focus_cycle_target
? focus_cycle_target
:
1750 (client
? client
: focus_client
)), e
);
1755 static gboolean
menu_hide_delay_func(gpointer data
)
1757 menu_can_hide
= TRUE
;
1758 return FALSE
; /* no repeat */
1761 static void focus_delay_dest(gpointer data
)
1766 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
)
1768 const ObFocusDelayData
*f1
= d1
;
1769 return f1
->client
== d2
;
1772 static gboolean
focus_delay_func(gpointer data
)
1774 ObFocusDelayData
*d
= data
;
1775 Time old
= event_curtime
;
1777 event_curtime
= d
->time
;
1778 if (focus_client
!= d
->client
) {
1779 if (client_focus(d
->client
) && config_focus_raise
)
1780 stacking_raise(CLIENT_AS_WINDOW(d
->client
));
1782 event_curtime
= old
;
1783 return FALSE
; /* no repeat */
1786 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1788 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1792 void event_halt_focus_delay()
1794 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1797 static Bool
event_look_for_enters(Display
*d
, XEvent
*e
, XPointer arg
)
1799 if (e
->type
== EnterNotify
&&
1800 /* these types aren't used for focusing */
1801 !(e
->xcrossing
.mode
== NotifyGrab
||
1802 e
->xcrossing
.mode
== NotifyUngrab
||
1803 e
->xcrossing
.detail
== NotifyInferior
))
1807 /* found an enter for that leave, ignore it if it's going to
1809 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1810 if (win
&& WINDOW_IS_CLIENT(win
))
1811 ++ignore_enter_focus
;
1813 return False
; /* don't disrupt the queue order, just count them */
1816 void event_ignore_all_queued_enters()
1820 XSync(ob_display
, FALSE
);
1822 /* count the events without disrupting them */
1823 ignore_enter_focus
= 0;
1824 XCheckIfEvent(ob_display
, &e
, event_look_for_enters
, NULL
);
1827 static gboolean
is_enter_focus_event_ignored(XEvent
*e
)
1829 g_assert(e
->type
== EnterNotify
&&
1830 !(e
->xcrossing
.mode
== NotifyGrab
||
1831 e
->xcrossing
.mode
== NotifyUngrab
||
1832 e
->xcrossing
.detail
== NotifyInferior
));
1834 ob_debug_type(OB_DEBUG_FOCUS
, "# enters ignored: %d\n",
1835 ignore_enter_focus
);
1837 if (ignore_enter_focus
) {
1838 --ignore_enter_focus
;
1844 void event_cancel_all_key_grabs()
1846 if (keyboard_interactively_grabbed())
1847 keyboard_interactive_cancel();
1848 else if (menu_frame_visible
)
1849 menu_frame_hide_all();
1850 else if (grab_on_keyboard())
1853 /* If we don't have the keyboard grabbed, then ungrab it with
1854 XUngrabKeyboard, so that there is not a passive grab left
1855 on from the KeyPress. If the grab is left on, and focus
1856 moves during that time, it will be NotifyWhileGrabbed, and
1857 applications like to ignore those! */
1858 if (!keyboard_interactively_grabbed())
1859 XUngrabKeyboard(ob_display
, CurrentTime
);
1863 gboolean
event_time_after(Time t1
, Time t2
)
1865 g_assert(t1
!= CurrentTime
);
1866 g_assert(t2
!= CurrentTime
);
1869 Timestamp values wrap around (after about 49.7 days). The server, given
1870 its current time is represented by timestamp T, always interprets
1871 timestamps from clients by treating half of the timestamp space as being
1872 later in time than T.
1873 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1876 /* TIME_HALF is half of the number space of a Time type variable */
1877 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1879 if (t2
>= TIME_HALF
)
1880 /* t2 is in the second half so t1 might wrap around and be smaller than
1882 return t1
>= t2
|| t1
< (t2
+ TIME_HALF
);
1884 /* t2 is in the first half so t1 has to come after it */
1885 return t1
>= t2
&& t1
< (t2
+ TIME_HALF
);