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.
32 #include "menuframe.h"
38 #include "framerender.h"
40 #include "moveresize.h"
43 #include "extensions.h"
44 #include "translate.h"
47 #include <X11/Xatom.h>
50 #ifdef HAVE_SYS_SELECT_H
51 # include <sys/select.h>
57 # include <unistd.h> /* for usleep() */
60 # include <X11/XKBlib.h>
64 #include <X11/ICE/ICElib.h>
78 static void event_process(const XEvent
*e
, gpointer data
);
79 static void event_handle_root(XEvent
*e
);
80 static gboolean
event_handle_menu_keyboard(XEvent
*e
);
81 static gboolean
event_handle_menu(XEvent
*e
);
82 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
83 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
84 static void event_handle_client(ObClient
*c
, XEvent
*e
);
85 static void event_handle_user_time_window_clients(GSList
*l
, XEvent
*e
);
86 static void event_handle_user_input(ObClient
*client
, XEvent
*e
);
88 static void focus_delay_dest(gpointer data
);
89 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
);
90 static gboolean
focus_delay_func(gpointer data
);
91 static void focus_delay_client_dest(ObClient
*client
, gpointer data
);
93 static gboolean
menu_hide_delay_func(gpointer data
);
95 /* The time for the current event being processed */
96 Time event_curtime
= CurrentTime
;
98 static guint ignore_enter_focus
= 0;
99 static gboolean menu_can_hide
;
100 static gboolean focus_left_screen
= FALSE
;
101 /*! This variable is used for focus fallback. If we fallback to a window, we
102 set this to the window. And when focus goes somewhere after that, it will
103 be set to NULL. If between falling back to that window and something
104 getting focused, the window gets unmanaged, then if there are no incoming
105 FocusIn events, we fallback again because focus has just gotten itself lost.
107 static ObClient
*focus_tried
= NULL
;
110 static void ice_handler(gint fd
, gpointer conn
)
113 IceProcessMessages(conn
, NULL
, &b
);
116 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
117 IcePointer
*watch_data
)
122 fd
= IceConnectionNumber(conn
);
123 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
125 ob_main_loop_fd_remove(ob_main_loop
, fd
);
131 void event_startup(gboolean reconfig
)
133 if (reconfig
) return;
135 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
138 IceAddConnectionWatch(ice_watch
, NULL
);
141 client_add_destructor(focus_delay_client_dest
, NULL
);
144 void event_shutdown(gboolean reconfig
)
146 if (reconfig
) return;
149 IceRemoveConnectionWatch(ice_watch
, NULL
);
152 client_remove_destructor(focus_delay_client_dest
);
155 static Window
event_get_window(XEvent
*e
)
162 window
= RootWindow(ob_display
, ob_screen
);
165 window
= e
->xmap
.window
;
168 window
= e
->xunmap
.window
;
171 window
= e
->xdestroywindow
.window
;
173 case ConfigureRequest
:
174 window
= e
->xconfigurerequest
.window
;
176 case ConfigureNotify
:
177 window
= e
->xconfigure
.window
;
181 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
182 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
184 window
= ((XkbBellNotifyEvent
*)e
)->window
;
191 if (extensions_sync
&&
192 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
197 window
= e
->xany
.window
;
202 static void event_set_curtime(XEvent
*e
)
204 Time t
= CurrentTime
;
206 /* grab the lasttime and hack up the state */
222 t
= e
->xproperty
.time
;
226 t
= e
->xcrossing
.time
;
230 if (extensions_sync
&&
231 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
233 t
= ((XSyncAlarmNotifyEvent
*)e
)->time
;
236 /* if more event types are anticipated, get their timestamp
244 static void event_hack_mods(XEvent
*e
)
247 XkbStateRec xkb_state
;
253 e
->xbutton
.state
= modkeys_only_modifier_masks(e
->xbutton
.state
);
256 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
259 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
261 if (XkbGetState(ob_display
, XkbUseCoreKbd
, &xkb_state
) == Success
) {
262 e
->xkey
.state
= xkb_state
.compat_state
;
266 /* remove from the state the mask of the modifier key being released,
267 if it is a modifier key being released that is */
268 e
->xkey
.state
&= ~modkeys_keycode_to_mask(e
->xkey
.keycode
);
271 e
->xmotion
.state
= modkeys_only_modifier_masks(e
->xmotion
.state
);
272 /* compress events */
275 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
277 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
278 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
285 static gboolean
wanted_focusevent(XEvent
*e
, gboolean in_client_only
)
287 gint mode
= e
->xfocus
.mode
;
288 gint detail
= e
->xfocus
.detail
;
289 Window win
= e
->xany
.window
;
291 if (e
->type
== FocusIn
) {
292 /* These are ones we never want.. */
294 /* This means focus was given by a keyboard/mouse grab. */
295 if (mode
== NotifyGrab
)
297 /* This means focus was given back from a keyboard/mouse grab. */
298 if (mode
== NotifyUngrab
)
301 /* These are the ones we want.. */
303 if (win
== RootWindow(ob_display
, ob_screen
) && !in_client_only
) {
304 /* This means focus reverted off of a client */
305 if (detail
== NotifyPointerRoot
|| detail
== NotifyDetailNone
||
306 detail
== NotifyInferior
)
312 /* This means focus moved from the root window to a client */
313 if (detail
== NotifyVirtual
)
315 /* This means focus moved from one client to another */
316 if (detail
== NotifyNonlinearVirtual
)
318 /* This means focus moved to the frame window */
319 if (detail
== NotifyInferior
&& !in_client_only
)
325 g_assert(e
->type
== FocusOut
);
327 /* These are ones we never want.. */
329 /* This means focus was taken by a keyboard/mouse grab. */
330 if (mode
== NotifyGrab
)
333 /* Focus left the root window revertedto state */
334 if (win
== RootWindow(ob_display
, ob_screen
))
337 /* These are the ones we want.. */
339 /* This means focus moved from a client to the root window */
340 if (detail
== NotifyVirtual
)
342 /* This means focus moved from one client to another */
343 if (detail
== NotifyNonlinearVirtual
)
345 /* This means focus had moved to our frame window and now moved off */
346 if (detail
== NotifyNonlinear
)
354 static Bool
look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
356 return e
->type
== FocusIn
&& wanted_focusevent(e
, FALSE
);
359 static Bool
look_for_focusin_client(Display
*d
, XEvent
*e
, XPointer arg
)
361 return e
->type
== FocusIn
&& wanted_focusevent(e
, TRUE
);
364 static void print_focusevent(XEvent
*e
)
366 gint mode
= e
->xfocus
.mode
;
367 gint detail
= e
->xfocus
.detail
;
368 Window win
= e
->xany
.window
;
369 const gchar
*modestr
, *detailstr
;
372 case NotifyNormal
: modestr
="NotifyNormal"; break;
373 case NotifyGrab
: modestr
="NotifyGrab"; break;
374 case NotifyUngrab
: modestr
="NotifyUngrab"; break;
375 case NotifyWhileGrabbed
: modestr
="NotifyWhileGrabbed"; break;
378 case NotifyAncestor
: detailstr
="NotifyAncestor"; break;
379 case NotifyVirtual
: detailstr
="NotifyVirtual"; break;
380 case NotifyInferior
: detailstr
="NotifyInferior"; break;
381 case NotifyNonlinear
: detailstr
="NotifyNonlinear"; break;
382 case NotifyNonlinearVirtual
: detailstr
="NotifyNonlinearVirtual"; break;
383 case NotifyPointer
: detailstr
="NotifyPointer"; break;
384 case NotifyPointerRoot
: detailstr
="NotifyPointerRoot"; break;
385 case NotifyDetailNone
: detailstr
="NotifyDetailNone"; break;
390 ob_debug_type(OB_DEBUG_FOCUS
, "Focus%s 0x%x mode=%s detail=%s\n",
391 (e
->xfocus
.type
== FocusIn
? "In" : "Out"),
397 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
402 if (!wanted_focusevent(e
, FALSE
))
407 if (!wanted_focusevent(e
, FALSE
))
414 static void event_process(const XEvent
*ec
, gpointer data
)
417 ObClient
*client
= NULL
;
419 ObDockApp
*dockapp
= NULL
;
420 ObWindow
*obwin
= NULL
;
421 GSList
*timewinclients
= NULL
;
423 ObEventData
*ed
= data
;
425 /* make a copy we can mangle */
429 window
= event_get_window(e
);
430 if (e
->type
!= PropertyNotify
||
431 !(timewinclients
= propwin_get_clients(window
,
432 OB_PROPWIN_USER_TIME
)))
433 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
434 switch (obwin
->type
) {
436 dock
= WINDOW_AS_DOCK(obwin
);
439 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
442 client
= WINDOW_AS_CLIENT(obwin
);
445 case Window_Internal
:
446 /* not to be used for events */
447 g_assert_not_reached();
452 event_set_curtime(e
);
454 if (event_ignore(e
, client
)) {
461 /* deal with it in the kernel */
463 if (menu_frame_visible
&&
464 (e
->type
== EnterNotify
|| e
->type
== LeaveNotify
))
466 /* crossing events for menu */
467 event_handle_menu(e
);
468 } else if (e
->type
== FocusIn
) {
469 if (e
->xfocus
.detail
== NotifyPointerRoot
||
470 e
->xfocus
.detail
== NotifyDetailNone
||
471 e
->xfocus
.detail
== NotifyInferior
)
474 ob_debug_type(OB_DEBUG_FOCUS
,
475 "Focus went to pointer root/none or to our frame "
478 /* If another FocusIn is in the queue then don't fallback yet. This
479 fixes the fun case of:
480 window map -> send focusin
481 window unmap -> get focusout
482 window map -> send focusin
483 get first focus out -> fall back to something (new window
484 hasn't received focus yet, so something else) -> send focusin
485 which means the "something else" is the last thing to get a
486 focusin sent to it, so the new window doesn't end up with focus.
488 But if the other focus in is something like PointerRoot then we
489 still want to fall back.
491 if (XCheckIfEvent(ob_display
, &ce
, look_for_focusin_client
, NULL
)){
492 XPutBackEvent(ob_display
, &ce
);
493 ob_debug_type(OB_DEBUG_FOCUS
,
494 " but another FocusIn is coming\n");
496 /* Focus has been reverted to the root window, nothing, or to
499 FocusOut events come after UnmapNotify, so we don't need to
500 worry about focusing an invalid window
503 /* In this case we know focus is in our screen */
504 if (e
->xfocus
.detail
== NotifyInferior
)
505 focus_left_screen
= FALSE
;
507 if (!focus_left_screen
)
508 focus_tried
= focus_fallback(TRUE
);
510 } else if (client
&& client
!= focus_client
) {
511 focus_left_screen
= FALSE
;
512 frame_adjust_focus(client
->frame
, TRUE
);
513 focus_set_client(client
);
514 client_calc_layer(client
);
515 client_bring_helper_windows(client
);
517 focus_tried
= NULL
; /* focus isn't "trying" to go anywhere now */
519 } else if (e
->type
== FocusOut
) {
520 gboolean nomove
= FALSE
;
523 /* Look for the followup FocusIn */
524 if (!XCheckIfEvent(ob_display
, &ce
, look_for_focusin
, NULL
)) {
525 /* There is no FocusIn, this means focus went to a window that
526 is not being managed, or a window on another screen. */
530 xerror_set_ignore(TRUE
);
531 if (XGetInputFocus(ob_display
, &win
, &i
) != 0 &&
532 XGetGeometry(ob_display
, win
, &root
, &i
,&i
,&u
,&u
,&u
,&u
) != 0 &&
533 root
!= RootWindow(ob_display
, ob_screen
))
535 ob_debug_type(OB_DEBUG_FOCUS
,
536 "Focus went to another screen !\n");
537 focus_left_screen
= TRUE
;
540 ob_debug_type(OB_DEBUG_FOCUS
,
541 "Focus went to a black hole !\n");
542 xerror_set_ignore(FALSE
);
543 /* nothing is focused */
544 focus_set_client(NULL
);
545 } else if (ce
.xany
.window
== e
->xany
.window
) {
546 ob_debug_type(OB_DEBUG_FOCUS
, "Focus didn't go anywhere\n");
547 /* If focus didn't actually move anywhere, there is nothing to do*/
550 /* Focus did move, so process the FocusIn event */
551 ObEventData ed
= { .ignored
= FALSE
};
552 event_process(&ce
, &ed
);
554 /* The FocusIn was ignored, this means it was on a window
555 that isn't a client. */
556 ob_debug_type(OB_DEBUG_FOCUS
,
557 "Focus went to an unmanaged window 0x%x !\n",
559 focus_tried
= focus_fallback(TRUE
);
563 if (client
&& !nomove
) {
564 frame_adjust_focus(client
->frame
, FALSE
);
565 /* focus_set_client has already been called for sure */
566 client_calc_layer(client
);
568 } else if (timewinclients
)
569 event_handle_user_time_window_clients(timewinclients
, e
);
571 event_handle_client(client
, e
);
573 event_handle_dockapp(dockapp
, e
);
575 event_handle_dock(dock
, e
);
576 else if (window
== RootWindow(ob_display
, ob_screen
))
577 event_handle_root(e
);
578 else if (e
->type
== MapRequest
)
579 client_manage(window
);
580 else if (e
->type
== ClientMessage
) {
581 /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
582 windows that are not managed yet. */
583 if (e
->xclient
.message_type
== prop_atoms
.net_request_frame_extents
) {
584 /* Pretend to manage the client, getting information used to
585 determine its decorations */
586 ObClient
*c
= client_fake_manage(e
->xclient
.window
);
589 /* set the frame extents on the window */
590 vals
[0] = c
->frame
->size
.left
;
591 vals
[1] = c
->frame
->size
.right
;
592 vals
[2] = c
->frame
->size
.top
;
593 vals
[3] = c
->frame
->size
.bottom
;
594 PROP_SETA32(e
->xclient
.window
, net_frame_extents
,
597 /* Free the pretend client */
598 client_fake_unmanage(c
);
601 else if (e
->type
== ConfigureRequest
) {
602 /* unhandled configure requests must be used to configure the
606 xwc
.x
= e
->xconfigurerequest
.x
;
607 xwc
.y
= e
->xconfigurerequest
.y
;
608 xwc
.width
= e
->xconfigurerequest
.width
;
609 xwc
.height
= e
->xconfigurerequest
.height
;
610 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
611 xwc
.sibling
= e
->xconfigurerequest
.above
;
612 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
614 /* we are not to be held responsible if someone sends us an
616 xerror_set_ignore(TRUE
);
617 XConfigureWindow(ob_display
, window
,
618 e
->xconfigurerequest
.value_mask
, &xwc
);
619 xerror_set_ignore(FALSE
);
622 else if (extensions_sync
&&
623 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
625 XSyncAlarmNotifyEvent
*se
= (XSyncAlarmNotifyEvent
*)e
;
626 if (se
->alarm
== moveresize_alarm
&& moveresize_in_progress
)
631 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
632 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
633 e
->type
== KeyRelease
)
635 event_handle_user_input(client
, e
);
638 /* if something happens and it's not from an XEvent, then we don't know
640 event_curtime
= CurrentTime
;
643 static void event_handle_root(XEvent
*e
)
649 ob_debug("Another WM has requested to replace us. Exiting.\n");
654 if (e
->xclient
.format
!= 32) break;
656 msgtype
= e
->xclient
.message_type
;
657 if (msgtype
== prop_atoms
.net_current_desktop
) {
658 guint d
= e
->xclient
.data
.l
[0];
659 if (d
< screen_num_desktops
) {
660 event_curtime
= e
->xclient
.data
.l
[1];
661 if (event_curtime
== 0)
662 ob_debug_type(OB_DEBUG_APP_BUGS
,
663 "_NET_CURRENT_DESKTOP message is missing "
665 screen_set_desktop(d
, TRUE
);
667 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
668 guint d
= e
->xclient
.data
.l
[0];
670 screen_set_num_desktops(d
);
671 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
672 screen_show_desktop(e
->xclient
.data
.l
[0] != 0, NULL
);
673 } else if (msgtype
== prop_atoms
.openbox_control
) {
674 if (e
->xclient
.data
.l
[0] == 1)
676 else if (e
->xclient
.data
.l
[0] == 2)
681 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
682 screen_update_desktop_names();
683 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
684 screen_update_layout();
686 case ConfigureNotify
:
688 XRRUpdateConfiguration(e
);
697 void event_enter_client(ObClient
*client
)
699 g_assert(config_focus_follow
);
701 if (client_enter_focusable(client
) && client_can_focus(client
)) {
702 if (config_focus_delay
) {
703 ObFocusDelayData
*data
;
705 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
707 data
= g_new(ObFocusDelayData
, 1);
708 data
->client
= client
;
709 data
->time
= event_curtime
;
711 ob_main_loop_timeout_add(ob_main_loop
,
714 data
, focus_delay_cmp
, focus_delay_dest
);
716 ObFocusDelayData data
;
717 data
.client
= client
;
718 data
.time
= event_curtime
;
719 focus_delay_func(&data
);
724 static void event_handle_user_time_window_clients(GSList
*l
, XEvent
*e
)
726 g_assert(e
->type
== PropertyNotify
);
727 if (e
->xproperty
.atom
== prop_atoms
.net_wm_user_time
) {
728 for (; l
; l
= g_slist_next(l
))
729 client_update_user_time(l
->data
);
733 static void event_handle_client(ObClient
*client
, XEvent
*e
)
738 static gint px
= -1, py
= -1;
743 /* save where the press occured for the first button pressed */
745 pb
= e
->xbutton
.button
;
750 /* Wheel buttons don't draw because they are an instant click, so it
751 is a waste of resources to go drawing it.
752 if the user is doing an intereactive thing, or has a menu open then
753 the mouse is grabbed (possibly) and if we get these events we don't
754 want to deal with them
756 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5) &&
757 !keyboard_interactively_grabbed() &&
760 /* use where the press occured */
761 con
= frame_context(client
, e
->xbutton
.window
, px
, py
);
762 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
764 if (e
->type
== ButtonRelease
&& e
->xbutton
.button
== pb
)
765 pb
= 0, px
= py
= -1;
768 case OB_FRAME_CONTEXT_MAXIMIZE
:
769 client
->frame
->max_press
= (e
->type
== ButtonPress
);
770 framerender_frame(client
->frame
);
772 case OB_FRAME_CONTEXT_CLOSE
:
773 client
->frame
->close_press
= (e
->type
== ButtonPress
);
774 framerender_frame(client
->frame
);
776 case OB_FRAME_CONTEXT_ICONIFY
:
777 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
778 framerender_frame(client
->frame
);
780 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
781 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
782 framerender_frame(client
->frame
);
784 case OB_FRAME_CONTEXT_SHADE
:
785 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
786 framerender_frame(client
->frame
);
789 /* nothing changes with clicks for any other contexts */
795 con
= frame_context(client
, e
->xmotion
.window
,
796 e
->xmotion
.x
, e
->xmotion
.y
);
798 case OB_FRAME_CONTEXT_TITLEBAR
:
799 /* we've left the button area inside the titlebar */
800 if (client
->frame
->max_hover
|| client
->frame
->desk_hover
||
801 client
->frame
->shade_hover
|| client
->frame
->iconify_hover
||
802 client
->frame
->close_hover
)
804 client
->frame
->max_hover
= FALSE
;
805 client
->frame
->desk_hover
= FALSE
;
806 client
->frame
->shade_hover
= FALSE
;
807 client
->frame
->iconify_hover
= FALSE
;
808 client
->frame
->close_hover
= FALSE
;
809 frame_adjust_state(client
->frame
);
812 case OB_FRAME_CONTEXT_MAXIMIZE
:
813 if (!client
->frame
->max_hover
) {
814 client
->frame
->max_hover
= TRUE
;
815 frame_adjust_state(client
->frame
);
818 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
819 if (!client
->frame
->desk_hover
) {
820 client
->frame
->desk_hover
= TRUE
;
821 frame_adjust_state(client
->frame
);
824 case OB_FRAME_CONTEXT_SHADE
:
825 if (!client
->frame
->shade_hover
) {
826 client
->frame
->shade_hover
= TRUE
;
827 frame_adjust_state(client
->frame
);
830 case OB_FRAME_CONTEXT_ICONIFY
:
831 if (!client
->frame
->iconify_hover
) {
832 client
->frame
->iconify_hover
= TRUE
;
833 frame_adjust_state(client
->frame
);
836 case OB_FRAME_CONTEXT_CLOSE
:
837 if (!client
->frame
->close_hover
) {
838 client
->frame
->close_hover
= TRUE
;
839 frame_adjust_state(client
->frame
);
847 con
= frame_context(client
, e
->xcrossing
.window
,
848 e
->xcrossing
.x
, e
->xcrossing
.y
);
850 case OB_FRAME_CONTEXT_MAXIMIZE
:
851 client
->frame
->max_hover
= FALSE
;
852 frame_adjust_state(client
->frame
);
854 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
855 client
->frame
->desk_hover
= FALSE
;
856 frame_adjust_state(client
->frame
);
858 case OB_FRAME_CONTEXT_SHADE
:
859 client
->frame
->shade_hover
= FALSE
;
860 frame_adjust_state(client
->frame
);
862 case OB_FRAME_CONTEXT_ICONIFY
:
863 client
->frame
->iconify_hover
= FALSE
;
864 frame_adjust_state(client
->frame
);
866 case OB_FRAME_CONTEXT_CLOSE
:
867 client
->frame
->close_hover
= FALSE
;
868 frame_adjust_state(client
->frame
);
870 case OB_FRAME_CONTEXT_FRAME
:
871 /* When the mouse leaves an animating window, don't use the
872 corresponding enter events. Pretend like the animating window
873 doesn't even exist..! */
874 if (frame_iconify_animating(client
->frame
))
875 event_ignore_queued_enters();
877 ob_debug_type(OB_DEBUG_FOCUS
,
878 "%sNotify mode %d detail %d on %lx\n",
879 (e
->type
== EnterNotify
? "Enter" : "Leave"),
881 e
->xcrossing
.detail
, (client
?client
->window
:0));
882 if (keyboard_interactively_grabbed())
884 if (config_focus_follow
&& config_focus_delay
&&
885 /* leave inferior events can happen when the mouse goes onto
886 the window's border and then into the window before the
888 e
->xcrossing
.detail
!= NotifyInferior
)
890 ob_main_loop_timeout_remove_data(ob_main_loop
,
901 gboolean nofocus
= FALSE
;
903 if (ignore_enter_focus
) {
904 ignore_enter_focus
--;
908 con
= frame_context(client
, e
->xcrossing
.window
,
909 e
->xcrossing
.x
, e
->xcrossing
.y
);
911 case OB_FRAME_CONTEXT_MAXIMIZE
:
912 client
->frame
->max_hover
= TRUE
;
913 frame_adjust_state(client
->frame
);
915 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
916 client
->frame
->desk_hover
= TRUE
;
917 frame_adjust_state(client
->frame
);
919 case OB_FRAME_CONTEXT_SHADE
:
920 client
->frame
->shade_hover
= TRUE
;
921 frame_adjust_state(client
->frame
);
923 case OB_FRAME_CONTEXT_ICONIFY
:
924 client
->frame
->iconify_hover
= TRUE
;
925 frame_adjust_state(client
->frame
);
927 case OB_FRAME_CONTEXT_CLOSE
:
928 client
->frame
->close_hover
= TRUE
;
929 frame_adjust_state(client
->frame
);
931 case OB_FRAME_CONTEXT_FRAME
:
932 if (keyboard_interactively_grabbed())
934 if (e
->xcrossing
.mode
== NotifyGrab
||
935 e
->xcrossing
.mode
== NotifyUngrab
||
936 /*ignore enters when we're already in the window */
937 e
->xcrossing
.detail
== NotifyInferior
)
939 ob_debug_type(OB_DEBUG_FOCUS
,
940 "%sNotify mode %d detail %d on %lx IGNORED\n",
941 (e
->type
== EnterNotify
? "Enter" : "Leave"),
943 e
->xcrossing
.detail
, client
?client
->window
:0);
945 ob_debug_type(OB_DEBUG_FOCUS
,
946 "%sNotify mode %d detail %d on %lx, "
947 "focusing window: %d\n",
948 (e
->type
== EnterNotify
? "Enter" : "Leave"),
950 e
->xcrossing
.detail
, (client
?client
->window
:0),
952 if (!nofocus
&& config_focus_follow
)
953 event_enter_client(client
);
961 case ConfigureRequest
:
963 /* dont compress these unless you're going to watch for property
964 notifies in between (these can change what the configure would
966 also you can't compress stacking events
971 /* if nothing is changed, then a configurenotify is needed */
972 gboolean config
= TRUE
;
976 w
= client
->area
.width
;
977 h
= client
->area
.height
;
979 ob_debug("ConfigureRequest desktop %d wmstate %d visibile %d\n",
980 screen_desktop
, client
->wmstate
, client
->frame
->visible
);
982 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
983 if (client
->border_width
!= e
->xconfigurerequest
.border_width
) {
984 client
->border_width
= e
->xconfigurerequest
.border_width
;
985 /* if only the border width is changing, then it's not needed*/
990 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
991 ObClient
*sibling
= NULL
;
993 /* get the sibling */
994 if (e
->xconfigurerequest
.value_mask
& CWSibling
) {
996 win
= g_hash_table_lookup(window_map
,
997 &e
->xconfigurerequest
.above
);
998 if (WINDOW_IS_CLIENT(win
) && WINDOW_AS_CLIENT(win
) != client
)
999 sibling
= WINDOW_AS_CLIENT(win
);
1002 /* activate it rather than just focus it */
1003 stacking_restack_request(client
, sibling
,
1004 e
->xconfigurerequest
.detail
, TRUE
);
1006 /* if a stacking change is requested then it is needed */
1010 /* don't allow clients to move shaded windows (fvwm does this) */
1011 if (client
->shaded
&& (e
->xconfigurerequest
.value_mask
& CWX
||
1012 e
->xconfigurerequest
.value_mask
& CWY
))
1014 e
->xconfigurerequest
.value_mask
&= ~CWX
;
1015 e
->xconfigurerequest
.value_mask
&= ~CWY
;
1017 /* if the client tried to move and we aren't letting it then a
1018 synthetic event is needed */
1022 if (e
->xconfigurerequest
.value_mask
& CWX
||
1023 e
->xconfigurerequest
.value_mask
& CWY
||
1024 e
->xconfigurerequest
.value_mask
& CWWidth
||
1025 e
->xconfigurerequest
.value_mask
& CWHeight
)
1027 if (e
->xconfigurerequest
.value_mask
& CWX
)
1028 x
= e
->xconfigurerequest
.x
;
1029 if (e
->xconfigurerequest
.value_mask
& CWY
)
1030 y
= e
->xconfigurerequest
.y
;
1031 if (e
->xconfigurerequest
.value_mask
& CWWidth
)
1032 w
= e
->xconfigurerequest
.width
;
1033 if (e
->xconfigurerequest
.value_mask
& CWHeight
)
1034 h
= e
->xconfigurerequest
.height
;
1036 /* if a new position or size is requested, then a configure is
1041 ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d\n",
1042 e
->xconfigurerequest
.value_mask
& CWX
, x
,
1043 e
->xconfigurerequest
.value_mask
& CWY
, y
,
1044 e
->xconfigurerequest
.value_mask
& CWWidth
, w
,
1045 e
->xconfigurerequest
.value_mask
& CWHeight
, h
);
1047 /* check for broken apps moving to their root position
1049 XXX remove this some day...that would be nice. right now all
1050 kde apps do this when they try activate themselves on another
1051 desktop. eg. open amarok window on desktop 1, switch to desktop
1052 2, click amarok tray icon. it will move by its decoration size.
1054 if (x
!= client
->area
.x
&&
1055 x
== (client
->frame
->area
.x
+ client
->frame
->size
.left
-
1056 (gint
)client
->border_width
) &&
1057 y
!= client
->area
.y
&&
1058 y
== (client
->frame
->area
.y
+ client
->frame
->size
.top
-
1059 (gint
)client
->border_width
))
1061 ob_debug_type(OB_DEBUG_APP_BUGS
,
1062 "Application %s is trying to move via "
1063 "ConfigureRequest to it's root window position "
1064 "but it is not using StaticGravity\n",
1072 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1073 client_configure_full(client
, x
, y
, w
, h
, FALSE
, TRUE
);
1078 if (client
->ignore_unmaps
) {
1079 client
->ignore_unmaps
--;
1082 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
1083 "ignores left %d\n",
1084 client
->window
, e
->xunmap
.event
, e
->xunmap
.from_configure
,
1085 client
->ignore_unmaps
);
1086 client_unmanage(client
);
1088 /* we were trying to focus this window but it's gone */
1089 if (client
== focus_tried
) {
1090 ob_debug_type(OB_DEBUG_FOCUS
, "Tried to focus window 0x%x and it "
1091 "is being unmanaged:\n");
1092 if (XCheckIfEvent(ob_display
, &ce
, look_for_focusin_client
, NULL
)){
1093 XPutBackEvent(ob_display
, &ce
);
1094 ob_debug_type(OB_DEBUG_FOCUS
,
1095 " but another FocusIn is coming\n");
1097 ob_debug_type(OB_DEBUG_FOCUS
,
1098 " so falling back focus again.\n");
1099 focus_tried
= focus_fallback(TRUE
);
1104 ob_debug("DestroyNotify for window 0x%x\n", client
->window
);
1105 client_unmanage(client
);
1107 /* we were trying to focus this window but it's gone */
1108 if (client
== focus_tried
) {
1109 ob_debug_type(OB_DEBUG_FOCUS
, "Tried to focus window 0x%x and it "
1110 "is being unmanaged:\n");
1111 if (XCheckIfEvent(ob_display
, &ce
, look_for_focusin_client
, NULL
)){
1112 XPutBackEvent(ob_display
, &ce
);
1113 ob_debug_type(OB_DEBUG_FOCUS
,
1114 " but another FocusIn is coming\n");
1116 ob_debug_type(OB_DEBUG_FOCUS
,
1117 " so falling back focus again.\n");
1118 focus_tried
= focus_fallback(TRUE
);
1122 case ReparentNotify
:
1123 /* this is when the client is first taken captive in the frame */
1124 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
1127 This event is quite rare and is usually handled in unmapHandler.
1128 However, if the window is unmapped when the reparent event occurs,
1129 the window manager never sees it because an unmap event is not sent
1130 to an already unmapped window.
1133 /* we don't want the reparent event, put it back on the stack for the
1134 X server to deal with after we unmanage the window */
1135 XPutBackEvent(ob_display
, e
);
1137 ob_debug("ReparentNotify for window 0x%x\n", client
->window
);
1138 client_unmanage(client
);
1140 /* we were trying to focus this window but it's gone */
1141 if (client
== focus_tried
) {
1142 ob_debug_type(OB_DEBUG_FOCUS
, "Tried to focus window 0x%x and it "
1143 "is being unmanaged:\n");
1144 if (XCheckIfEvent(ob_display
, &ce
, look_for_focusin_client
, NULL
)){
1145 XPutBackEvent(ob_display
, &ce
);
1146 ob_debug_type(OB_DEBUG_FOCUS
,
1147 " but another FocusIn is coming\n");
1149 ob_debug_type(OB_DEBUG_FOCUS
,
1150 " so falling back focus again.\n");
1151 focus_tried
= focus_fallback(TRUE
);
1156 ob_debug("MapRequest for 0x%lx\n", client
->window
);
1157 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
1158 does, we don't want it!
1159 it can happen now when the window is on
1160 another desktop, but we still don't
1162 client_activate(client
, FALSE
, TRUE
);
1165 /* validate cuz we query stuff off the client here */
1166 if (!client_validate(client
)) break;
1168 if (e
->xclient
.format
!= 32) return;
1170 msgtype
= e
->xclient
.message_type
;
1171 if (msgtype
== prop_atoms
.wm_change_state
) {
1172 /* compress changes into a single change */
1173 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1175 /* XXX: it would be nice to compress ALL messages of a
1176 type, not just messages in a row without other
1177 message types between. */
1178 if (ce
.xclient
.message_type
!= msgtype
) {
1179 XPutBackEvent(ob_display
, &ce
);
1182 e
->xclient
= ce
.xclient
;
1184 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
1185 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
1186 /* compress changes into a single change */
1187 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1189 /* XXX: it would be nice to compress ALL messages of a
1190 type, not just messages in a row without other
1191 message types between. */
1192 if (ce
.xclient
.message_type
!= msgtype
) {
1193 XPutBackEvent(ob_display
, &ce
);
1196 e
->xclient
= ce
.xclient
;
1198 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
1199 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
1200 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
1202 } else if (msgtype
== prop_atoms
.net_wm_state
) {
1203 /* can't compress these */
1204 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1205 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
1206 e
->xclient
.data
.l
[0] == 1 ? "Add" :
1207 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1208 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
1210 client_set_state(client
, e
->xclient
.data
.l
[0],
1211 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
1212 } else if (msgtype
== prop_atoms
.net_close_window
) {
1213 ob_debug("net_close_window for 0x%lx\n", client
->window
);
1214 client_close(client
);
1215 } else if (msgtype
== prop_atoms
.net_active_window
) {
1216 ob_debug("net_active_window for 0x%lx source=%s\n",
1218 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
1219 (e
->xclient
.data
.l
[0] == 1 ? "application" :
1220 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
1221 /* XXX make use of data.l[2] !? */
1222 event_curtime
= e
->xclient
.data
.l
[1];
1223 if (event_curtime
== 0)
1224 ob_debug_type(OB_DEBUG_APP_BUGS
,
1225 "_NET_ACTIVE_WINDOW message for window %s is "
1226 "missing a timestamp\n", client
->title
);
1227 client_activate(client
, FALSE
,
1228 (e
->xclient
.data
.l
[0] == 0 ||
1229 e
->xclient
.data
.l
[0] == 2));
1230 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
1231 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1232 client
->window
, e
->xclient
.data
.l
[2]);
1233 if ((Atom
)e
->xclient
.data
.l
[2] ==
1234 prop_atoms
.net_wm_moveresize_size_topleft
||
1235 (Atom
)e
->xclient
.data
.l
[2] ==
1236 prop_atoms
.net_wm_moveresize_size_top
||
1237 (Atom
)e
->xclient
.data
.l
[2] ==
1238 prop_atoms
.net_wm_moveresize_size_topright
||
1239 (Atom
)e
->xclient
.data
.l
[2] ==
1240 prop_atoms
.net_wm_moveresize_size_right
||
1241 (Atom
)e
->xclient
.data
.l
[2] ==
1242 prop_atoms
.net_wm_moveresize_size_right
||
1243 (Atom
)e
->xclient
.data
.l
[2] ==
1244 prop_atoms
.net_wm_moveresize_size_bottomright
||
1245 (Atom
)e
->xclient
.data
.l
[2] ==
1246 prop_atoms
.net_wm_moveresize_size_bottom
||
1247 (Atom
)e
->xclient
.data
.l
[2] ==
1248 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1249 (Atom
)e
->xclient
.data
.l
[2] ==
1250 prop_atoms
.net_wm_moveresize_size_left
||
1251 (Atom
)e
->xclient
.data
.l
[2] ==
1252 prop_atoms
.net_wm_moveresize_move
||
1253 (Atom
)e
->xclient
.data
.l
[2] ==
1254 prop_atoms
.net_wm_moveresize_size_keyboard
||
1255 (Atom
)e
->xclient
.data
.l
[2] ==
1256 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1258 moveresize_start(client
, e
->xclient
.data
.l
[0],
1259 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1260 e
->xclient
.data
.l
[2]);
1262 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1263 prop_atoms
.net_wm_moveresize_cancel
)
1264 moveresize_end(TRUE
);
1265 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1266 gint grav
, x
, y
, w
, h
;
1268 if (e
->xclient
.data
.l
[0] & 0xff)
1269 grav
= e
->xclient
.data
.l
[0] & 0xff;
1271 grav
= client
->gravity
;
1273 if (e
->xclient
.data
.l
[0] & 1 << 8)
1274 x
= e
->xclient
.data
.l
[1];
1277 if (e
->xclient
.data
.l
[0] & 1 << 9)
1278 y
= e
->xclient
.data
.l
[2];
1281 if (e
->xclient
.data
.l
[0] & 1 << 10)
1282 w
= e
->xclient
.data
.l
[3];
1284 w
= client
->area
.width
;
1285 if (e
->xclient
.data
.l
[0] & 1 << 11)
1286 h
= e
->xclient
.data
.l
[4];
1288 h
= client
->area
.height
;
1290 ob_debug("MOVERESIZE x %d %d y %d %d\n",
1291 e
->xclient
.data
.l
[0] & 1 << 8, x
,
1292 e
->xclient
.data
.l
[0] & 1 << 9, y
);
1293 client_convert_gravity(client
, grav
, &x
, &y
, w
, h
);
1294 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1295 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
);
1296 } else if (msgtype
== prop_atoms
.net_restack_window
) {
1297 if (e
->xclient
.data
.l
[0] != 2) {
1298 ob_debug_type(OB_DEBUG_APP_BUGS
,
1299 "_NET_RESTACK_WINDOW sent for window %s with "
1300 "invalid source indication %ld\n",
1301 client
->title
, e
->xclient
.data
.l
[0]);
1303 ObClient
*sibling
= NULL
;
1304 if (e
->xclient
.data
.l
[1]) {
1305 ObWindow
*win
= g_hash_table_lookup(window_map
,
1306 &e
->xclient
.data
.l
[1]);
1307 if (WINDOW_IS_CLIENT(win
) &&
1308 WINDOW_AS_CLIENT(win
) != client
)
1310 sibling
= WINDOW_AS_CLIENT(win
);
1312 if (sibling
== NULL
)
1313 ob_debug_type(OB_DEBUG_APP_BUGS
,
1314 "_NET_RESTACK_WINDOW sent for window %s "
1315 "with invalid sibling 0x%x\n",
1316 client
->title
, e
->xclient
.data
.l
[1]);
1318 if (e
->xclient
.data
.l
[2] == Below
||
1319 e
->xclient
.data
.l
[2] == BottomIf
||
1320 e
->xclient
.data
.l
[2] == Above
||
1321 e
->xclient
.data
.l
[2] == TopIf
||
1322 e
->xclient
.data
.l
[2] == Opposite
)
1324 /* just raise, don't activate */
1325 stacking_restack_request(client
, sibling
,
1326 e
->xclient
.data
.l
[2], FALSE
);
1327 /* send a synthetic ConfigureNotify, cuz this is supposed
1328 to be like a ConfigureRequest. */
1329 client_configure_full(client
, client
->area
.x
,
1332 client
->area
.height
,
1335 ob_debug_type(OB_DEBUG_APP_BUGS
,
1336 "_NET_RESTACK_WINDOW sent for window %s "
1337 "with invalid detail %d\n",
1338 client
->title
, e
->xclient
.data
.l
[2]);
1342 case PropertyNotify
:
1343 /* validate cuz we query stuff off the client here */
1344 if (!client_validate(client
)) break;
1346 /* compress changes to a single property into a single change */
1347 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1351 /* XXX: it would be nice to compress ALL changes to a property,
1352 not just changes in a row without other props between. */
1354 a
= ce
.xproperty
.atom
;
1355 b
= e
->xproperty
.atom
;
1359 if ((a
== prop_atoms
.net_wm_name
||
1360 a
== prop_atoms
.wm_name
||
1361 a
== prop_atoms
.net_wm_icon_name
||
1362 a
== prop_atoms
.wm_icon_name
)
1364 (b
== prop_atoms
.net_wm_name
||
1365 b
== prop_atoms
.wm_name
||
1366 b
== prop_atoms
.net_wm_icon_name
||
1367 b
== prop_atoms
.wm_icon_name
)) {
1370 if (a
== prop_atoms
.net_wm_icon
&&
1371 b
== prop_atoms
.net_wm_icon
)
1374 XPutBackEvent(ob_display
, &ce
);
1378 msgtype
= e
->xproperty
.atom
;
1379 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1380 client_update_normal_hints(client
);
1381 /* normal hints can make a window non-resizable */
1382 client_setup_decor_and_functions(client
);
1383 } else if (msgtype
== XA_WM_HINTS
) {
1384 client_update_wmhints(client
);
1385 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1386 client_update_transient_for(client
);
1387 client_get_type_and_transientness(client
);
1388 /* type may have changed, so update the layer */
1389 client_calc_layer(client
);
1390 client_setup_decor_and_functions(client
);
1391 } else if (msgtype
== prop_atoms
.net_wm_name
||
1392 msgtype
== prop_atoms
.wm_name
||
1393 msgtype
== prop_atoms
.net_wm_icon_name
||
1394 msgtype
== prop_atoms
.wm_icon_name
) {
1395 client_update_title(client
);
1396 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1397 client_update_protocols(client
);
1398 client_setup_decor_and_functions(client
);
1400 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1401 client_update_strut(client
);
1403 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1404 client_update_icons(client
);
1406 else if (msgtype
== prop_atoms
.net_wm_icon_geometry
) {
1407 client_update_icon_geometry(client
);
1409 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1410 client_update_user_time(client
);
1412 else if (msgtype
== prop_atoms
.net_wm_user_time_window
) {
1413 client_update_user_time_window(client
);
1416 else if (msgtype
== prop_atoms
.net_wm_sync_request_counter
) {
1417 client_update_sync_request_counter(client
);
1420 case ColormapNotify
:
1421 client_update_colormap(client
, e
->xcolormap
.colormap
);
1426 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1427 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1428 frame_adjust_shape(client
->frame
);
1434 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1438 if (e
->xbutton
.button
== 1)
1439 stacking_raise(DOCK_AS_WINDOW(s
));
1440 else if (e
->xbutton
.button
== 2)
1441 stacking_lower(DOCK_AS_WINDOW(s
));
1452 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1456 dock_app_drag(app
, &e
->xmotion
);
1459 if (app
->ignore_unmaps
) {
1460 app
->ignore_unmaps
--;
1463 dock_remove(app
, TRUE
);
1466 dock_remove(app
, FALSE
);
1468 case ReparentNotify
:
1469 dock_remove(app
, FALSE
);
1471 case ConfigureNotify
:
1472 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1477 static ObMenuFrame
* find_active_menu()
1480 ObMenuFrame
*ret
= NULL
;
1482 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1491 static ObMenuFrame
* find_active_or_last_menu()
1493 ObMenuFrame
*ret
= NULL
;
1495 ret
= find_active_menu();
1496 if (!ret
&& menu_frame_visible
)
1497 ret
= menu_frame_visible
->data
;
1501 static gboolean
event_handle_menu_keyboard(XEvent
*ev
)
1503 guint keycode
, state
;
1506 gboolean ret
= TRUE
;
1508 keycode
= ev
->xkey
.keycode
;
1509 state
= ev
->xkey
.state
;
1510 unikey
= translate_unichar(keycode
);
1512 frame
= find_active_or_last_menu();
1516 else if (keycode
== ob_keycode(OB_KEY_ESCAPE
) && state
== 0) {
1517 /* Escape closes the active menu */
1518 menu_frame_hide(frame
);
1521 else if (keycode
== ob_keycode(OB_KEY_RETURN
) && (state
== 0 ||
1522 state
== ControlMask
))
1524 /* Enter runs the active item or goes into the submenu.
1525 Control-Enter runs it without closing the menu. */
1527 menu_frame_select_next(frame
->child
);
1529 menu_entry_frame_execute(frame
->selected
, state
, ev
->xkey
.time
);
1532 else if (keycode
== ob_keycode(OB_KEY_LEFT
) && ev
->xkey
.state
== 0) {
1533 /* Left goes to the parent menu */
1534 menu_frame_select(frame
, NULL
, TRUE
);
1537 else if (keycode
== ob_keycode(OB_KEY_RIGHT
) && ev
->xkey
.state
== 0) {
1538 /* Right goes to the selected submenu */
1539 if (frame
->child
) menu_frame_select_next(frame
->child
);
1542 else if (keycode
== ob_keycode(OB_KEY_UP
) && state
== 0) {
1543 menu_frame_select_previous(frame
);
1546 else if (keycode
== ob_keycode(OB_KEY_DOWN
) && state
== 0) {
1547 menu_frame_select_next(frame
);
1550 /* keyboard accelerator shortcuts. */
1551 else if (ev
->xkey
.state
== 0 &&
1552 /* was it a valid key? */
1554 /* don't bother if the menu is empty. */
1559 ObMenuEntryFrame
*found
= NULL
;
1560 guint num_found
= 0;
1562 /* start after the selected one */
1563 start
= frame
->entries
;
1564 if (frame
->selected
) {
1565 for (it
= start
; frame
->selected
!= it
->data
; it
= g_list_next(it
))
1566 g_assert(it
!= NULL
); /* nothing was selected? */
1567 /* next with wraparound */
1568 start
= g_list_next(it
);
1569 if (start
== NULL
) start
= frame
->entries
;
1574 ObMenuEntryFrame
*e
= it
->data
;
1575 gunichar entrykey
= 0;
1577 if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
)
1578 entrykey
= e
->entry
->data
.normal
.shortcut
;
1579 else if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_SUBMENU
)
1580 entrykey
= e
->entry
->data
.submenu
.submenu
->shortcut
;
1582 if (unikey
== entrykey
) {
1583 if (found
== NULL
) found
= e
;
1587 /* next with wraparound */
1588 it
= g_list_next(it
);
1589 if (it
== NULL
) it
= frame
->entries
;
1590 } while (it
!= start
);
1593 if (found
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
&&
1596 menu_frame_select(frame
, found
, TRUE
);
1597 usleep(50000); /* highlight the item for a short bit so the
1598 user can see what happened */
1599 menu_entry_frame_execute(found
, state
, ev
->xkey
.time
);
1601 menu_frame_select(frame
, found
, TRUE
);
1603 menu_frame_select_next(frame
->child
);
1614 static gboolean
event_handle_menu(XEvent
*ev
)
1617 ObMenuEntryFrame
*e
;
1618 gboolean ret
= TRUE
;
1622 if ((ev
->xbutton
.button
< 4 || ev
->xbutton
.button
> 5)
1625 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1626 ev
->xbutton
.y_root
)))
1627 menu_entry_frame_execute(e
, ev
->xbutton
.state
,
1630 menu_frame_hide_all();
1634 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
))) {
1635 if (e
->ignore_enters
)
1638 menu_frame_select(e
->frame
, e
, FALSE
);
1642 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
)) &&
1643 (f
= find_active_menu()) && f
->selected
== e
&&
1644 e
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1646 menu_frame_select(e
->frame
, NULL
, FALSE
);
1649 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1650 ev
->xmotion
.y_root
)))
1651 menu_frame_select(e
->frame
, e
, FALSE
);
1654 ret
= event_handle_menu_keyboard(ev
);
1660 static void event_handle_user_input(ObClient
*client
, XEvent
*e
)
1662 g_assert(e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
1663 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
1664 e
->type
== KeyRelease
);
1666 if (menu_frame_visible
) {
1667 if (event_handle_menu(e
))
1668 /* don't use the event if the menu used it, but if the menu
1669 didn't use it and it's a keypress that is bound, it will
1670 close the menu and be used */
1674 /* if the keyboard interactive action uses the event then dont
1675 use it for bindings. likewise is moveresize uses the event. */
1676 if (!keyboard_process_interactive_grab(e
, &client
) &&
1677 !(moveresize_in_progress
&& moveresize_event(e
)))
1679 if (moveresize_in_progress
)
1680 /* make further actions work on the client being
1682 client
= moveresize_client
;
1684 menu_can_hide
= FALSE
;
1685 ob_main_loop_timeout_add(ob_main_loop
,
1686 config_menu_hide_delay
* 1000,
1687 menu_hide_delay_func
,
1688 NULL
, g_direct_equal
, NULL
);
1690 if (e
->type
== ButtonPress
||
1691 e
->type
== ButtonRelease
||
1692 e
->type
== MotionNotify
)
1694 /* the frame may not be "visible" but they can still click on it
1695 in the case where it is animating before disappearing */
1696 if (!client
|| !frame_iconify_animating(client
->frame
))
1697 mouse_event(client
, e
);
1698 } else if (e
->type
== KeyPress
) {
1699 keyboard_event((focus_cycle_target
? focus_cycle_target
:
1700 (client
? client
: focus_client
)), e
);
1705 static gboolean
menu_hide_delay_func(gpointer data
)
1707 menu_can_hide
= TRUE
;
1708 return FALSE
; /* no repeat */
1711 static void focus_delay_dest(gpointer data
)
1716 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
)
1718 const ObFocusDelayData
*f1
= d1
;
1719 return f1
->client
== d2
;
1722 static gboolean
focus_delay_func(gpointer data
)
1724 ObFocusDelayData
*d
= data
;
1725 Time old
= event_curtime
;
1727 event_curtime
= d
->time
;
1728 if (focus_client
!= d
->client
) {
1729 if (client_focus(d
->client
) && config_focus_raise
)
1730 stacking_raise(CLIENT_AS_WINDOW(d
->client
));
1732 event_curtime
= old
;
1733 return FALSE
; /* no repeat */
1736 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1738 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1742 void event_halt_focus_delay()
1744 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1747 void event_ignore_queued_enters()
1749 GSList
*saved
= NULL
, *it
;
1752 XSync(ob_display
, FALSE
);
1754 /* count the events */
1756 e
= g_new(XEvent
, 1);
1757 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1760 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1761 if (win
&& WINDOW_IS_CLIENT(win
))
1762 ++ignore_enter_focus
;
1764 saved
= g_slist_append(saved
, e
);
1770 /* put the events back */
1771 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1772 XPutBackEvent(ob_display
, it
->data
);
1775 g_slist_free(saved
);
1778 gboolean
event_time_after(Time t1
, Time t2
)
1780 g_assert(t1
!= CurrentTime
);
1781 g_assert(t2
!= CurrentTime
);
1784 Timestamp values wrap around (after about 49.7 days). The server, given
1785 its current time is represented by timestamp T, always interprets
1786 timestamps from clients by treating half of the timestamp space as being
1787 later in time than T.
1788 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1791 /* TIME_HALF is half of the number space of a Time type variable */
1792 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1794 if (t2
>= TIME_HALF
)
1795 /* t2 is in the second half so t1 might wrap around and be smaller than
1797 return t1
>= t2
|| t1
< (t2
+ TIME_HALF
);
1799 /* t2 is in the first half so t1 has to come after it */
1800 return t1
>= t2
&& t1
< (t2
+ TIME_HALF
);