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
;
103 static void ice_handler(gint fd
, gpointer conn
)
106 IceProcessMessages(conn
, NULL
, &b
);
109 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
110 IcePointer
*watch_data
)
115 fd
= IceConnectionNumber(conn
);
116 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
118 ob_main_loop_fd_remove(ob_main_loop
, fd
);
124 void event_startup(gboolean reconfig
)
126 if (reconfig
) return;
128 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
131 IceAddConnectionWatch(ice_watch
, NULL
);
134 client_add_destroy_notify(focus_delay_client_dest
, NULL
);
137 void event_shutdown(gboolean reconfig
)
139 if (reconfig
) return;
142 IceRemoveConnectionWatch(ice_watch
, NULL
);
145 client_remove_destroy_notify(focus_delay_client_dest
);
148 static Window
event_get_window(XEvent
*e
)
155 window
= RootWindow(ob_display
, ob_screen
);
158 window
= e
->xmap
.window
;
161 window
= e
->xunmap
.window
;
164 window
= e
->xdestroywindow
.window
;
166 case ConfigureRequest
:
167 window
= e
->xconfigurerequest
.window
;
169 case ConfigureNotify
:
170 window
= e
->xconfigure
.window
;
174 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
175 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
177 window
= ((XkbBellNotifyEvent
*)e
)->window
;
184 if (extensions_sync
&&
185 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
190 window
= e
->xany
.window
;
195 static void event_set_curtime(XEvent
*e
)
197 Time t
= CurrentTime
;
199 /* grab the lasttime and hack up the state */
215 t
= e
->xproperty
.time
;
219 t
= e
->xcrossing
.time
;
223 if (extensions_sync
&&
224 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
226 t
= ((XSyncAlarmNotifyEvent
*)e
)->time
;
229 /* if more event types are anticipated, get their timestamp
237 static void event_hack_mods(XEvent
*e
)
240 XkbStateRec xkb_state
;
246 e
->xbutton
.state
= modkeys_only_modifier_masks(e
->xbutton
.state
);
249 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
252 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
254 if (XkbGetState(ob_display
, XkbUseCoreKbd
, &xkb_state
) == Success
) {
255 e
->xkey
.state
= xkb_state
.compat_state
;
259 /* remove from the state the mask of the modifier key being released,
260 if it is a modifier key being released that is */
261 e
->xkey
.state
&= ~modkeys_keycode_to_mask(e
->xkey
.keycode
);
264 e
->xmotion
.state
= modkeys_only_modifier_masks(e
->xmotion
.state
);
265 /* compress events */
268 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
270 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
271 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
278 static gboolean
wanted_focusevent(XEvent
*e
, gboolean in_client_only
)
280 gint mode
= e
->xfocus
.mode
;
281 gint detail
= e
->xfocus
.detail
;
282 Window win
= e
->xany
.window
;
284 if (e
->type
== FocusIn
) {
285 /* These are ones we never want.. */
287 /* This means focus was given by a keyboard/mouse grab. */
288 if (mode
== NotifyGrab
)
290 /* This means focus was given back from a keyboard/mouse grab. */
291 if (mode
== NotifyUngrab
)
294 /* These are the ones we want.. */
296 if (win
== RootWindow(ob_display
, ob_screen
)) {
297 /* If looking for a focus in on a client, then always return
298 FALSE for focus in's to the root window */
301 /* This means focus reverted off of a client */
302 else if (detail
== NotifyPointerRoot
||
303 detail
== NotifyDetailNone
||
304 detail
== NotifyInferior
)
310 /* This means focus moved from the root window to a client */
311 if (detail
== NotifyVirtual
)
313 /* This means focus moved from one client to another */
314 if (detail
== NotifyNonlinearVirtual
)
316 /* This means focus moved to the frame window */
317 if (detail
== NotifyInferior
&& !in_client_only
)
323 g_assert(e
->type
== FocusOut
);
325 /* These are ones we never want.. */
327 /* This means focus was taken by a keyboard/mouse grab. */
328 if (mode
== NotifyGrab
)
331 /* Focus left the root window revertedto state */
332 if (win
== RootWindow(ob_display
, ob_screen
))
335 /* These are the ones we want.. */
337 /* This means focus moved from a client to the root window */
338 if (detail
== NotifyVirtual
)
340 /* This means focus moved from one client to another */
341 if (detail
== NotifyNonlinearVirtual
)
343 /* This means focus had moved to our frame window and now moved off */
344 if (detail
== NotifyNonlinear
)
352 static Bool
event_look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
354 return e
->type
== FocusIn
&& wanted_focusevent(e
, FALSE
);
357 Bool
event_look_for_focusin_client(Display
*d
, XEvent
*e
, XPointer arg
)
359 return e
->type
== FocusIn
&& wanted_focusevent(e
, TRUE
) &&
360 e
->xfocus
.window
!= screen_support_win
;
363 static void print_focusevent(XEvent
*e
)
365 gint mode
= e
->xfocus
.mode
;
366 gint detail
= e
->xfocus
.detail
;
367 Window win
= e
->xany
.window
;
368 const gchar
*modestr
, *detailstr
;
371 case NotifyNormal
: modestr
="NotifyNormal"; break;
372 case NotifyGrab
: modestr
="NotifyGrab"; break;
373 case NotifyUngrab
: modestr
="NotifyUngrab"; break;
374 case NotifyWhileGrabbed
: modestr
="NotifyWhileGrabbed"; break;
377 case NotifyAncestor
: detailstr
="NotifyAncestor"; break;
378 case NotifyVirtual
: detailstr
="NotifyVirtual"; break;
379 case NotifyInferior
: detailstr
="NotifyInferior"; break;
380 case NotifyNonlinear
: detailstr
="NotifyNonlinear"; break;
381 case NotifyNonlinearVirtual
: detailstr
="NotifyNonlinearVirtual"; break;
382 case NotifyPointer
: detailstr
="NotifyPointer"; break;
383 case NotifyPointerRoot
: detailstr
="NotifyPointerRoot"; break;
384 case NotifyDetailNone
: detailstr
="NotifyDetailNone"; break;
389 ob_debug_type(OB_DEBUG_FOCUS
, "Focus%s 0x%x mode=%s detail=%s\n",
390 (e
->xfocus
.type
== FocusIn
? "In" : "Out"),
396 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
401 if (!wanted_focusevent(e
, FALSE
))
406 if (!wanted_focusevent(e
, FALSE
))
413 static void event_process(const XEvent
*ec
, gpointer data
)
416 ObClient
*client
= NULL
;
418 ObDockApp
*dockapp
= NULL
;
419 ObWindow
*obwin
= NULL
;
420 GSList
*timewinclients
= NULL
;
422 ObEventData
*ed
= data
;
424 /* make a copy we can mangle */
428 window
= event_get_window(e
);
429 if (e
->type
!= PropertyNotify
||
430 !(timewinclients
= propwin_get_clients(window
,
431 OB_PROPWIN_USER_TIME
)))
432 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
433 switch (obwin
->type
) {
435 dock
= WINDOW_AS_DOCK(obwin
);
438 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
441 client
= WINDOW_AS_CLIENT(obwin
);
444 case Window_Internal
:
445 /* not to be used for events */
446 g_assert_not_reached();
451 event_set_curtime(e
);
453 if (event_ignore(e
, client
)) {
460 /* deal with it in the kernel */
462 if (menu_frame_visible
&&
463 (e
->type
== EnterNotify
|| e
->type
== LeaveNotify
))
465 /* crossing events for menu */
466 event_handle_menu(e
);
467 } else if (e
->type
== FocusIn
) {
468 if (e
->xfocus
.detail
== NotifyPointerRoot
||
469 e
->xfocus
.detail
== NotifyDetailNone
||
470 e
->xfocus
.detail
== NotifyInferior
)
473 ob_debug_type(OB_DEBUG_FOCUS
,
474 "Focus went to pointer root/none or to our frame "
477 /* If another FocusIn is in the queue then don't fallback yet. This
478 fixes the fun case of:
479 window map -> send focusin
480 window unmap -> get focusout
481 window map -> send focusin
482 get first focus out -> fall back to something (new window
483 hasn't received focus yet, so something else) -> send focusin
484 which means the "something else" is the last thing to get a
485 focusin sent to it, so the new window doesn't end up with focus.
487 But if the other focus in is something like PointerRoot then we
488 still want to fall back.
490 if (XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin_client
,
493 XPutBackEvent(ob_display
, &ce
);
494 ob_debug_type(OB_DEBUG_FOCUS
,
495 " but another FocusIn is coming\n");
497 /* Focus has been reverted to the root window, nothing, or to
500 FocusOut events come after UnmapNotify, so we don't need to
501 worry about focusing an invalid window
504 /* In this case we know focus is in our screen */
505 if (e
->xfocus
.detail
== NotifyInferior
)
506 focus_left_screen
= FALSE
;
508 if (!focus_left_screen
)
509 focus_fallback(TRUE
);
511 } else if (client
&& client
!= focus_client
) {
512 focus_left_screen
= FALSE
;
513 frame_adjust_focus(client
->frame
, TRUE
);
514 focus_set_client(client
);
515 client_calc_layer(client
);
516 client_bring_helper_windows(client
);
518 } else if (e
->type
== FocusOut
) {
519 gboolean nomove
= FALSE
;
522 /* Look for the followup FocusIn */
523 if (!XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin
, NULL
)) {
524 /* There is no FocusIn, this means focus went to a window that
525 is not being managed, or a window on another screen. */
529 xerror_set_ignore(TRUE
);
530 if (XGetInputFocus(ob_display
, &win
, &i
) != 0 &&
531 XGetGeometry(ob_display
, win
, &root
, &i
,&i
,&u
,&u
,&u
,&u
) != 0 &&
532 root
!= RootWindow(ob_display
, ob_screen
))
534 ob_debug_type(OB_DEBUG_FOCUS
,
535 "Focus went to another screen !\n");
536 focus_left_screen
= TRUE
;
539 ob_debug_type(OB_DEBUG_FOCUS
,
540 "Focus went to a black hole !\n");
541 xerror_set_ignore(FALSE
);
542 /* nothing is focused */
543 focus_set_client(NULL
);
544 } else if (ce
.xany
.window
== e
->xany
.window
) {
545 ob_debug_type(OB_DEBUG_FOCUS
, "Focus didn't go anywhere\n");
546 /* If focus didn't actually move anywhere, there is nothing to do*/
549 /* Focus did move, so process the FocusIn event */
550 ObEventData ed
= { .ignored
= FALSE
};
551 event_process(&ce
, &ed
);
553 /* The FocusIn was ignored, this means it was on a window
554 that isn't a client. */
555 ob_debug_type(OB_DEBUG_FOCUS
,
556 "Focus went to an unmanaged window 0x%x !\n",
558 focus_fallback(TRUE
);
562 if (client
&& !nomove
) {
563 frame_adjust_focus(client
->frame
, FALSE
);
564 /* focus_set_client has already been called for sure */
565 client_calc_layer(client
);
567 } else if (timewinclients
)
568 event_handle_user_time_window_clients(timewinclients
, e
);
570 event_handle_client(client
, e
);
572 event_handle_dockapp(dockapp
, e
);
574 event_handle_dock(dock
, e
);
575 else if (window
== RootWindow(ob_display
, ob_screen
))
576 event_handle_root(e
);
577 else if (e
->type
== MapRequest
)
578 client_manage(window
);
579 else if (e
->type
== ClientMessage
) {
580 /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
581 windows that are not managed yet. */
582 if (e
->xclient
.message_type
== prop_atoms
.net_request_frame_extents
) {
583 /* Pretend to manage the client, getting information used to
584 determine its decorations */
585 ObClient
*c
= client_fake_manage(e
->xclient
.window
);
588 /* set the frame extents on the window */
589 vals
[0] = c
->frame
->size
.left
;
590 vals
[1] = c
->frame
->size
.right
;
591 vals
[2] = c
->frame
->size
.top
;
592 vals
[3] = c
->frame
->size
.bottom
;
593 PROP_SETA32(e
->xclient
.window
, net_frame_extents
,
596 /* Free the pretend client */
597 client_fake_unmanage(c
);
600 else if (e
->type
== ConfigureRequest
) {
601 /* unhandled configure requests must be used to configure the
605 xwc
.x
= e
->xconfigurerequest
.x
;
606 xwc
.y
= e
->xconfigurerequest
.y
;
607 xwc
.width
= e
->xconfigurerequest
.width
;
608 xwc
.height
= e
->xconfigurerequest
.height
;
609 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
610 xwc
.sibling
= e
->xconfigurerequest
.above
;
611 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
613 /* we are not to be held responsible if someone sends us an
615 xerror_set_ignore(TRUE
);
616 XConfigureWindow(ob_display
, window
,
617 e
->xconfigurerequest
.value_mask
, &xwc
);
618 xerror_set_ignore(FALSE
);
621 else if (extensions_sync
&&
622 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
624 XSyncAlarmNotifyEvent
*se
= (XSyncAlarmNotifyEvent
*)e
;
625 if (se
->alarm
== moveresize_alarm
&& moveresize_in_progress
)
630 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
631 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
632 e
->type
== KeyRelease
)
634 event_handle_user_input(client
, e
);
637 /* if something happens and it's not from an XEvent, then we don't know
639 event_curtime
= CurrentTime
;
642 static void event_handle_root(XEvent
*e
)
648 ob_debug("Another WM has requested to replace us. Exiting.\n");
653 if (e
->xclient
.format
!= 32) break;
655 msgtype
= e
->xclient
.message_type
;
656 if (msgtype
== prop_atoms
.net_current_desktop
) {
657 guint d
= e
->xclient
.data
.l
[0];
658 if (d
< screen_num_desktops
) {
659 event_curtime
= e
->xclient
.data
.l
[1];
660 if (event_curtime
== 0)
661 ob_debug_type(OB_DEBUG_APP_BUGS
,
662 "_NET_CURRENT_DESKTOP message is missing "
664 screen_set_desktop(d
, TRUE
);
666 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
667 guint d
= e
->xclient
.data
.l
[0];
669 screen_set_num_desktops(d
);
670 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
671 screen_show_desktop(e
->xclient
.data
.l
[0] != 0, NULL
);
672 } else if (msgtype
== prop_atoms
.openbox_control
) {
673 if (e
->xclient
.data
.l
[0] == 1)
675 else if (e
->xclient
.data
.l
[0] == 2)
680 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
681 screen_update_desktop_names();
682 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
683 screen_update_layout();
685 case ConfigureNotify
:
687 XRRUpdateConfiguration(e
);
696 void event_enter_client(ObClient
*client
)
698 g_assert(config_focus_follow
);
700 if (client_enter_focusable(client
) && client_can_focus(client
)) {
701 if (config_focus_delay
) {
702 ObFocusDelayData
*data
;
704 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
706 data
= g_new(ObFocusDelayData
, 1);
707 data
->client
= client
;
708 data
->time
= event_curtime
;
710 ob_main_loop_timeout_add(ob_main_loop
,
713 data
, focus_delay_cmp
, focus_delay_dest
);
715 ObFocusDelayData data
;
716 data
.client
= client
;
717 data
.time
= event_curtime
;
718 focus_delay_func(&data
);
723 static void event_handle_user_time_window_clients(GSList
*l
, XEvent
*e
)
725 g_assert(e
->type
== PropertyNotify
);
726 if (e
->xproperty
.atom
== prop_atoms
.net_wm_user_time
) {
727 for (; l
; l
= g_slist_next(l
))
728 client_update_user_time(l
->data
);
732 static void event_handle_client(ObClient
*client
, XEvent
*e
)
737 static gint px
= -1, py
= -1;
742 /* save where the press occured for the first button pressed */
744 pb
= e
->xbutton
.button
;
749 /* Wheel buttons don't draw because they are an instant click, so it
750 is a waste of resources to go drawing it.
751 if the user is doing an intereactive thing, or has a menu open then
752 the mouse is grabbed (possibly) and if we get these events we don't
753 want to deal with them
755 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5) &&
756 !keyboard_interactively_grabbed() &&
759 /* use where the press occured */
760 con
= frame_context(client
, e
->xbutton
.window
, px
, py
);
761 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
763 if (e
->type
== ButtonRelease
&& e
->xbutton
.button
== pb
)
764 pb
= 0, px
= py
= -1;
767 case OB_FRAME_CONTEXT_MAXIMIZE
:
768 client
->frame
->max_press
= (e
->type
== ButtonPress
);
769 framerender_frame(client
->frame
);
771 case OB_FRAME_CONTEXT_CLOSE
:
772 client
->frame
->close_press
= (e
->type
== ButtonPress
);
773 framerender_frame(client
->frame
);
775 case OB_FRAME_CONTEXT_ICONIFY
:
776 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
777 framerender_frame(client
->frame
);
779 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
780 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
781 framerender_frame(client
->frame
);
783 case OB_FRAME_CONTEXT_SHADE
:
784 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
785 framerender_frame(client
->frame
);
788 /* nothing changes with clicks for any other contexts */
794 con
= frame_context(client
, e
->xmotion
.window
,
795 e
->xmotion
.x
, e
->xmotion
.y
);
797 case OB_FRAME_CONTEXT_TITLEBAR
:
798 /* we've left the button area inside the titlebar */
799 if (client
->frame
->max_hover
|| client
->frame
->desk_hover
||
800 client
->frame
->shade_hover
|| client
->frame
->iconify_hover
||
801 client
->frame
->close_hover
)
803 client
->frame
->max_hover
= FALSE
;
804 client
->frame
->desk_hover
= FALSE
;
805 client
->frame
->shade_hover
= FALSE
;
806 client
->frame
->iconify_hover
= FALSE
;
807 client
->frame
->close_hover
= FALSE
;
808 frame_adjust_state(client
->frame
);
811 case OB_FRAME_CONTEXT_MAXIMIZE
:
812 if (!client
->frame
->max_hover
) {
813 client
->frame
->max_hover
= TRUE
;
814 frame_adjust_state(client
->frame
);
817 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
818 if (!client
->frame
->desk_hover
) {
819 client
->frame
->desk_hover
= TRUE
;
820 frame_adjust_state(client
->frame
);
823 case OB_FRAME_CONTEXT_SHADE
:
824 if (!client
->frame
->shade_hover
) {
825 client
->frame
->shade_hover
= TRUE
;
826 frame_adjust_state(client
->frame
);
829 case OB_FRAME_CONTEXT_ICONIFY
:
830 if (!client
->frame
->iconify_hover
) {
831 client
->frame
->iconify_hover
= TRUE
;
832 frame_adjust_state(client
->frame
);
835 case OB_FRAME_CONTEXT_CLOSE
:
836 if (!client
->frame
->close_hover
) {
837 client
->frame
->close_hover
= TRUE
;
838 frame_adjust_state(client
->frame
);
846 con
= frame_context(client
, e
->xcrossing
.window
,
847 e
->xcrossing
.x
, e
->xcrossing
.y
);
849 case OB_FRAME_CONTEXT_MAXIMIZE
:
850 client
->frame
->max_hover
= FALSE
;
851 frame_adjust_state(client
->frame
);
853 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
854 client
->frame
->desk_hover
= FALSE
;
855 frame_adjust_state(client
->frame
);
857 case OB_FRAME_CONTEXT_SHADE
:
858 client
->frame
->shade_hover
= FALSE
;
859 frame_adjust_state(client
->frame
);
861 case OB_FRAME_CONTEXT_ICONIFY
:
862 client
->frame
->iconify_hover
= FALSE
;
863 frame_adjust_state(client
->frame
);
865 case OB_FRAME_CONTEXT_CLOSE
:
866 client
->frame
->close_hover
= FALSE
;
867 frame_adjust_state(client
->frame
);
869 case OB_FRAME_CONTEXT_FRAME
:
870 /* When the mouse leaves an animating window, don't use the
871 corresponding enter events. Pretend like the animating window
872 doesn't even exist..! */
873 if (frame_iconify_animating(client
->frame
))
874 event_ignore_queued_enters();
876 ob_debug_type(OB_DEBUG_FOCUS
,
877 "%sNotify mode %d detail %d on %lx\n",
878 (e
->type
== EnterNotify
? "Enter" : "Leave"),
880 e
->xcrossing
.detail
, (client
?client
->window
:0));
881 if (keyboard_interactively_grabbed())
883 if (config_focus_follow
&& config_focus_delay
&&
884 /* leave inferior events can happen when the mouse goes onto
885 the window's border and then into the window before the
887 e
->xcrossing
.detail
!= NotifyInferior
)
889 ob_main_loop_timeout_remove_data(ob_main_loop
,
900 gboolean nofocus
= FALSE
;
902 if (ignore_enter_focus
) {
903 ignore_enter_focus
--;
907 con
= frame_context(client
, e
->xcrossing
.window
,
908 e
->xcrossing
.x
, e
->xcrossing
.y
);
910 case OB_FRAME_CONTEXT_MAXIMIZE
:
911 client
->frame
->max_hover
= TRUE
;
912 frame_adjust_state(client
->frame
);
914 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
915 client
->frame
->desk_hover
= TRUE
;
916 frame_adjust_state(client
->frame
);
918 case OB_FRAME_CONTEXT_SHADE
:
919 client
->frame
->shade_hover
= TRUE
;
920 frame_adjust_state(client
->frame
);
922 case OB_FRAME_CONTEXT_ICONIFY
:
923 client
->frame
->iconify_hover
= TRUE
;
924 frame_adjust_state(client
->frame
);
926 case OB_FRAME_CONTEXT_CLOSE
:
927 client
->frame
->close_hover
= TRUE
;
928 frame_adjust_state(client
->frame
);
930 case OB_FRAME_CONTEXT_FRAME
:
931 if (keyboard_interactively_grabbed())
933 if (e
->xcrossing
.mode
== NotifyGrab
||
934 e
->xcrossing
.mode
== NotifyUngrab
||
935 /*ignore enters when we're already in the window */
936 e
->xcrossing
.detail
== NotifyInferior
)
938 ob_debug_type(OB_DEBUG_FOCUS
,
939 "%sNotify mode %d detail %d on %lx IGNORED\n",
940 (e
->type
== EnterNotify
? "Enter" : "Leave"),
942 e
->xcrossing
.detail
, client
?client
->window
:0);
944 ob_debug_type(OB_DEBUG_FOCUS
,
945 "%sNotify mode %d detail %d on %lx, "
946 "focusing window: %d\n",
947 (e
->type
== EnterNotify
? "Enter" : "Leave"),
949 e
->xcrossing
.detail
, (client
?client
->window
:0),
951 if (!nofocus
&& config_focus_follow
)
952 event_enter_client(client
);
960 case ConfigureRequest
:
962 /* dont compress these unless you're going to watch for property
963 notifies in between (these can change what the configure would
965 also you can't compress stacking events
970 /* if nothing is changed, then a configurenotify is needed */
971 gboolean config
= TRUE
;
975 w
= client
->area
.width
;
976 h
= client
->area
.height
;
978 ob_debug("ConfigureRequest desktop %d wmstate %d visibile %d\n",
979 screen_desktop
, client
->wmstate
, client
->frame
->visible
);
981 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
982 if (client
->border_width
!= e
->xconfigurerequest
.border_width
) {
983 client
->border_width
= e
->xconfigurerequest
.border_width
;
984 /* if only the border width is changing, then it's not needed*/
989 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
990 ObClient
*sibling
= NULL
;
992 /* get the sibling */
993 if (e
->xconfigurerequest
.value_mask
& CWSibling
) {
995 win
= g_hash_table_lookup(window_map
,
996 &e
->xconfigurerequest
.above
);
997 if (WINDOW_IS_CLIENT(win
) && WINDOW_AS_CLIENT(win
) != client
)
998 sibling
= WINDOW_AS_CLIENT(win
);
1001 /* activate it rather than just focus it */
1002 stacking_restack_request(client
, sibling
,
1003 e
->xconfigurerequest
.detail
, TRUE
);
1005 /* if a stacking change is requested then it is needed */
1009 /* don't allow clients to move shaded windows (fvwm does this) */
1010 if (client
->shaded
&& (e
->xconfigurerequest
.value_mask
& CWX
||
1011 e
->xconfigurerequest
.value_mask
& CWY
))
1013 e
->xconfigurerequest
.value_mask
&= ~CWX
;
1014 e
->xconfigurerequest
.value_mask
&= ~CWY
;
1016 /* if the client tried to move and we aren't letting it then a
1017 synthetic event is needed */
1021 if (e
->xconfigurerequest
.value_mask
& CWX
||
1022 e
->xconfigurerequest
.value_mask
& CWY
||
1023 e
->xconfigurerequest
.value_mask
& CWWidth
||
1024 e
->xconfigurerequest
.value_mask
& CWHeight
)
1026 if (e
->xconfigurerequest
.value_mask
& CWX
)
1027 x
= e
->xconfigurerequest
.x
;
1028 if (e
->xconfigurerequest
.value_mask
& CWY
)
1029 y
= e
->xconfigurerequest
.y
;
1030 if (e
->xconfigurerequest
.value_mask
& CWWidth
)
1031 w
= e
->xconfigurerequest
.width
;
1032 if (e
->xconfigurerequest
.value_mask
& CWHeight
)
1033 h
= e
->xconfigurerequest
.height
;
1035 /* if a new position or size is requested, then a configure is
1040 ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d\n",
1041 e
->xconfigurerequest
.value_mask
& CWX
, x
,
1042 e
->xconfigurerequest
.value_mask
& CWY
, y
,
1043 e
->xconfigurerequest
.value_mask
& CWWidth
, w
,
1044 e
->xconfigurerequest
.value_mask
& CWHeight
, h
);
1046 /* check for broken apps moving to their root position
1048 XXX remove this some day...that would be nice. right now all
1049 kde apps do this when they try activate themselves on another
1050 desktop. eg. open amarok window on desktop 1, switch to desktop
1051 2, click amarok tray icon. it will move by its decoration size.
1053 if (x
!= client
->area
.x
&&
1054 x
== (client
->frame
->area
.x
+ client
->frame
->size
.left
-
1055 (gint
)client
->border_width
) &&
1056 y
!= client
->area
.y
&&
1057 y
== (client
->frame
->area
.y
+ client
->frame
->size
.top
-
1058 (gint
)client
->border_width
))
1060 ob_debug_type(OB_DEBUG_APP_BUGS
,
1061 "Application %s is trying to move via "
1062 "ConfigureRequest to it's root window position "
1063 "but it is not using StaticGravity\n",
1071 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1072 client_configure_full(client
, x
, y
, w
, h
, FALSE
, TRUE
);
1077 if (client
->ignore_unmaps
) {
1078 client
->ignore_unmaps
--;
1081 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
1082 "ignores left %d\n",
1083 client
->window
, e
->xunmap
.event
, e
->xunmap
.from_configure
,
1084 client
->ignore_unmaps
);
1085 client_unmanage(client
);
1088 ob_debug("DestroyNotify for window 0x%x\n", client
->window
);
1089 client_unmanage(client
);
1091 case ReparentNotify
:
1092 /* this is when the client is first taken captive in the frame */
1093 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
1096 This event is quite rare and is usually handled in unmapHandler.
1097 However, if the window is unmapped when the reparent event occurs,
1098 the window manager never sees it because an unmap event is not sent
1099 to an already unmapped window.
1102 /* we don't want the reparent event, put it back on the stack for the
1103 X server to deal with after we unmanage the window */
1104 XPutBackEvent(ob_display
, e
);
1106 ob_debug("ReparentNotify for window 0x%x\n", client
->window
);
1107 client_unmanage(client
);
1110 ob_debug("MapRequest for 0x%lx\n", client
->window
);
1111 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
1112 does, we don't want it!
1113 it can happen now when the window is on
1114 another desktop, but we still don't
1116 client_activate(client
, FALSE
, TRUE
);
1119 /* validate cuz we query stuff off the client here */
1120 if (!client_validate(client
)) break;
1122 if (e
->xclient
.format
!= 32) return;
1124 msgtype
= e
->xclient
.message_type
;
1125 if (msgtype
== prop_atoms
.wm_change_state
) {
1126 /* compress changes into a single change */
1127 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1129 /* XXX: it would be nice to compress ALL messages of a
1130 type, not just messages in a row without other
1131 message types between. */
1132 if (ce
.xclient
.message_type
!= msgtype
) {
1133 XPutBackEvent(ob_display
, &ce
);
1136 e
->xclient
= ce
.xclient
;
1138 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
1139 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
1140 /* compress changes into a single change */
1141 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1143 /* XXX: it would be nice to compress ALL messages of a
1144 type, not just messages in a row without other
1145 message types between. */
1146 if (ce
.xclient
.message_type
!= msgtype
) {
1147 XPutBackEvent(ob_display
, &ce
);
1150 e
->xclient
= ce
.xclient
;
1152 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
1153 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
1154 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
1156 } else if (msgtype
== prop_atoms
.net_wm_state
) {
1157 /* can't compress these */
1158 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1159 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
1160 e
->xclient
.data
.l
[0] == 1 ? "Add" :
1161 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1162 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
1164 client_set_state(client
, e
->xclient
.data
.l
[0],
1165 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
1166 } else if (msgtype
== prop_atoms
.net_close_window
) {
1167 ob_debug("net_close_window for 0x%lx\n", client
->window
);
1168 client_close(client
);
1169 } else if (msgtype
== prop_atoms
.net_active_window
) {
1170 ob_debug("net_active_window for 0x%lx source=%s\n",
1172 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
1173 (e
->xclient
.data
.l
[0] == 1 ? "application" :
1174 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
1175 /* XXX make use of data.l[2] !? */
1176 event_curtime
= e
->xclient
.data
.l
[1];
1177 if (event_curtime
== 0)
1178 ob_debug_type(OB_DEBUG_APP_BUGS
,
1179 "_NET_ACTIVE_WINDOW message for window %s is "
1180 "missing a timestamp\n", client
->title
);
1181 client_activate(client
, FALSE
,
1182 (e
->xclient
.data
.l
[0] == 0 ||
1183 e
->xclient
.data
.l
[0] == 2));
1184 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
1185 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1186 client
->window
, e
->xclient
.data
.l
[2]);
1187 if ((Atom
)e
->xclient
.data
.l
[2] ==
1188 prop_atoms
.net_wm_moveresize_size_topleft
||
1189 (Atom
)e
->xclient
.data
.l
[2] ==
1190 prop_atoms
.net_wm_moveresize_size_top
||
1191 (Atom
)e
->xclient
.data
.l
[2] ==
1192 prop_atoms
.net_wm_moveresize_size_topright
||
1193 (Atom
)e
->xclient
.data
.l
[2] ==
1194 prop_atoms
.net_wm_moveresize_size_right
||
1195 (Atom
)e
->xclient
.data
.l
[2] ==
1196 prop_atoms
.net_wm_moveresize_size_right
||
1197 (Atom
)e
->xclient
.data
.l
[2] ==
1198 prop_atoms
.net_wm_moveresize_size_bottomright
||
1199 (Atom
)e
->xclient
.data
.l
[2] ==
1200 prop_atoms
.net_wm_moveresize_size_bottom
||
1201 (Atom
)e
->xclient
.data
.l
[2] ==
1202 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1203 (Atom
)e
->xclient
.data
.l
[2] ==
1204 prop_atoms
.net_wm_moveresize_size_left
||
1205 (Atom
)e
->xclient
.data
.l
[2] ==
1206 prop_atoms
.net_wm_moveresize_move
||
1207 (Atom
)e
->xclient
.data
.l
[2] ==
1208 prop_atoms
.net_wm_moveresize_size_keyboard
||
1209 (Atom
)e
->xclient
.data
.l
[2] ==
1210 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1212 moveresize_start(client
, e
->xclient
.data
.l
[0],
1213 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1214 e
->xclient
.data
.l
[2]);
1216 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1217 prop_atoms
.net_wm_moveresize_cancel
)
1218 moveresize_end(TRUE
);
1219 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1220 gint grav
, x
, y
, w
, h
;
1222 if (e
->xclient
.data
.l
[0] & 0xff)
1223 grav
= e
->xclient
.data
.l
[0] & 0xff;
1225 grav
= client
->gravity
;
1227 if (e
->xclient
.data
.l
[0] & 1 << 8)
1228 x
= e
->xclient
.data
.l
[1];
1231 if (e
->xclient
.data
.l
[0] & 1 << 9)
1232 y
= e
->xclient
.data
.l
[2];
1235 if (e
->xclient
.data
.l
[0] & 1 << 10)
1236 w
= e
->xclient
.data
.l
[3];
1238 w
= client
->area
.width
;
1239 if (e
->xclient
.data
.l
[0] & 1 << 11)
1240 h
= e
->xclient
.data
.l
[4];
1242 h
= client
->area
.height
;
1244 ob_debug("MOVERESIZE x %d %d y %d %d\n",
1245 e
->xclient
.data
.l
[0] & 1 << 8, x
,
1246 e
->xclient
.data
.l
[0] & 1 << 9, y
);
1247 client_convert_gravity(client
, grav
, &x
, &y
, w
, h
);
1248 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1249 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
);
1250 } else if (msgtype
== prop_atoms
.net_restack_window
) {
1251 if (e
->xclient
.data
.l
[0] != 2) {
1252 ob_debug_type(OB_DEBUG_APP_BUGS
,
1253 "_NET_RESTACK_WINDOW sent for window %s with "
1254 "invalid source indication %ld\n",
1255 client
->title
, e
->xclient
.data
.l
[0]);
1257 ObClient
*sibling
= NULL
;
1258 if (e
->xclient
.data
.l
[1]) {
1259 ObWindow
*win
= g_hash_table_lookup(window_map
,
1260 &e
->xclient
.data
.l
[1]);
1261 if (WINDOW_IS_CLIENT(win
) &&
1262 WINDOW_AS_CLIENT(win
) != client
)
1264 sibling
= WINDOW_AS_CLIENT(win
);
1266 if (sibling
== NULL
)
1267 ob_debug_type(OB_DEBUG_APP_BUGS
,
1268 "_NET_RESTACK_WINDOW sent for window %s "
1269 "with invalid sibling 0x%x\n",
1270 client
->title
, e
->xclient
.data
.l
[1]);
1272 if (e
->xclient
.data
.l
[2] == Below
||
1273 e
->xclient
.data
.l
[2] == BottomIf
||
1274 e
->xclient
.data
.l
[2] == Above
||
1275 e
->xclient
.data
.l
[2] == TopIf
||
1276 e
->xclient
.data
.l
[2] == Opposite
)
1278 /* just raise, don't activate */
1279 stacking_restack_request(client
, sibling
,
1280 e
->xclient
.data
.l
[2], FALSE
);
1281 /* send a synthetic ConfigureNotify, cuz this is supposed
1282 to be like a ConfigureRequest. */
1283 client_configure_full(client
, client
->area
.x
,
1286 client
->area
.height
,
1289 ob_debug_type(OB_DEBUG_APP_BUGS
,
1290 "_NET_RESTACK_WINDOW sent for window %s "
1291 "with invalid detail %d\n",
1292 client
->title
, e
->xclient
.data
.l
[2]);
1296 case PropertyNotify
:
1297 /* validate cuz we query stuff off the client here */
1298 if (!client_validate(client
)) break;
1300 /* compress changes to a single property into a single change */
1301 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1305 /* XXX: it would be nice to compress ALL changes to a property,
1306 not just changes in a row without other props between. */
1308 a
= ce
.xproperty
.atom
;
1309 b
= e
->xproperty
.atom
;
1313 if ((a
== prop_atoms
.net_wm_name
||
1314 a
== prop_atoms
.wm_name
||
1315 a
== prop_atoms
.net_wm_icon_name
||
1316 a
== prop_atoms
.wm_icon_name
)
1318 (b
== prop_atoms
.net_wm_name
||
1319 b
== prop_atoms
.wm_name
||
1320 b
== prop_atoms
.net_wm_icon_name
||
1321 b
== prop_atoms
.wm_icon_name
)) {
1324 if (a
== prop_atoms
.net_wm_icon
&&
1325 b
== prop_atoms
.net_wm_icon
)
1328 XPutBackEvent(ob_display
, &ce
);
1332 msgtype
= e
->xproperty
.atom
;
1333 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1334 client_update_normal_hints(client
);
1335 /* normal hints can make a window non-resizable */
1336 client_setup_decor_and_functions(client
);
1337 } else if (msgtype
== XA_WM_HINTS
) {
1338 client_update_wmhints(client
);
1339 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1340 client_update_transient_for(client
);
1341 client_get_type_and_transientness(client
);
1342 /* type may have changed, so update the layer */
1343 client_calc_layer(client
);
1344 client_setup_decor_and_functions(client
);
1345 } else if (msgtype
== prop_atoms
.net_wm_name
||
1346 msgtype
== prop_atoms
.wm_name
||
1347 msgtype
== prop_atoms
.net_wm_icon_name
||
1348 msgtype
== prop_atoms
.wm_icon_name
) {
1349 client_update_title(client
);
1350 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1351 client_update_protocols(client
);
1352 client_setup_decor_and_functions(client
);
1354 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1355 client_update_strut(client
);
1357 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1358 client_update_icons(client
);
1360 else if (msgtype
== prop_atoms
.net_wm_icon_geometry
) {
1361 client_update_icon_geometry(client
);
1363 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1364 client_update_user_time(client
);
1366 else if (msgtype
== prop_atoms
.net_wm_user_time_window
) {
1367 client_update_user_time_window(client
);
1370 else if (msgtype
== prop_atoms
.net_wm_sync_request_counter
) {
1371 client_update_sync_request_counter(client
);
1374 case ColormapNotify
:
1375 client_update_colormap(client
, e
->xcolormap
.colormap
);
1380 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1381 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1382 frame_adjust_shape(client
->frame
);
1388 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1392 if (e
->xbutton
.button
== 1)
1393 stacking_raise(DOCK_AS_WINDOW(s
));
1394 else if (e
->xbutton
.button
== 2)
1395 stacking_lower(DOCK_AS_WINDOW(s
));
1406 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1410 dock_app_drag(app
, &e
->xmotion
);
1413 if (app
->ignore_unmaps
) {
1414 app
->ignore_unmaps
--;
1417 dock_remove(app
, TRUE
);
1420 dock_remove(app
, FALSE
);
1422 case ReparentNotify
:
1423 dock_remove(app
, FALSE
);
1425 case ConfigureNotify
:
1426 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1431 static ObMenuFrame
* find_active_menu()
1434 ObMenuFrame
*ret
= NULL
;
1436 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1445 static ObMenuFrame
* find_active_or_last_menu()
1447 ObMenuFrame
*ret
= NULL
;
1449 ret
= find_active_menu();
1450 if (!ret
&& menu_frame_visible
)
1451 ret
= menu_frame_visible
->data
;
1455 static gboolean
event_handle_menu_keyboard(XEvent
*ev
)
1457 guint keycode
, state
;
1460 gboolean ret
= TRUE
;
1462 keycode
= ev
->xkey
.keycode
;
1463 state
= ev
->xkey
.state
;
1464 unikey
= translate_unichar(keycode
);
1466 frame
= find_active_or_last_menu();
1470 else if (keycode
== ob_keycode(OB_KEY_ESCAPE
) && state
== 0) {
1471 /* Escape closes the active menu */
1472 menu_frame_hide(frame
);
1475 else if (keycode
== ob_keycode(OB_KEY_RETURN
) && (state
== 0 ||
1476 state
== ControlMask
))
1478 /* Enter runs the active item or goes into the submenu.
1479 Control-Enter runs it without closing the menu. */
1481 menu_frame_select_next(frame
->child
);
1483 menu_entry_frame_execute(frame
->selected
, state
, ev
->xkey
.time
);
1486 else if (keycode
== ob_keycode(OB_KEY_LEFT
) && ev
->xkey
.state
== 0) {
1487 /* Left goes to the parent menu */
1488 menu_frame_select(frame
, NULL
, TRUE
);
1491 else if (keycode
== ob_keycode(OB_KEY_RIGHT
) && ev
->xkey
.state
== 0) {
1492 /* Right goes to the selected submenu */
1493 if (frame
->child
) menu_frame_select_next(frame
->child
);
1496 else if (keycode
== ob_keycode(OB_KEY_UP
) && state
== 0) {
1497 menu_frame_select_previous(frame
);
1500 else if (keycode
== ob_keycode(OB_KEY_DOWN
) && state
== 0) {
1501 menu_frame_select_next(frame
);
1504 /* keyboard accelerator shortcuts. */
1505 else if (ev
->xkey
.state
== 0 &&
1506 /* was it a valid key? */
1508 /* don't bother if the menu is empty. */
1513 ObMenuEntryFrame
*found
= NULL
;
1514 guint num_found
= 0;
1516 /* start after the selected one */
1517 start
= frame
->entries
;
1518 if (frame
->selected
) {
1519 for (it
= start
; frame
->selected
!= it
->data
; it
= g_list_next(it
))
1520 g_assert(it
!= NULL
); /* nothing was selected? */
1521 /* next with wraparound */
1522 start
= g_list_next(it
);
1523 if (start
== NULL
) start
= frame
->entries
;
1528 ObMenuEntryFrame
*e
= it
->data
;
1529 gunichar entrykey
= 0;
1531 if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
)
1532 entrykey
= e
->entry
->data
.normal
.shortcut
;
1533 else if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_SUBMENU
)
1534 entrykey
= e
->entry
->data
.submenu
.submenu
->shortcut
;
1536 if (unikey
== entrykey
) {
1537 if (found
== NULL
) found
= e
;
1541 /* next with wraparound */
1542 it
= g_list_next(it
);
1543 if (it
== NULL
) it
= frame
->entries
;
1544 } while (it
!= start
);
1547 if (found
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
&&
1550 menu_frame_select(frame
, found
, TRUE
);
1551 usleep(50000); /* highlight the item for a short bit so the
1552 user can see what happened */
1553 menu_entry_frame_execute(found
, state
, ev
->xkey
.time
);
1555 menu_frame_select(frame
, found
, TRUE
);
1557 menu_frame_select_next(frame
->child
);
1568 static gboolean
event_handle_menu(XEvent
*ev
)
1571 ObMenuEntryFrame
*e
;
1572 gboolean ret
= TRUE
;
1576 if ((ev
->xbutton
.button
< 4 || ev
->xbutton
.button
> 5)
1579 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1580 ev
->xbutton
.y_root
)))
1581 menu_entry_frame_execute(e
, ev
->xbutton
.state
,
1584 menu_frame_hide_all();
1588 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
))) {
1589 if (e
->ignore_enters
)
1592 menu_frame_select(e
->frame
, e
, FALSE
);
1596 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
)) &&
1597 (f
= find_active_menu()) && f
->selected
== e
&&
1598 e
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1600 menu_frame_select(e
->frame
, NULL
, FALSE
);
1604 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1605 ev
->xmotion
.y_root
)))
1606 menu_frame_select(e
->frame
, e
, FALSE
);
1609 ret
= event_handle_menu_keyboard(ev
);
1615 static void event_handle_user_input(ObClient
*client
, XEvent
*e
)
1617 g_assert(e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
1618 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
1619 e
->type
== KeyRelease
);
1621 if (menu_frame_visible
) {
1622 if (event_handle_menu(e
))
1623 /* don't use the event if the menu used it, but if the menu
1624 didn't use it and it's a keypress that is bound, it will
1625 close the menu and be used */
1629 /* if the keyboard interactive action uses the event then dont
1630 use it for bindings. likewise is moveresize uses the event. */
1631 if (!keyboard_process_interactive_grab(e
, &client
) &&
1632 !(moveresize_in_progress
&& moveresize_event(e
)))
1634 if (moveresize_in_progress
)
1635 /* make further actions work on the client being
1637 client
= moveresize_client
;
1639 menu_can_hide
= FALSE
;
1640 ob_main_loop_timeout_add(ob_main_loop
,
1641 config_menu_hide_delay
* 1000,
1642 menu_hide_delay_func
,
1643 NULL
, g_direct_equal
, NULL
);
1645 if (e
->type
== ButtonPress
||
1646 e
->type
== ButtonRelease
||
1647 e
->type
== MotionNotify
)
1649 /* the frame may not be "visible" but they can still click on it
1650 in the case where it is animating before disappearing */
1651 if (!client
|| !frame_iconify_animating(client
->frame
))
1652 mouse_event(client
, e
);
1653 } else if (e
->type
== KeyPress
) {
1654 keyboard_event((focus_cycle_target
? focus_cycle_target
:
1655 (client
? client
: focus_client
)), e
);
1660 static gboolean
menu_hide_delay_func(gpointer data
)
1662 menu_can_hide
= TRUE
;
1663 return FALSE
; /* no repeat */
1666 static void focus_delay_dest(gpointer data
)
1671 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
)
1673 const ObFocusDelayData
*f1
= d1
;
1674 return f1
->client
== d2
;
1677 static gboolean
focus_delay_func(gpointer data
)
1679 ObFocusDelayData
*d
= data
;
1680 Time old
= event_curtime
;
1682 event_curtime
= d
->time
;
1683 if (focus_client
!= d
->client
) {
1684 if (client_focus(d
->client
) && config_focus_raise
)
1685 stacking_raise(CLIENT_AS_WINDOW(d
->client
));
1687 event_curtime
= old
;
1688 return FALSE
; /* no repeat */
1691 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1693 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1697 void event_halt_focus_delay()
1699 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1702 void event_ignore_queued_enters()
1704 GSList
*saved
= NULL
, *it
;
1707 XSync(ob_display
, FALSE
);
1709 /* count the events */
1711 e
= g_new(XEvent
, 1);
1712 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1715 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1716 if (win
&& WINDOW_IS_CLIENT(win
))
1717 ++ignore_enter_focus
;
1719 saved
= g_slist_append(saved
, e
);
1725 /* put the events back */
1726 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1727 XPutBackEvent(ob_display
, it
->data
);
1730 g_slist_free(saved
);
1733 gboolean
event_time_after(Time t1
, Time t2
)
1735 g_assert(t1
!= CurrentTime
);
1736 g_assert(t2
!= CurrentTime
);
1739 Timestamp values wrap around (after about 49.7 days). The server, given
1740 its current time is represented by timestamp T, always interprets
1741 timestamps from clients by treating half of the timestamp space as being
1742 later in time than T.
1743 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1746 /* TIME_HALF is half of the number space of a Time type variable */
1747 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1749 if (t2
>= TIME_HALF
)
1750 /* t2 is in the second half so t1 might wrap around and be smaller than
1752 return t1
>= t2
|| t1
< (t2
+ TIME_HALF
);
1754 /* t2 is in the first half so t1 has to come after it */
1755 return t1
>= t2
&& t1
< (t2
+ TIME_HALF
);