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 to the frame window */
311 if (detail
== NotifyInferior
&& !in_client_only
)
314 /* It was on a client, was it a valid one?
315 It's possible to get a FocusIn event for a client that was managed
316 but has disappeared. Don't even parse those FocusIn events.
318 if (in_client_only
) {
319 ObWindow
*w
= g_hash_table_lookup(window_map
, &e
->xfocus
.window
);
320 if (!w
|| !WINDOW_IS_CLIENT(w
))
324 /* This means focus moved from the root window to a client */
325 if (detail
== NotifyVirtual
)
327 /* This means focus moved from one client to another */
328 if (detail
== NotifyNonlinearVirtual
)
334 g_assert(e
->type
== FocusOut
);
336 /* These are ones we never want.. */
338 /* This means focus was taken by a keyboard/mouse grab. */
339 if (mode
== NotifyGrab
)
342 /* Focus left the root window revertedto state */
343 if (win
== RootWindow(ob_display
, ob_screen
))
346 /* These are the ones we want.. */
348 /* This means focus moved from a client to the root window */
349 if (detail
== NotifyVirtual
)
351 /* This means focus moved from one client to another */
352 if (detail
== NotifyNonlinearVirtual
)
354 /* This means focus had moved to our frame window and now moved off */
355 if (detail
== NotifyNonlinear
)
363 static Bool
event_look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
365 return e
->type
== FocusIn
&& wanted_focusevent(e
, FALSE
);
368 Bool
event_look_for_focusin_client(Display
*d
, XEvent
*e
, XPointer arg
)
370 return e
->type
== FocusIn
&& wanted_focusevent(e
, TRUE
);
373 static void print_focusevent(XEvent
*e
)
375 gint mode
= e
->xfocus
.mode
;
376 gint detail
= e
->xfocus
.detail
;
377 Window win
= e
->xany
.window
;
378 const gchar
*modestr
, *detailstr
;
381 case NotifyNormal
: modestr
="NotifyNormal"; break;
382 case NotifyGrab
: modestr
="NotifyGrab"; break;
383 case NotifyUngrab
: modestr
="NotifyUngrab"; break;
384 case NotifyWhileGrabbed
: modestr
="NotifyWhileGrabbed"; break;
387 case NotifyAncestor
: detailstr
="NotifyAncestor"; break;
388 case NotifyVirtual
: detailstr
="NotifyVirtual"; break;
389 case NotifyInferior
: detailstr
="NotifyInferior"; break;
390 case NotifyNonlinear
: detailstr
="NotifyNonlinear"; break;
391 case NotifyNonlinearVirtual
: detailstr
="NotifyNonlinearVirtual"; break;
392 case NotifyPointer
: detailstr
="NotifyPointer"; break;
393 case NotifyPointerRoot
: detailstr
="NotifyPointerRoot"; break;
394 case NotifyDetailNone
: detailstr
="NotifyDetailNone"; break;
399 ob_debug_type(OB_DEBUG_FOCUS
, "Focus%s 0x%x mode=%s detail=%s\n",
400 (e
->xfocus
.type
== FocusIn
? "In" : "Out"),
406 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
411 if (!wanted_focusevent(e
, FALSE
))
416 if (!wanted_focusevent(e
, FALSE
))
423 static void event_process(const XEvent
*ec
, gpointer data
)
426 ObClient
*client
= NULL
;
428 ObDockApp
*dockapp
= NULL
;
429 ObWindow
*obwin
= NULL
;
430 GSList
*timewinclients
= NULL
;
432 ObEventData
*ed
= data
;
434 /* make a copy we can mangle */
438 window
= event_get_window(e
);
439 if (e
->type
!= PropertyNotify
||
440 !(timewinclients
= propwin_get_clients(window
,
441 OB_PROPWIN_USER_TIME
)))
442 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
443 switch (obwin
->type
) {
445 dock
= WINDOW_AS_DOCK(obwin
);
448 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
451 client
= WINDOW_AS_CLIENT(obwin
);
454 case Window_Internal
:
455 /* not to be used for events */
456 g_assert_not_reached();
461 event_set_curtime(e
);
463 if (event_ignore(e
, client
)) {
470 /* deal with it in the kernel */
472 if (menu_frame_visible
&&
473 (e
->type
== EnterNotify
|| e
->type
== LeaveNotify
))
475 /* crossing events for menu */
476 event_handle_menu(e
);
477 } else if (e
->type
== FocusIn
) {
478 if (e
->xfocus
.detail
== NotifyPointerRoot
||
479 e
->xfocus
.detail
== NotifyDetailNone
||
480 e
->xfocus
.detail
== NotifyInferior
)
484 ob_debug_type(OB_DEBUG_FOCUS
,
485 "Focus went to pointer root/none or to our frame "
488 /* If another FocusIn is in the queue then don't fallback yet. This
489 fixes the fun case of:
490 window map -> send focusin
491 window unmap -> get focusout
492 window map -> send focusin
493 get first focus out -> fall back to something (new window
494 hasn't received focus yet, so something else) -> send focusin
495 which means the "something else" is the last thing to get a
496 focusin sent to it, so the new window doesn't end up with focus.
498 But if the other focus in is something like PointerRoot then we
499 still want to fall back.
501 if (XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin_client
,
504 XPutBackEvent(ob_display
, &ce
);
505 ob_debug_type(OB_DEBUG_FOCUS
,
506 " but another FocusIn is coming\n");
508 /* Focus has been reverted to the root window, nothing, or to
511 FocusOut events come after UnmapNotify, so we don't need to
512 worry about focusing an invalid window
515 /* In this case we know focus is in our screen */
516 if (e
->xfocus
.detail
== NotifyInferior
)
517 focus_left_screen
= FALSE
;
519 if (!focus_left_screen
)
520 focus_fallback(TRUE
);
527 ob_debug_type(OB_DEBUG_FOCUS
,
528 "Focus went to a window that is already gone\n");
530 /* If you send focus to a window and then it disappears, you can
531 get the FocusIn FocusOut for it, after it is unmanaged.
533 if (XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin_client
,
536 XPutBackEvent(ob_display
, &ce
);
537 ob_debug_type(OB_DEBUG_FOCUS
,
538 " but another FocusIn is coming\n");
540 focus_fallback(TRUE
);
543 else if (client
!= focus_client
) {
544 focus_left_screen
= FALSE
;
545 frame_adjust_focus(client
->frame
, TRUE
);
546 focus_set_client(client
);
547 client_calc_layer(client
);
548 client_bring_helper_windows(client
);
550 } else if (e
->type
== FocusOut
) {
551 gboolean nomove
= FALSE
;
554 /* Look for the followup FocusIn */
555 if (!XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin
, NULL
)) {
556 /* There is no FocusIn, this means focus went to a window that
557 is not being managed, or a window on another screen. */
561 xerror_set_ignore(TRUE
);
562 if (XGetInputFocus(ob_display
, &win
, &i
) != 0 &&
563 XGetGeometry(ob_display
, win
, &root
, &i
,&i
,&u
,&u
,&u
,&u
) != 0 &&
564 root
!= RootWindow(ob_display
, ob_screen
))
566 ob_debug_type(OB_DEBUG_FOCUS
,
567 "Focus went to another screen !\n");
568 focus_left_screen
= TRUE
;
571 ob_debug_type(OB_DEBUG_FOCUS
,
572 "Focus went to a black hole !\n");
573 xerror_set_ignore(FALSE
);
574 /* nothing is focused */
575 focus_set_client(NULL
);
576 } else if (ce
.xany
.window
== e
->xany
.window
) {
577 ob_debug_type(OB_DEBUG_FOCUS
, "Focus didn't go anywhere\n");
578 /* If focus didn't actually move anywhere, there is nothing to do*/
581 /* Focus did move, so process the FocusIn event */
582 ObEventData ed
= { .ignored
= FALSE
};
583 event_process(&ce
, &ed
);
585 /* The FocusIn was ignored, this means it was on a window
586 that isn't a client. */
587 ob_debug_type(OB_DEBUG_FOCUS
,
588 "Focus went to an unmanaged window 0x%x !\n",
590 focus_fallback(TRUE
);
594 if (client
&& !nomove
) {
595 frame_adjust_focus(client
->frame
, FALSE
);
596 if (client
== focus_client
)
597 focus_set_client(NULL
);
598 /* focus_set_client has already been called for sure */
599 client_calc_layer(client
);
601 } else if (timewinclients
)
602 event_handle_user_time_window_clients(timewinclients
, e
);
604 event_handle_client(client
, e
);
606 event_handle_dockapp(dockapp
, e
);
608 event_handle_dock(dock
, e
);
609 else if (window
== RootWindow(ob_display
, ob_screen
))
610 event_handle_root(e
);
611 else if (e
->type
== MapRequest
)
612 client_manage(window
);
613 else if (e
->type
== ClientMessage
) {
614 /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
615 windows that are not managed yet. */
616 if (e
->xclient
.message_type
== prop_atoms
.net_request_frame_extents
) {
617 /* Pretend to manage the client, getting information used to
618 determine its decorations */
619 ObClient
*c
= client_fake_manage(e
->xclient
.window
);
622 /* set the frame extents on the window */
623 vals
[0] = c
->frame
->size
.left
;
624 vals
[1] = c
->frame
->size
.right
;
625 vals
[2] = c
->frame
->size
.top
;
626 vals
[3] = c
->frame
->size
.bottom
;
627 PROP_SETA32(e
->xclient
.window
, net_frame_extents
,
630 /* Free the pretend client */
631 client_fake_unmanage(c
);
634 else if (e
->type
== ConfigureRequest
) {
635 /* unhandled configure requests must be used to configure the
639 xwc
.x
= e
->xconfigurerequest
.x
;
640 xwc
.y
= e
->xconfigurerequest
.y
;
641 xwc
.width
= e
->xconfigurerequest
.width
;
642 xwc
.height
= e
->xconfigurerequest
.height
;
643 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
644 xwc
.sibling
= e
->xconfigurerequest
.above
;
645 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
647 /* we are not to be held responsible if someone sends us an
649 xerror_set_ignore(TRUE
);
650 XConfigureWindow(ob_display
, window
,
651 e
->xconfigurerequest
.value_mask
, &xwc
);
652 xerror_set_ignore(FALSE
);
655 else if (extensions_sync
&&
656 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
658 XSyncAlarmNotifyEvent
*se
= (XSyncAlarmNotifyEvent
*)e
;
659 if (se
->alarm
== moveresize_alarm
&& moveresize_in_progress
)
664 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
665 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
666 e
->type
== KeyRelease
)
668 event_handle_user_input(client
, e
);
671 /* if something happens and it's not from an XEvent, then we don't know
673 event_curtime
= CurrentTime
;
676 static void event_handle_root(XEvent
*e
)
682 ob_debug("Another WM has requested to replace us. Exiting.\n");
687 if (e
->xclient
.format
!= 32) break;
689 msgtype
= e
->xclient
.message_type
;
690 if (msgtype
== prop_atoms
.net_current_desktop
) {
691 guint d
= e
->xclient
.data
.l
[0];
692 if (d
< screen_num_desktops
) {
693 event_curtime
= e
->xclient
.data
.l
[1];
694 if (event_curtime
== 0)
695 ob_debug_type(OB_DEBUG_APP_BUGS
,
696 "_NET_CURRENT_DESKTOP message is missing "
698 screen_set_desktop(d
, TRUE
);
700 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
701 guint d
= e
->xclient
.data
.l
[0];
703 screen_set_num_desktops(d
);
704 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
705 screen_show_desktop(e
->xclient
.data
.l
[0] != 0, NULL
);
706 } else if (msgtype
== prop_atoms
.ob_control
) {
707 if (e
->xclient
.data
.l
[0] == 1)
709 else if (e
->xclient
.data
.l
[0] == 2)
714 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
715 screen_update_desktop_names();
716 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
717 screen_update_layout();
719 case ConfigureNotify
:
721 XRRUpdateConfiguration(e
);
730 void event_enter_client(ObClient
*client
)
732 g_assert(config_focus_follow
);
734 if (client_enter_focusable(client
) && client_can_focus(client
)) {
735 if (config_focus_delay
) {
736 ObFocusDelayData
*data
;
738 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
740 data
= g_new(ObFocusDelayData
, 1);
741 data
->client
= client
;
742 data
->time
= event_curtime
;
744 ob_main_loop_timeout_add(ob_main_loop
,
747 data
, focus_delay_cmp
, focus_delay_dest
);
749 ObFocusDelayData data
;
750 data
.client
= client
;
751 data
.time
= event_curtime
;
752 focus_delay_func(&data
);
757 static void event_handle_user_time_window_clients(GSList
*l
, XEvent
*e
)
759 g_assert(e
->type
== PropertyNotify
);
760 if (e
->xproperty
.atom
== prop_atoms
.net_wm_user_time
) {
761 for (; l
; l
= g_slist_next(l
))
762 client_update_user_time(l
->data
);
766 static void event_handle_client(ObClient
*client
, XEvent
*e
)
771 static gint px
= -1, py
= -1;
776 /* save where the press occured for the first button pressed */
778 pb
= e
->xbutton
.button
;
783 /* Wheel buttons don't draw because they are an instant click, so it
784 is a waste of resources to go drawing it.
785 if the user is doing an intereactive thing, or has a menu open then
786 the mouse is grabbed (possibly) and if we get these events we don't
787 want to deal with them
789 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5) &&
790 !keyboard_interactively_grabbed() &&
793 /* use where the press occured */
794 con
= frame_context(client
, e
->xbutton
.window
, px
, py
);
795 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
797 if (e
->type
== ButtonRelease
&& e
->xbutton
.button
== pb
)
798 pb
= 0, px
= py
= -1;
801 case OB_FRAME_CONTEXT_MAXIMIZE
:
802 client
->frame
->max_press
= (e
->type
== ButtonPress
);
803 framerender_frame(client
->frame
);
805 case OB_FRAME_CONTEXT_CLOSE
:
806 client
->frame
->close_press
= (e
->type
== ButtonPress
);
807 framerender_frame(client
->frame
);
809 case OB_FRAME_CONTEXT_ICONIFY
:
810 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
811 framerender_frame(client
->frame
);
813 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
814 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
815 framerender_frame(client
->frame
);
817 case OB_FRAME_CONTEXT_SHADE
:
818 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
819 framerender_frame(client
->frame
);
822 /* nothing changes with clicks for any other contexts */
828 con
= frame_context(client
, e
->xmotion
.window
,
829 e
->xmotion
.x
, e
->xmotion
.y
);
831 case OB_FRAME_CONTEXT_TITLEBAR
:
832 /* we've left the button area inside the titlebar */
833 if (client
->frame
->max_hover
|| client
->frame
->desk_hover
||
834 client
->frame
->shade_hover
|| client
->frame
->iconify_hover
||
835 client
->frame
->close_hover
)
837 client
->frame
->max_hover
= FALSE
;
838 client
->frame
->desk_hover
= FALSE
;
839 client
->frame
->shade_hover
= FALSE
;
840 client
->frame
->iconify_hover
= FALSE
;
841 client
->frame
->close_hover
= FALSE
;
842 frame_adjust_state(client
->frame
);
845 case OB_FRAME_CONTEXT_MAXIMIZE
:
846 if (!client
->frame
->max_hover
) {
847 client
->frame
->max_hover
= TRUE
;
848 frame_adjust_state(client
->frame
);
851 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
852 if (!client
->frame
->desk_hover
) {
853 client
->frame
->desk_hover
= TRUE
;
854 frame_adjust_state(client
->frame
);
857 case OB_FRAME_CONTEXT_SHADE
:
858 if (!client
->frame
->shade_hover
) {
859 client
->frame
->shade_hover
= TRUE
;
860 frame_adjust_state(client
->frame
);
863 case OB_FRAME_CONTEXT_ICONIFY
:
864 if (!client
->frame
->iconify_hover
) {
865 client
->frame
->iconify_hover
= TRUE
;
866 frame_adjust_state(client
->frame
);
869 case OB_FRAME_CONTEXT_CLOSE
:
870 if (!client
->frame
->close_hover
) {
871 client
->frame
->close_hover
= TRUE
;
872 frame_adjust_state(client
->frame
);
880 con
= frame_context(client
, e
->xcrossing
.window
,
881 e
->xcrossing
.x
, e
->xcrossing
.y
);
883 case OB_FRAME_CONTEXT_MAXIMIZE
:
884 client
->frame
->max_hover
= FALSE
;
885 frame_adjust_state(client
->frame
);
887 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
888 client
->frame
->desk_hover
= FALSE
;
889 frame_adjust_state(client
->frame
);
891 case OB_FRAME_CONTEXT_SHADE
:
892 client
->frame
->shade_hover
= FALSE
;
893 frame_adjust_state(client
->frame
);
895 case OB_FRAME_CONTEXT_ICONIFY
:
896 client
->frame
->iconify_hover
= FALSE
;
897 frame_adjust_state(client
->frame
);
899 case OB_FRAME_CONTEXT_CLOSE
:
900 client
->frame
->close_hover
= FALSE
;
901 frame_adjust_state(client
->frame
);
903 case OB_FRAME_CONTEXT_FRAME
:
904 /* When the mouse leaves an animating window, don't use the
905 corresponding enter events. Pretend like the animating window
906 doesn't even exist..! */
907 if (frame_iconify_animating(client
->frame
))
908 event_ignore_queued_enters();
910 ob_debug_type(OB_DEBUG_FOCUS
,
911 "%sNotify mode %d detail %d on %lx\n",
912 (e
->type
== EnterNotify
? "Enter" : "Leave"),
914 e
->xcrossing
.detail
, (client
?client
->window
:0));
915 if (keyboard_interactively_grabbed())
917 if (config_focus_follow
&& config_focus_delay
&&
918 /* leave inferior events can happen when the mouse goes onto
919 the window's border and then into the window before the
921 e
->xcrossing
.detail
!= NotifyInferior
)
923 ob_main_loop_timeout_remove_data(ob_main_loop
,
934 gboolean nofocus
= FALSE
;
936 if (ignore_enter_focus
) {
937 ignore_enter_focus
--;
941 con
= frame_context(client
, e
->xcrossing
.window
,
942 e
->xcrossing
.x
, e
->xcrossing
.y
);
944 case OB_FRAME_CONTEXT_MAXIMIZE
:
945 client
->frame
->max_hover
= TRUE
;
946 frame_adjust_state(client
->frame
);
948 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
949 client
->frame
->desk_hover
= TRUE
;
950 frame_adjust_state(client
->frame
);
952 case OB_FRAME_CONTEXT_SHADE
:
953 client
->frame
->shade_hover
= TRUE
;
954 frame_adjust_state(client
->frame
);
956 case OB_FRAME_CONTEXT_ICONIFY
:
957 client
->frame
->iconify_hover
= TRUE
;
958 frame_adjust_state(client
->frame
);
960 case OB_FRAME_CONTEXT_CLOSE
:
961 client
->frame
->close_hover
= TRUE
;
962 frame_adjust_state(client
->frame
);
964 case OB_FRAME_CONTEXT_FRAME
:
965 if (keyboard_interactively_grabbed())
967 if (e
->xcrossing
.mode
== NotifyGrab
||
968 e
->xcrossing
.mode
== NotifyUngrab
||
969 /*ignore enters when we're already in the window */
970 e
->xcrossing
.detail
== NotifyInferior
)
972 ob_debug_type(OB_DEBUG_FOCUS
,
973 "%sNotify mode %d detail %d on %lx IGNORED\n",
974 (e
->type
== EnterNotify
? "Enter" : "Leave"),
976 e
->xcrossing
.detail
, client
?client
->window
:0);
978 ob_debug_type(OB_DEBUG_FOCUS
,
979 "%sNotify mode %d detail %d on %lx, "
980 "focusing window: %d\n",
981 (e
->type
== EnterNotify
? "Enter" : "Leave"),
983 e
->xcrossing
.detail
, (client
?client
->window
:0),
985 if (!nofocus
&& config_focus_follow
)
986 event_enter_client(client
);
994 case ConfigureRequest
:
996 /* dont compress these unless you're going to watch for property
997 notifies in between (these can change what the configure would
999 also you can't compress stacking events
1004 /* if nothing is changed, then a configurenotify is needed */
1005 gboolean config
= TRUE
;
1009 w
= client
->area
.width
;
1010 h
= client
->area
.height
;
1012 ob_debug("ConfigureRequest desktop %d wmstate %d visibile %d\n",
1013 screen_desktop
, client
->wmstate
, client
->frame
->visible
);
1015 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
1016 if (client
->border_width
!= e
->xconfigurerequest
.border_width
) {
1017 client
->border_width
= e
->xconfigurerequest
.border_width
;
1018 /* if only the border width is changing, then it's not needed*/
1023 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
1024 ObClient
*sibling
= NULL
;
1026 /* get the sibling */
1027 if (e
->xconfigurerequest
.value_mask
& CWSibling
) {
1029 win
= g_hash_table_lookup(window_map
,
1030 &e
->xconfigurerequest
.above
);
1031 if (WINDOW_IS_CLIENT(win
) && WINDOW_AS_CLIENT(win
) != client
)
1032 sibling
= WINDOW_AS_CLIENT(win
);
1035 /* activate it rather than just focus it */
1036 stacking_restack_request(client
, sibling
,
1037 e
->xconfigurerequest
.detail
, TRUE
);
1039 /* if a stacking change is requested then it is needed */
1043 /* don't allow clients to move shaded windows (fvwm does this) */
1044 if (client
->shaded
&& (e
->xconfigurerequest
.value_mask
& CWX
||
1045 e
->xconfigurerequest
.value_mask
& CWY
))
1047 e
->xconfigurerequest
.value_mask
&= ~CWX
;
1048 e
->xconfigurerequest
.value_mask
&= ~CWY
;
1050 /* if the client tried to move and we aren't letting it then a
1051 synthetic event is needed */
1055 if (e
->xconfigurerequest
.value_mask
& CWX
||
1056 e
->xconfigurerequest
.value_mask
& CWY
||
1057 e
->xconfigurerequest
.value_mask
& CWWidth
||
1058 e
->xconfigurerequest
.value_mask
& CWHeight
)
1060 if (e
->xconfigurerequest
.value_mask
& CWX
)
1061 x
= e
->xconfigurerequest
.x
;
1062 if (e
->xconfigurerequest
.value_mask
& CWY
)
1063 y
= e
->xconfigurerequest
.y
;
1064 if (e
->xconfigurerequest
.value_mask
& CWWidth
)
1065 w
= e
->xconfigurerequest
.width
;
1066 if (e
->xconfigurerequest
.value_mask
& CWHeight
)
1067 h
= e
->xconfigurerequest
.height
;
1069 /* if a new position or size is requested, then a configure is
1074 ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d\n",
1075 e
->xconfigurerequest
.value_mask
& CWX
, x
,
1076 e
->xconfigurerequest
.value_mask
& CWY
, y
,
1077 e
->xconfigurerequest
.value_mask
& CWWidth
, w
,
1078 e
->xconfigurerequest
.value_mask
& CWHeight
, h
);
1080 /* check for broken apps moving to their root position
1082 XXX remove this some day...that would be nice. right now all
1083 kde apps do this when they try activate themselves on another
1084 desktop. eg. open amarok window on desktop 1, switch to desktop
1085 2, click amarok tray icon. it will move by its decoration size.
1087 if (x
!= client
->area
.x
&&
1088 x
== (client
->frame
->area
.x
+ client
->frame
->size
.left
-
1089 (gint
)client
->border_width
) &&
1090 y
!= client
->area
.y
&&
1091 y
== (client
->frame
->area
.y
+ client
->frame
->size
.top
-
1092 (gint
)client
->border_width
))
1094 ob_debug_type(OB_DEBUG_APP_BUGS
,
1095 "Application %s is trying to move via "
1096 "ConfigureRequest to it's root window position "
1097 "but it is not using StaticGravity\n",
1105 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1106 client_configure_full(client
, x
, y
, w
, h
, FALSE
, TRUE
);
1111 if (client
->ignore_unmaps
) {
1112 client
->ignore_unmaps
--;
1115 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
1116 "ignores left %d\n",
1117 client
->window
, e
->xunmap
.event
, e
->xunmap
.from_configure
,
1118 client
->ignore_unmaps
);
1119 client_unmanage(client
);
1122 ob_debug("DestroyNotify for window 0x%x\n", client
->window
);
1123 client_unmanage(client
);
1125 case ReparentNotify
:
1126 /* this is when the client is first taken captive in the frame */
1127 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
1130 This event is quite rare and is usually handled in unmapHandler.
1131 However, if the window is unmapped when the reparent event occurs,
1132 the window manager never sees it because an unmap event is not sent
1133 to an already unmapped window.
1136 /* we don't want the reparent event, put it back on the stack for the
1137 X server to deal with after we unmanage the window */
1138 XPutBackEvent(ob_display
, e
);
1140 ob_debug("ReparentNotify for window 0x%x\n", client
->window
);
1141 client_unmanage(client
);
1144 ob_debug("MapRequest for 0x%lx\n", client
->window
);
1145 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
1146 does, we don't want it!
1147 it can happen now when the window is on
1148 another desktop, but we still don't
1150 client_activate(client
, FALSE
, TRUE
);
1153 /* validate cuz we query stuff off the client here */
1154 if (!client_validate(client
)) break;
1156 if (e
->xclient
.format
!= 32) return;
1158 msgtype
= e
->xclient
.message_type
;
1159 if (msgtype
== prop_atoms
.wm_change_state
) {
1160 /* compress changes into a single change */
1161 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1163 /* XXX: it would be nice to compress ALL messages of a
1164 type, not just messages in a row without other
1165 message types between. */
1166 if (ce
.xclient
.message_type
!= msgtype
) {
1167 XPutBackEvent(ob_display
, &ce
);
1170 e
->xclient
= ce
.xclient
;
1172 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
1173 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
1174 /* compress changes into a single change */
1175 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1177 /* XXX: it would be nice to compress ALL messages of a
1178 type, not just messages in a row without other
1179 message types between. */
1180 if (ce
.xclient
.message_type
!= msgtype
) {
1181 XPutBackEvent(ob_display
, &ce
);
1184 e
->xclient
= ce
.xclient
;
1186 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
1187 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
1188 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
1190 } else if (msgtype
== prop_atoms
.net_wm_state
) {
1191 /* can't compress these */
1192 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1193 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
1194 e
->xclient
.data
.l
[0] == 1 ? "Add" :
1195 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1196 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
1198 client_set_state(client
, e
->xclient
.data
.l
[0],
1199 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
1200 } else if (msgtype
== prop_atoms
.net_close_window
) {
1201 ob_debug("net_close_window for 0x%lx\n", client
->window
);
1202 client_close(client
);
1203 } else if (msgtype
== prop_atoms
.net_active_window
) {
1204 ob_debug("net_active_window for 0x%lx source=%s\n",
1206 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
1207 (e
->xclient
.data
.l
[0] == 1 ? "application" :
1208 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
1209 /* XXX make use of data.l[2] !? */
1210 event_curtime
= e
->xclient
.data
.l
[1];
1211 if (event_curtime
== 0)
1212 ob_debug_type(OB_DEBUG_APP_BUGS
,
1213 "_NET_ACTIVE_WINDOW message for window %s is "
1214 "missing a timestamp\n", client
->title
);
1215 client_activate(client
, FALSE
,
1216 (e
->xclient
.data
.l
[0] == 0 ||
1217 e
->xclient
.data
.l
[0] == 2));
1218 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
1219 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1220 client
->window
, e
->xclient
.data
.l
[2]);
1221 if ((Atom
)e
->xclient
.data
.l
[2] ==
1222 prop_atoms
.net_wm_moveresize_size_topleft
||
1223 (Atom
)e
->xclient
.data
.l
[2] ==
1224 prop_atoms
.net_wm_moveresize_size_top
||
1225 (Atom
)e
->xclient
.data
.l
[2] ==
1226 prop_atoms
.net_wm_moveresize_size_topright
||
1227 (Atom
)e
->xclient
.data
.l
[2] ==
1228 prop_atoms
.net_wm_moveresize_size_right
||
1229 (Atom
)e
->xclient
.data
.l
[2] ==
1230 prop_atoms
.net_wm_moveresize_size_right
||
1231 (Atom
)e
->xclient
.data
.l
[2] ==
1232 prop_atoms
.net_wm_moveresize_size_bottomright
||
1233 (Atom
)e
->xclient
.data
.l
[2] ==
1234 prop_atoms
.net_wm_moveresize_size_bottom
||
1235 (Atom
)e
->xclient
.data
.l
[2] ==
1236 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1237 (Atom
)e
->xclient
.data
.l
[2] ==
1238 prop_atoms
.net_wm_moveresize_size_left
||
1239 (Atom
)e
->xclient
.data
.l
[2] ==
1240 prop_atoms
.net_wm_moveresize_move
||
1241 (Atom
)e
->xclient
.data
.l
[2] ==
1242 prop_atoms
.net_wm_moveresize_size_keyboard
||
1243 (Atom
)e
->xclient
.data
.l
[2] ==
1244 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1246 moveresize_start(client
, e
->xclient
.data
.l
[0],
1247 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1248 e
->xclient
.data
.l
[2]);
1250 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1251 prop_atoms
.net_wm_moveresize_cancel
)
1252 moveresize_end(TRUE
);
1253 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1254 gint grav
, x
, y
, w
, h
;
1256 if (e
->xclient
.data
.l
[0] & 0xff)
1257 grav
= e
->xclient
.data
.l
[0] & 0xff;
1259 grav
= client
->gravity
;
1261 if (e
->xclient
.data
.l
[0] & 1 << 8)
1262 x
= e
->xclient
.data
.l
[1];
1265 if (e
->xclient
.data
.l
[0] & 1 << 9)
1266 y
= e
->xclient
.data
.l
[2];
1269 if (e
->xclient
.data
.l
[0] & 1 << 10)
1270 w
= e
->xclient
.data
.l
[3];
1272 w
= client
->area
.width
;
1273 if (e
->xclient
.data
.l
[0] & 1 << 11)
1274 h
= e
->xclient
.data
.l
[4];
1276 h
= client
->area
.height
;
1278 ob_debug("MOVERESIZE x %d %d y %d %d\n",
1279 e
->xclient
.data
.l
[0] & 1 << 8, x
,
1280 e
->xclient
.data
.l
[0] & 1 << 9, y
);
1281 client_convert_gravity(client
, grav
, &x
, &y
, w
, h
);
1282 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1283 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
);
1284 } else if (msgtype
== prop_atoms
.net_restack_window
) {
1285 if (e
->xclient
.data
.l
[0] != 2) {
1286 ob_debug_type(OB_DEBUG_APP_BUGS
,
1287 "_NET_RESTACK_WINDOW sent for window %s with "
1288 "invalid source indication %ld\n",
1289 client
->title
, e
->xclient
.data
.l
[0]);
1291 ObClient
*sibling
= NULL
;
1292 if (e
->xclient
.data
.l
[1]) {
1293 ObWindow
*win
= g_hash_table_lookup(window_map
,
1294 &e
->xclient
.data
.l
[1]);
1295 if (WINDOW_IS_CLIENT(win
) &&
1296 WINDOW_AS_CLIENT(win
) != client
)
1298 sibling
= WINDOW_AS_CLIENT(win
);
1300 if (sibling
== NULL
)
1301 ob_debug_type(OB_DEBUG_APP_BUGS
,
1302 "_NET_RESTACK_WINDOW sent for window %s "
1303 "with invalid sibling 0x%x\n",
1304 client
->title
, e
->xclient
.data
.l
[1]);
1306 if (e
->xclient
.data
.l
[2] == Below
||
1307 e
->xclient
.data
.l
[2] == BottomIf
||
1308 e
->xclient
.data
.l
[2] == Above
||
1309 e
->xclient
.data
.l
[2] == TopIf
||
1310 e
->xclient
.data
.l
[2] == Opposite
)
1312 /* just raise, don't activate */
1313 stacking_restack_request(client
, sibling
,
1314 e
->xclient
.data
.l
[2], FALSE
);
1315 /* send a synthetic ConfigureNotify, cuz this is supposed
1316 to be like a ConfigureRequest. */
1317 client_configure_full(client
, client
->area
.x
,
1320 client
->area
.height
,
1323 ob_debug_type(OB_DEBUG_APP_BUGS
,
1324 "_NET_RESTACK_WINDOW sent for window %s "
1325 "with invalid detail %d\n",
1326 client
->title
, e
->xclient
.data
.l
[2]);
1330 case PropertyNotify
:
1331 /* validate cuz we query stuff off the client here */
1332 if (!client_validate(client
)) break;
1334 /* compress changes to a single property into a single change */
1335 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1339 /* XXX: it would be nice to compress ALL changes to a property,
1340 not just changes in a row without other props between. */
1342 a
= ce
.xproperty
.atom
;
1343 b
= e
->xproperty
.atom
;
1347 if ((a
== prop_atoms
.net_wm_name
||
1348 a
== prop_atoms
.wm_name
||
1349 a
== prop_atoms
.net_wm_icon_name
||
1350 a
== prop_atoms
.wm_icon_name
)
1352 (b
== prop_atoms
.net_wm_name
||
1353 b
== prop_atoms
.wm_name
||
1354 b
== prop_atoms
.net_wm_icon_name
||
1355 b
== prop_atoms
.wm_icon_name
)) {
1358 if (a
== prop_atoms
.net_wm_icon
&&
1359 b
== prop_atoms
.net_wm_icon
)
1362 XPutBackEvent(ob_display
, &ce
);
1366 msgtype
= e
->xproperty
.atom
;
1367 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1368 client_update_normal_hints(client
);
1369 /* normal hints can make a window non-resizable */
1370 client_setup_decor_and_functions(client
);
1371 } else if (msgtype
== XA_WM_HINTS
) {
1372 client_update_wmhints(client
);
1373 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1374 client_update_transient_for(client
);
1375 client_get_type_and_transientness(client
);
1376 /* type may have changed, so update the layer */
1377 client_calc_layer(client
);
1378 client_setup_decor_and_functions(client
);
1379 } else if (msgtype
== prop_atoms
.net_wm_name
||
1380 msgtype
== prop_atoms
.wm_name
||
1381 msgtype
== prop_atoms
.net_wm_icon_name
||
1382 msgtype
== prop_atoms
.wm_icon_name
) {
1383 client_update_title(client
);
1384 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1385 client_update_protocols(client
);
1386 client_setup_decor_and_functions(client
);
1388 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1389 client_update_strut(client
);
1391 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1392 client_update_icons(client
);
1394 else if (msgtype
== prop_atoms
.net_wm_icon_geometry
) {
1395 client_update_icon_geometry(client
);
1397 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1398 client_update_user_time(client
);
1400 else if (msgtype
== prop_atoms
.net_wm_user_time_window
) {
1401 client_update_user_time_window(client
);
1404 else if (msgtype
== prop_atoms
.net_wm_sync_request_counter
) {
1405 client_update_sync_request_counter(client
);
1408 case ColormapNotify
:
1409 client_update_colormap(client
, e
->xcolormap
.colormap
);
1414 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1415 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1416 frame_adjust_shape(client
->frame
);
1422 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1426 if (e
->xbutton
.button
== 1)
1427 stacking_raise(DOCK_AS_WINDOW(s
));
1428 else if (e
->xbutton
.button
== 2)
1429 stacking_lower(DOCK_AS_WINDOW(s
));
1440 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1444 dock_app_drag(app
, &e
->xmotion
);
1447 if (app
->ignore_unmaps
) {
1448 app
->ignore_unmaps
--;
1451 dock_remove(app
, TRUE
);
1454 dock_remove(app
, FALSE
);
1456 case ReparentNotify
:
1457 dock_remove(app
, FALSE
);
1459 case ConfigureNotify
:
1460 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1465 static ObMenuFrame
* find_active_menu()
1468 ObMenuFrame
*ret
= NULL
;
1470 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1479 static ObMenuFrame
* find_active_or_last_menu()
1481 ObMenuFrame
*ret
= NULL
;
1483 ret
= find_active_menu();
1484 if (!ret
&& menu_frame_visible
)
1485 ret
= menu_frame_visible
->data
;
1489 static gboolean
event_handle_menu_keyboard(XEvent
*ev
)
1491 guint keycode
, state
;
1494 gboolean ret
= TRUE
;
1496 keycode
= ev
->xkey
.keycode
;
1497 state
= ev
->xkey
.state
;
1498 unikey
= translate_unichar(keycode
);
1500 frame
= find_active_or_last_menu();
1504 else if (keycode
== ob_keycode(OB_KEY_ESCAPE
) && state
== 0) {
1505 /* Escape closes the active menu */
1506 menu_frame_hide(frame
);
1509 else if (keycode
== ob_keycode(OB_KEY_RETURN
) && (state
== 0 ||
1510 state
== ControlMask
))
1512 /* Enter runs the active item or goes into the submenu.
1513 Control-Enter runs it without closing the menu. */
1515 menu_frame_select_next(frame
->child
);
1517 menu_entry_frame_execute(frame
->selected
, state
, ev
->xkey
.time
);
1520 else if (keycode
== ob_keycode(OB_KEY_LEFT
) && ev
->xkey
.state
== 0) {
1521 /* Left goes to the parent menu */
1522 menu_frame_select(frame
, NULL
, TRUE
);
1525 else if (keycode
== ob_keycode(OB_KEY_RIGHT
) && ev
->xkey
.state
== 0) {
1526 /* Right goes to the selected submenu */
1527 if (frame
->child
) menu_frame_select_next(frame
->child
);
1530 else if (keycode
== ob_keycode(OB_KEY_UP
) && state
== 0) {
1531 menu_frame_select_previous(frame
);
1534 else if (keycode
== ob_keycode(OB_KEY_DOWN
) && state
== 0) {
1535 menu_frame_select_next(frame
);
1538 /* keyboard accelerator shortcuts. */
1539 else if (ev
->xkey
.state
== 0 &&
1540 /* was it a valid key? */
1542 /* don't bother if the menu is empty. */
1547 ObMenuEntryFrame
*found
= NULL
;
1548 guint num_found
= 0;
1550 /* start after the selected one */
1551 start
= frame
->entries
;
1552 if (frame
->selected
) {
1553 for (it
= start
; frame
->selected
!= it
->data
; it
= g_list_next(it
))
1554 g_assert(it
!= NULL
); /* nothing was selected? */
1555 /* next with wraparound */
1556 start
= g_list_next(it
);
1557 if (start
== NULL
) start
= frame
->entries
;
1562 ObMenuEntryFrame
*e
= it
->data
;
1563 gunichar entrykey
= 0;
1565 if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
)
1566 entrykey
= e
->entry
->data
.normal
.shortcut
;
1567 else if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_SUBMENU
)
1568 entrykey
= e
->entry
->data
.submenu
.submenu
->shortcut
;
1570 if (unikey
== entrykey
) {
1571 if (found
== NULL
) found
= e
;
1575 /* next with wraparound */
1576 it
= g_list_next(it
);
1577 if (it
== NULL
) it
= frame
->entries
;
1578 } while (it
!= start
);
1581 if (found
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
&&
1584 menu_frame_select(frame
, found
, TRUE
);
1585 usleep(50000); /* highlight the item for a short bit so the
1586 user can see what happened */
1587 menu_entry_frame_execute(found
, state
, ev
->xkey
.time
);
1589 menu_frame_select(frame
, found
, TRUE
);
1591 menu_frame_select_next(frame
->child
);
1602 static gboolean
event_handle_menu(XEvent
*ev
)
1605 ObMenuEntryFrame
*e
;
1606 gboolean ret
= TRUE
;
1610 if ((ev
->xbutton
.button
< 4 || ev
->xbutton
.button
> 5)
1613 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1614 ev
->xbutton
.y_root
)))
1615 menu_entry_frame_execute(e
, ev
->xbutton
.state
,
1618 menu_frame_hide_all();
1622 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
))) {
1623 if (e
->ignore_enters
)
1626 menu_frame_select(e
->frame
, e
, FALSE
);
1630 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
)) &&
1631 (f
= find_active_menu()) && f
->selected
== e
&&
1632 e
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1634 menu_frame_select(e
->frame
, NULL
, FALSE
);
1638 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1639 ev
->xmotion
.y_root
)))
1640 menu_frame_select(e
->frame
, e
, FALSE
);
1643 ret
= event_handle_menu_keyboard(ev
);
1649 static void event_handle_user_input(ObClient
*client
, XEvent
*e
)
1651 g_assert(e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
1652 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
1653 e
->type
== KeyRelease
);
1655 if (menu_frame_visible
) {
1656 if (event_handle_menu(e
))
1657 /* don't use the event if the menu used it, but if the menu
1658 didn't use it and it's a keypress that is bound, it will
1659 close the menu and be used */
1663 /* if the keyboard interactive action uses the event then dont
1664 use it for bindings. likewise is moveresize uses the event. */
1665 if (!keyboard_process_interactive_grab(e
, &client
) &&
1666 !(moveresize_in_progress
&& moveresize_event(e
)))
1668 if (moveresize_in_progress
)
1669 /* make further actions work on the client being
1671 client
= moveresize_client
;
1673 menu_can_hide
= FALSE
;
1674 ob_main_loop_timeout_add(ob_main_loop
,
1675 config_menu_hide_delay
* 1000,
1676 menu_hide_delay_func
,
1677 NULL
, g_direct_equal
, NULL
);
1679 if (e
->type
== ButtonPress
||
1680 e
->type
== ButtonRelease
||
1681 e
->type
== MotionNotify
)
1683 /* the frame may not be "visible" but they can still click on it
1684 in the case where it is animating before disappearing */
1685 if (!client
|| !frame_iconify_animating(client
->frame
))
1686 mouse_event(client
, e
);
1687 } else if (e
->type
== KeyPress
) {
1688 keyboard_event((focus_cycle_target
? focus_cycle_target
:
1689 (client
? client
: focus_client
)), e
);
1694 static gboolean
menu_hide_delay_func(gpointer data
)
1696 menu_can_hide
= TRUE
;
1697 return FALSE
; /* no repeat */
1700 static void focus_delay_dest(gpointer data
)
1705 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
)
1707 const ObFocusDelayData
*f1
= d1
;
1708 return f1
->client
== d2
;
1711 static gboolean
focus_delay_func(gpointer data
)
1713 ObFocusDelayData
*d
= data
;
1714 Time old
= event_curtime
;
1716 event_curtime
= d
->time
;
1717 if (focus_client
!= d
->client
) {
1718 if (client_focus(d
->client
) && config_focus_raise
)
1719 stacking_raise(CLIENT_AS_WINDOW(d
->client
));
1721 event_curtime
= old
;
1722 return FALSE
; /* no repeat */
1725 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1727 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1731 void event_halt_focus_delay()
1733 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1736 void event_ignore_queued_enters()
1738 GSList
*saved
= NULL
, *it
;
1741 XSync(ob_display
, FALSE
);
1743 /* count the events */
1745 e
= g_new(XEvent
, 1);
1746 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1749 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1750 if (win
&& WINDOW_IS_CLIENT(win
))
1751 ++ignore_enter_focus
;
1753 saved
= g_slist_append(saved
, e
);
1759 /* put the events back */
1760 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1761 XPutBackEvent(ob_display
, it
->data
);
1764 g_slist_free(saved
);
1767 gboolean
event_time_after(Time t1
, Time t2
)
1769 g_assert(t1
!= CurrentTime
);
1770 g_assert(t2
!= CurrentTime
);
1773 Timestamp values wrap around (after about 49.7 days). The server, given
1774 its current time is represented by timestamp T, always interprets
1775 timestamps from clients by treating half of the timestamp space as being
1776 later in time than T.
1777 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1780 /* TIME_HALF is half of the number space of a Time type variable */
1781 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1783 if (t2
>= TIME_HALF
)
1784 /* t2 is in the second half so t1 might wrap around and be smaller than
1786 return t1
>= t2
|| t1
< (t2
+ TIME_HALF
);
1788 /* t2 is in the first half so t1 has to come after it */
1789 return t1
>= t2
&& t1
< (t2
+ TIME_HALF
);