1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 event.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
33 #include "menuframe.h"
39 #include "framerender.h"
41 #include "moveresize.h"
44 #include "extensions.h"
45 #include "translate.h"
48 #include <X11/Xatom.h>
51 #ifdef HAVE_SYS_SELECT_H
52 # include <sys/select.h>
58 # include <unistd.h> /* for usleep() */
61 # include <X11/XKBlib.h>
65 #include <X11/ICE/ICElib.h>
79 static void event_process(const XEvent
*e
, gpointer data
);
80 static void event_handle_root(XEvent
*e
);
81 static gboolean
event_handle_menu_keyboard(XEvent
*e
);
82 static gboolean
event_handle_menu(XEvent
*e
);
83 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
84 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
85 static void event_handle_client(ObClient
*c
, XEvent
*e
);
86 static void event_handle_user_time_window_clients(GSList
*l
, XEvent
*e
);
87 static void event_handle_user_input(ObClient
*client
, XEvent
*e
);
88 static gboolean
is_enter_focus_event_ignored(XEvent
*e
);
90 static void focus_delay_dest(gpointer data
);
91 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
);
92 static gboolean
focus_delay_func(gpointer data
);
93 static void focus_delay_client_dest(ObClient
*client
, gpointer data
);
95 static gboolean
menu_hide_delay_func(gpointer data
);
97 /* The time for the current event being processed */
98 Time event_curtime
= CurrentTime
;
100 #define NUM_IGNORE_SERIALS 20
102 static guint ignore_enter_focus
= 0;
103 /*! This is a 0 terminated list of ignored serials */
104 static gulong ignore_enter_serials
[NUM_IGNORE_SERIALS
+1] = {0};
105 static gboolean menu_can_hide
;
106 static gboolean focus_left_screen
= FALSE
;
109 static void ice_handler(gint fd
, gpointer conn
)
112 IceProcessMessages(conn
, NULL
, &b
);
115 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
116 IcePointer
*watch_data
)
121 fd
= IceConnectionNumber(conn
);
122 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
124 ob_main_loop_fd_remove(ob_main_loop
, fd
);
130 void event_startup(gboolean reconfig
)
132 if (reconfig
) return;
134 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
137 IceAddConnectionWatch(ice_watch
, NULL
);
140 client_add_destroy_notify(focus_delay_client_dest
, NULL
);
143 void event_shutdown(gboolean reconfig
)
145 if (reconfig
) return;
148 IceRemoveConnectionWatch(ice_watch
, NULL
);
151 client_remove_destroy_notify(focus_delay_client_dest
);
154 static Window
event_get_window(XEvent
*e
)
161 window
= RootWindow(ob_display
, ob_screen
);
164 window
= e
->xmap
.window
;
167 window
= e
->xunmap
.window
;
170 window
= e
->xdestroywindow
.window
;
172 case ConfigureRequest
:
173 window
= e
->xconfigurerequest
.window
;
175 case ConfigureNotify
:
176 window
= e
->xconfigure
.window
;
180 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
181 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
183 window
= ((XkbBellNotifyEvent
*)e
)->window
;
190 if (extensions_sync
&&
191 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
196 window
= e
->xany
.window
;
201 static void event_set_curtime(XEvent
*e
)
203 Time t
= CurrentTime
;
205 /* grab the lasttime and hack up the state */
221 t
= e
->xproperty
.time
;
225 t
= e
->xcrossing
.time
;
229 if (extensions_sync
&&
230 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
232 t
= ((XSyncAlarmNotifyEvent
*)e
)->time
;
235 /* if more event types are anticipated, get their timestamp
243 static void event_hack_mods(XEvent
*e
)
246 XkbStateRec xkb_state
;
252 e
->xbutton
.state
= modkeys_only_modifier_masks(e
->xbutton
.state
);
255 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
258 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
260 if (XkbGetState(ob_display
, XkbUseCoreKbd
, &xkb_state
) == Success
) {
261 e
->xkey
.state
= xkb_state
.compat_state
;
265 /* remove from the state the mask of the modifier key being released,
266 if it is a modifier key being released that is */
267 e
->xkey
.state
&= ~modkeys_keycode_to_mask(e
->xkey
.keycode
);
270 e
->xmotion
.state
= modkeys_only_modifier_masks(e
->xmotion
.state
);
271 /* compress events */
274 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
276 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
277 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
284 static gboolean
wanted_focusevent(XEvent
*e
, gboolean in_client_only
)
286 gint mode
= e
->xfocus
.mode
;
287 gint detail
= e
->xfocus
.detail
;
288 Window win
= e
->xany
.window
;
290 if (e
->type
== FocusIn
) {
291 /* These are ones we never want.. */
293 /* This means focus was given by a keyboard/mouse grab. */
294 if (mode
== NotifyGrab
)
296 /* This means focus was given back from a keyboard/mouse grab. */
297 if (mode
== NotifyUngrab
)
300 /* These are the ones we want.. */
302 if (win
== RootWindow(ob_display
, ob_screen
)) {
303 /* If looking for a focus in on a client, then always return
304 FALSE for focus in's to the root window */
307 /* This means focus reverted off of a client */
308 else if (detail
== NotifyPointerRoot
||
309 detail
== NotifyDetailNone
||
310 detail
== NotifyInferior
)
316 /* It was on a client, was it a valid one?
317 It's possible to get a FocusIn event for a client that was managed
320 if (in_client_only
) {
321 ObWindow
*w
= g_hash_table_lookup(window_map
, &e
->xfocus
.window
);
322 if (!w
|| !WINDOW_IS_CLIENT(w
))
326 /* This means focus moved from the root window to a client */
327 if (detail
== NotifyVirtual
)
329 /* This means focus moved from one client to another */
330 if (detail
== NotifyNonlinearVirtual
)
336 g_assert(e
->type
== FocusOut
);
338 /* These are ones we never want.. */
340 /* This means focus was taken by a keyboard/mouse grab. */
341 if (mode
== NotifyGrab
)
344 /* Focus left the root window revertedto state */
345 if (win
== RootWindow(ob_display
, ob_screen
))
348 /* These are the ones we want.. */
350 /* This means focus moved from a client to the root window */
351 if (detail
== NotifyVirtual
)
353 /* This means focus moved from one client to another */
354 if (detail
== NotifyNonlinearVirtual
)
362 static Bool
event_look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
364 return e
->type
== FocusIn
&& wanted_focusevent(e
, FALSE
);
367 static Bool
event_look_for_focusin_client(Display
*d
, XEvent
*e
, XPointer arg
)
369 return e
->type
== FocusIn
&& wanted_focusevent(e
, TRUE
);
372 static void print_focusevent(XEvent
*e
)
374 gint mode
= e
->xfocus
.mode
;
375 gint detail
= e
->xfocus
.detail
;
376 Window win
= e
->xany
.window
;
377 const gchar
*modestr
, *detailstr
;
380 case NotifyNormal
: modestr
="NotifyNormal"; break;
381 case NotifyGrab
: modestr
="NotifyGrab"; break;
382 case NotifyUngrab
: modestr
="NotifyUngrab"; break;
383 case NotifyWhileGrabbed
: modestr
="NotifyWhileGrabbed"; break;
386 case NotifyAncestor
: detailstr
="NotifyAncestor"; break;
387 case NotifyVirtual
: detailstr
="NotifyVirtual"; break;
388 case NotifyInferior
: detailstr
="NotifyInferior"; break;
389 case NotifyNonlinear
: detailstr
="NotifyNonlinear"; break;
390 case NotifyNonlinearVirtual
: detailstr
="NotifyNonlinearVirtual"; break;
391 case NotifyPointer
: detailstr
="NotifyPointer"; break;
392 case NotifyPointerRoot
: detailstr
="NotifyPointerRoot"; break;
393 case NotifyDetailNone
: detailstr
="NotifyDetailNone"; break;
396 if (mode
== NotifyGrab
|| mode
== NotifyUngrab
)
401 ob_debug_type(OB_DEBUG_FOCUS
, "Focus%s 0x%x mode=%s detail=%s\n",
402 (e
->xfocus
.type
== FocusIn
? "In" : "Out"),
408 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
413 if (!wanted_focusevent(e
, FALSE
))
418 if (!wanted_focusevent(e
, FALSE
))
425 static void event_process(const XEvent
*ec
, gpointer data
)
428 ObClient
*client
= NULL
;
430 ObDockApp
*dockapp
= NULL
;
431 ObWindow
*obwin
= NULL
;
432 GSList
*timewinclients
= NULL
;
434 ObEventData
*ed
= data
;
436 /* make a copy we can mangle */
440 window
= event_get_window(e
);
441 if (e
->type
!= PropertyNotify
||
442 !(timewinclients
= propwin_get_clients(window
,
443 OB_PROPWIN_USER_TIME
)))
444 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
445 switch (obwin
->type
) {
447 dock
= WINDOW_AS_DOCK(obwin
);
450 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
453 client
= WINDOW_AS_CLIENT(obwin
);
456 case Window_Internal
:
457 /* not to be used for events */
458 g_assert_not_reached();
463 event_set_curtime(e
);
465 if (event_ignore(e
, client
)) {
472 /* deal with it in the kernel */
474 if (menu_frame_visible
&&
475 (e
->type
== EnterNotify
|| e
->type
== LeaveNotify
))
477 /* crossing events for menu */
478 event_handle_menu(e
);
479 } else if (e
->type
== FocusIn
) {
480 if (e
->xfocus
.detail
== NotifyPointerRoot
||
481 e
->xfocus
.detail
== NotifyDetailNone
||
482 e
->xfocus
.detail
== NotifyInferior
)
486 ob_debug_type(OB_DEBUG_FOCUS
, "Focus went to pointer root/none\n");
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 or nothing.
510 FocusOut events come after UnmapNotify, so we don't need to
511 worry about focusing an invalid window
514 if (!focus_left_screen
)
515 focus_fallback(TRUE
);
520 ob_debug_type(OB_DEBUG_FOCUS
,
521 "Focus went to a window that is already gone\n");
523 /* If you send focus to a window and then it disappears, you can
524 get the FocusIn for it, after it is unmanaged.
525 Just wait for the next FocusOut/FocusIn pair. */
527 else if (client
!= focus_client
) {
528 focus_left_screen
= FALSE
;
529 frame_adjust_focus(client
->frame
, TRUE
);
530 focus_set_client(client
);
531 client_calc_layer(client
);
532 client_bring_helper_windows(client
);
534 } else if (e
->type
== FocusOut
) {
535 gboolean nomove
= FALSE
;
538 /* Look for the followup FocusIn */
539 if (!XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin
, NULL
)) {
540 /* There is no FocusIn, this means focus went to a window that
541 is not being managed, or a window on another screen. */
545 xerror_set_ignore(TRUE
);
546 if (XGetInputFocus(ob_display
, &win
, &i
) != 0 &&
547 XGetGeometry(ob_display
, win
, &root
, &i
,&i
,&u
,&u
,&u
,&u
) != 0 &&
548 root
!= RootWindow(ob_display
, ob_screen
))
550 ob_debug_type(OB_DEBUG_FOCUS
,
551 "Focus went to another screen !\n");
552 focus_left_screen
= TRUE
;
555 ob_debug_type(OB_DEBUG_FOCUS
,
556 "Focus went to a black hole !\n");
557 xerror_set_ignore(FALSE
);
558 /* nothing is focused */
559 focus_set_client(NULL
);
560 } else if (ce
.xany
.window
== e
->xany
.window
) {
561 ob_debug_type(OB_DEBUG_FOCUS
, "Focus didn't go anywhere\n");
562 /* If focus didn't actually move anywhere, there is nothing to do*/
565 /* Focus did move, so process the FocusIn event */
566 ObEventData ed
= { .ignored
= FALSE
};
567 event_process(&ce
, &ed
);
569 /* The FocusIn was ignored, this means it was on a window
570 that isn't a client. */
571 ob_debug_type(OB_DEBUG_FOCUS
,
572 "Focus went to an unmanaged window 0x%x !\n",
574 focus_fallback(TRUE
);
578 if (client
&& !nomove
) {
579 frame_adjust_focus(client
->frame
, FALSE
);
580 if (client
== focus_client
)
581 focus_set_client(NULL
);
582 client_calc_layer(client
);
584 } else if (timewinclients
)
585 event_handle_user_time_window_clients(timewinclients
, e
);
587 event_handle_client(client
, e
);
589 event_handle_dockapp(dockapp
, e
);
591 event_handle_dock(dock
, e
);
592 else if (window
== RootWindow(ob_display
, ob_screen
))
593 event_handle_root(e
);
594 else if (e
->type
== MapRequest
)
595 client_manage(window
);
596 else if (e
->type
== ClientMessage
) {
597 /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
598 windows that are not managed yet. */
599 if (e
->xclient
.message_type
== prop_atoms
.net_request_frame_extents
) {
600 /* Pretend to manage the client, getting information used to
601 determine its decorations */
602 ObClient
*c
= client_fake_manage(e
->xclient
.window
);
605 /* set the frame extents on the window */
606 vals
[0] = c
->frame
->size
.left
;
607 vals
[1] = c
->frame
->size
.right
;
608 vals
[2] = c
->frame
->size
.top
;
609 vals
[3] = c
->frame
->size
.bottom
;
610 PROP_SETA32(e
->xclient
.window
, net_frame_extents
,
613 /* Free the pretend client */
614 client_fake_unmanage(c
);
617 else if (e
->type
== ConfigureRequest
) {
618 /* unhandled configure requests must be used to configure the
622 xwc
.x
= e
->xconfigurerequest
.x
;
623 xwc
.y
= e
->xconfigurerequest
.y
;
624 xwc
.width
= e
->xconfigurerequest
.width
;
625 xwc
.height
= e
->xconfigurerequest
.height
;
626 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
627 xwc
.sibling
= e
->xconfigurerequest
.above
;
628 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
630 /* we are not to be held responsible if someone sends us an
632 xerror_set_ignore(TRUE
);
633 XConfigureWindow(ob_display
, window
,
634 e
->xconfigurerequest
.value_mask
, &xwc
);
635 xerror_set_ignore(FALSE
);
638 else if (extensions_sync
&&
639 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
641 XSyncAlarmNotifyEvent
*se
= (XSyncAlarmNotifyEvent
*)e
;
642 if (se
->alarm
== moveresize_alarm
&& moveresize_in_progress
)
647 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
648 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
649 e
->type
== KeyRelease
)
651 event_handle_user_input(client
, e
);
654 /* if something happens and it's not from an XEvent, then we don't know
656 event_curtime
= CurrentTime
;
659 static void event_handle_root(XEvent
*e
)
665 ob_debug("Another WM has requested to replace us. Exiting.\n");
670 if (e
->xclient
.format
!= 32) break;
672 msgtype
= e
->xclient
.message_type
;
673 if (msgtype
== prop_atoms
.net_current_desktop
) {
674 guint d
= e
->xclient
.data
.l
[0];
675 if (d
< screen_num_desktops
) {
676 event_curtime
= e
->xclient
.data
.l
[1];
677 if (event_curtime
== 0)
678 ob_debug_type(OB_DEBUG_APP_BUGS
,
679 "_NET_CURRENT_DESKTOP message is missing "
681 screen_set_desktop(d
, TRUE
);
683 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
684 guint d
= e
->xclient
.data
.l
[0];
686 screen_set_num_desktops(d
);
687 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
688 screen_show_desktop(e
->xclient
.data
.l
[0] != 0, NULL
);
689 } else if (msgtype
== prop_atoms
.ob_control
) {
690 if (e
->xclient
.data
.l
[0] == 1)
692 else if (e
->xclient
.data
.l
[0] == 2)
697 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
698 screen_update_desktop_names();
699 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
700 screen_update_layout();
702 case ConfigureNotify
:
704 XRRUpdateConfiguration(e
);
713 void event_enter_client(ObClient
*client
)
715 g_assert(config_focus_follow
);
717 if (client_enter_focusable(client
) && client_can_focus(client
)) {
718 if (config_focus_delay
) {
719 ObFocusDelayData
*data
;
721 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
723 data
= g_new(ObFocusDelayData
, 1);
724 data
->client
= client
;
725 data
->time
= event_curtime
;
727 ob_main_loop_timeout_add(ob_main_loop
,
730 data
, focus_delay_cmp
, focus_delay_dest
);
732 ObFocusDelayData data
;
733 data
.client
= client
;
734 data
.time
= event_curtime
;
735 focus_delay_func(&data
);
740 static void event_handle_user_time_window_clients(GSList
*l
, XEvent
*e
)
742 g_assert(e
->type
== PropertyNotify
);
743 if (e
->xproperty
.atom
== prop_atoms
.net_wm_user_time
) {
744 for (; l
; l
= g_slist_next(l
))
745 client_update_user_time(l
->data
);
749 static void event_handle_client(ObClient
*client
, XEvent
*e
)
754 static gint px
= -1, py
= -1;
759 /* save where the press occured for the first button pressed */
761 pb
= e
->xbutton
.button
;
766 /* Wheel buttons don't draw because they are an instant click, so it
767 is a waste of resources to go drawing it.
768 if the user is doing an intereactive thing, or has a menu open then
769 the mouse is grabbed (possibly) and if we get these events we don't
770 want to deal with them
772 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5) &&
773 !keyboard_interactively_grabbed() &&
776 /* use where the press occured */
777 con
= frame_context(client
, e
->xbutton
.window
, px
, py
);
778 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
780 if (e
->type
== ButtonRelease
&& e
->xbutton
.button
== pb
)
781 pb
= 0, px
= py
= -1;
784 case OB_FRAME_CONTEXT_MAXIMIZE
:
785 client
->frame
->max_press
= (e
->type
== ButtonPress
);
786 framerender_frame(client
->frame
);
788 case OB_FRAME_CONTEXT_CLOSE
:
789 client
->frame
->close_press
= (e
->type
== ButtonPress
);
790 framerender_frame(client
->frame
);
792 case OB_FRAME_CONTEXT_ICONIFY
:
793 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
794 framerender_frame(client
->frame
);
796 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
797 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
798 framerender_frame(client
->frame
);
800 case OB_FRAME_CONTEXT_SHADE
:
801 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
802 framerender_frame(client
->frame
);
805 /* nothing changes with clicks for any other contexts */
811 con
= frame_context(client
, e
->xmotion
.window
,
812 e
->xmotion
.x
, e
->xmotion
.y
);
814 case OB_FRAME_CONTEXT_TITLEBAR
:
815 /* we've left the button area inside the titlebar */
816 if (client
->frame
->max_hover
|| client
->frame
->desk_hover
||
817 client
->frame
->shade_hover
|| client
->frame
->iconify_hover
||
818 client
->frame
->close_hover
)
820 client
->frame
->max_hover
= FALSE
;
821 client
->frame
->desk_hover
= FALSE
;
822 client
->frame
->shade_hover
= FALSE
;
823 client
->frame
->iconify_hover
= FALSE
;
824 client
->frame
->close_hover
= FALSE
;
825 frame_adjust_state(client
->frame
);
828 case OB_FRAME_CONTEXT_MAXIMIZE
:
829 if (!client
->frame
->max_hover
) {
830 client
->frame
->max_hover
= TRUE
;
831 frame_adjust_state(client
->frame
);
834 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
835 if (!client
->frame
->desk_hover
) {
836 client
->frame
->desk_hover
= TRUE
;
837 frame_adjust_state(client
->frame
);
840 case OB_FRAME_CONTEXT_SHADE
:
841 if (!client
->frame
->shade_hover
) {
842 client
->frame
->shade_hover
= TRUE
;
843 frame_adjust_state(client
->frame
);
846 case OB_FRAME_CONTEXT_ICONIFY
:
847 if (!client
->frame
->iconify_hover
) {
848 client
->frame
->iconify_hover
= TRUE
;
849 frame_adjust_state(client
->frame
);
852 case OB_FRAME_CONTEXT_CLOSE
:
853 if (!client
->frame
->close_hover
) {
854 client
->frame
->close_hover
= TRUE
;
855 frame_adjust_state(client
->frame
);
863 con
= frame_context(client
, e
->xcrossing
.window
,
864 e
->xcrossing
.x
, e
->xcrossing
.y
);
866 case OB_FRAME_CONTEXT_MAXIMIZE
:
867 client
->frame
->max_hover
= FALSE
;
868 frame_adjust_state(client
->frame
);
870 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
871 client
->frame
->desk_hover
= FALSE
;
872 frame_adjust_state(client
->frame
);
874 case OB_FRAME_CONTEXT_SHADE
:
875 client
->frame
->shade_hover
= FALSE
;
876 frame_adjust_state(client
->frame
);
878 case OB_FRAME_CONTEXT_ICONIFY
:
879 client
->frame
->iconify_hover
= FALSE
;
880 frame_adjust_state(client
->frame
);
882 case OB_FRAME_CONTEXT_CLOSE
:
883 client
->frame
->close_hover
= FALSE
;
884 frame_adjust_state(client
->frame
);
886 case OB_FRAME_CONTEXT_FRAME
:
887 /* When the mouse leaves an animating window, don't use the
888 corresponding enter events. Pretend like the animating window
889 doesn't even exist..! */
890 if (frame_iconify_animating(client
->frame
))
891 event_ignore_enters_leaving_window(client
);
893 ob_debug_type(OB_DEBUG_FOCUS
,
894 "%sNotify mode %d detail %d on %lx\n",
895 (e
->type
== EnterNotify
? "Enter" : "Leave"),
897 e
->xcrossing
.detail
, (client
?client
->window
:0));
898 if (keyboard_interactively_grabbed())
900 if (config_focus_follow
&& config_focus_delay
&&
901 /* leave inferior events can happen when the mouse goes onto
902 the window's border and then into the window before the
904 e
->xcrossing
.detail
!= NotifyInferior
)
906 ob_main_loop_timeout_remove_data(ob_main_loop
,
917 gboolean nofocus
= FALSE
;
919 if (is_enter_focus_event_ignored(e
))
922 con
= frame_context(client
, e
->xcrossing
.window
,
923 e
->xcrossing
.x
, e
->xcrossing
.y
);
925 case OB_FRAME_CONTEXT_MAXIMIZE
:
926 client
->frame
->max_hover
= TRUE
;
927 frame_adjust_state(client
->frame
);
929 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
930 client
->frame
->desk_hover
= TRUE
;
931 frame_adjust_state(client
->frame
);
933 case OB_FRAME_CONTEXT_SHADE
:
934 client
->frame
->shade_hover
= TRUE
;
935 frame_adjust_state(client
->frame
);
937 case OB_FRAME_CONTEXT_ICONIFY
:
938 client
->frame
->iconify_hover
= TRUE
;
939 frame_adjust_state(client
->frame
);
941 case OB_FRAME_CONTEXT_CLOSE
:
942 client
->frame
->close_hover
= TRUE
;
943 frame_adjust_state(client
->frame
);
945 case OB_FRAME_CONTEXT_FRAME
:
946 if (keyboard_interactively_grabbed())
948 if (e
->xcrossing
.mode
== NotifyGrab
||
949 e
->xcrossing
.mode
== NotifyUngrab
||
950 /*ignore enters when we're already in the window */
951 e
->xcrossing
.detail
== NotifyInferior
)
953 ob_debug_type(OB_DEBUG_FOCUS
,
954 "%sNotify mode %d detail %d on %lx IGNORED\n",
955 (e
->type
== EnterNotify
? "Enter" : "Leave"),
957 e
->xcrossing
.detail
, client
?client
->window
:0);
959 ob_debug_type(OB_DEBUG_FOCUS
,
960 "%sNotify mode %d detail %d on %lx, "
961 "focusing window: %d\n",
962 (e
->type
== EnterNotify
? "Enter" : "Leave"),
964 e
->xcrossing
.detail
, (client
?client
->window
:0),
966 if (!nofocus
&& config_focus_follow
)
967 event_enter_client(client
);
975 case ConfigureRequest
:
977 /* dont compress these unless you're going to watch for property
978 notifies in between (these can change what the configure would
980 also you can't compress stacking events
985 /* if nothing is changed, then a configurenotify is needed */
986 gboolean config
= TRUE
;
990 w
= client
->area
.width
;
991 h
= client
->area
.height
;
993 ob_debug("ConfigureRequest desktop %d wmstate %d visibile %d\n",
994 screen_desktop
, client
->wmstate
, client
->frame
->visible
);
996 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
997 if (client
->border_width
!= e
->xconfigurerequest
.border_width
) {
998 client
->border_width
= e
->xconfigurerequest
.border_width
;
999 /* if only the border width is changing, then it's not needed*/
1004 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
1005 ObClient
*sibling
= NULL
;
1007 /* get the sibling */
1008 if (e
->xconfigurerequest
.value_mask
& CWSibling
) {
1010 win
= g_hash_table_lookup(window_map
,
1011 &e
->xconfigurerequest
.above
);
1012 if (WINDOW_IS_CLIENT(win
) && WINDOW_AS_CLIENT(win
) != client
)
1013 sibling
= WINDOW_AS_CLIENT(win
);
1016 /* activate it rather than just focus it */
1017 stacking_restack_request(client
, sibling
,
1018 e
->xconfigurerequest
.detail
, TRUE
);
1020 /* if a stacking change is requested then it is needed */
1024 /* don't allow clients to move shaded windows (fvwm does this) */
1025 if (client
->shaded
&& (e
->xconfigurerequest
.value_mask
& CWX
||
1026 e
->xconfigurerequest
.value_mask
& CWY
))
1028 e
->xconfigurerequest
.value_mask
&= ~CWX
;
1029 e
->xconfigurerequest
.value_mask
&= ~CWY
;
1031 /* if the client tried to move and we aren't letting it then a
1032 synthetic event is needed */
1036 if (e
->xconfigurerequest
.value_mask
& CWX
||
1037 e
->xconfigurerequest
.value_mask
& CWY
||
1038 e
->xconfigurerequest
.value_mask
& CWWidth
||
1039 e
->xconfigurerequest
.value_mask
& CWHeight
)
1041 if (e
->xconfigurerequest
.value_mask
& CWX
)
1042 x
= e
->xconfigurerequest
.x
;
1043 if (e
->xconfigurerequest
.value_mask
& CWY
)
1044 y
= e
->xconfigurerequest
.y
;
1045 if (e
->xconfigurerequest
.value_mask
& CWWidth
)
1046 w
= e
->xconfigurerequest
.width
;
1047 if (e
->xconfigurerequest
.value_mask
& CWHeight
)
1048 h
= e
->xconfigurerequest
.height
;
1050 /* if a new position or size is requested, then a configure is
1055 ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d\n",
1056 e
->xconfigurerequest
.value_mask
& CWX
, x
,
1057 e
->xconfigurerequest
.value_mask
& CWY
, y
,
1058 e
->xconfigurerequest
.value_mask
& CWWidth
, w
,
1059 e
->xconfigurerequest
.value_mask
& CWHeight
, h
);
1061 /* check for broken apps moving to their root position
1063 XXX remove this some day...that would be nice. right now all
1064 kde apps do this when they try activate themselves on another
1065 desktop. eg. open amarok window on desktop 1, switch to desktop
1066 2, click amarok tray icon. it will move by its decoration size.
1068 if (x
!= client
->area
.x
&&
1069 x
== (client
->frame
->area
.x
+ client
->frame
->size
.left
-
1070 (gint
)client
->border_width
) &&
1071 y
!= client
->area
.y
&&
1072 y
== (client
->frame
->area
.y
+ client
->frame
->size
.top
-
1073 (gint
)client
->border_width
))
1075 ob_debug_type(OB_DEBUG_APP_BUGS
,
1076 "Application %s is trying to move via "
1077 "ConfigureRequest to it's root window position "
1078 "but it is not using StaticGravity\n",
1086 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1087 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
);
1089 /* ignore enter events caused by these like ob actions do */
1090 event_ignore_enters_leaving_window(client
);
1095 if (client
->ignore_unmaps
) {
1096 client
->ignore_unmaps
--;
1099 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
1100 "ignores left %d\n",
1101 client
->window
, e
->xunmap
.event
, e
->xunmap
.from_configure
,
1102 client
->ignore_unmaps
);
1103 client_unmanage(client
);
1106 ob_debug("DestroyNotify for window 0x%x\n", client
->window
);
1107 client_unmanage(client
);
1109 case ReparentNotify
:
1110 /* this is when the client is first taken captive in the frame */
1111 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
1114 This event is quite rare and is usually handled in unmapHandler.
1115 However, if the window is unmapped when the reparent event occurs,
1116 the window manager never sees it because an unmap event is not sent
1117 to an already unmapped window.
1120 /* we don't want the reparent event, put it back on the stack for the
1121 X server to deal with after we unmanage the window */
1122 XPutBackEvent(ob_display
, e
);
1124 ob_debug("ReparentNotify for window 0x%x\n", client
->window
);
1125 client_unmanage(client
);
1128 ob_debug("MapRequest for 0x%lx\n", client
->window
);
1129 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
1130 does, we don't want it!
1131 it can happen now when the window is on
1132 another desktop, but we still don't
1134 client_activate(client
, FALSE
, TRUE
);
1137 /* validate cuz we query stuff off the client here */
1138 if (!client_validate(client
)) break;
1140 if (e
->xclient
.format
!= 32) return;
1142 msgtype
= e
->xclient
.message_type
;
1143 if (msgtype
== prop_atoms
.wm_change_state
) {
1144 /* compress changes into a single change */
1145 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1147 /* XXX: it would be nice to compress ALL messages of a
1148 type, not just messages in a row without other
1149 message types between. */
1150 if (ce
.xclient
.message_type
!= msgtype
) {
1151 XPutBackEvent(ob_display
, &ce
);
1154 e
->xclient
= ce
.xclient
;
1156 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
1157 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
1158 /* compress changes into a single change */
1159 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1161 /* XXX: it would be nice to compress ALL messages of a
1162 type, not just messages in a row without other
1163 message types between. */
1164 if (ce
.xclient
.message_type
!= msgtype
) {
1165 XPutBackEvent(ob_display
, &ce
);
1168 e
->xclient
= ce
.xclient
;
1170 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
1171 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
1172 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
1174 } else if (msgtype
== prop_atoms
.net_wm_state
) {
1175 /* can't compress these */
1176 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1177 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
1178 e
->xclient
.data
.l
[0] == 1 ? "Add" :
1179 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1180 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
1182 client_set_state(client
, e
->xclient
.data
.l
[0],
1183 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
1185 /* ignore enter events caused by these like ob actions do */
1186 event_ignore_enters_leaving_window(client
);
1187 } else if (msgtype
== prop_atoms
.net_close_window
) {
1188 ob_debug("net_close_window for 0x%lx\n", client
->window
);
1189 client_close(client
);
1190 } else if (msgtype
== prop_atoms
.net_active_window
) {
1191 ob_debug("net_active_window for 0x%lx source=%s\n",
1193 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
1194 (e
->xclient
.data
.l
[0] == 1 ? "application" :
1195 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
1196 /* XXX make use of data.l[2] !? */
1197 event_curtime
= e
->xclient
.data
.l
[1];
1198 if (event_curtime
== 0)
1199 ob_debug_type(OB_DEBUG_APP_BUGS
,
1200 "_NET_ACTIVE_WINDOW message for window %s is "
1201 "missing a timestamp\n", client
->title
);
1202 client_activate(client
, FALSE
,
1203 (e
->xclient
.data
.l
[0] == 0 ||
1204 e
->xclient
.data
.l
[0] == 2));
1205 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
1206 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1207 client
->window
, e
->xclient
.data
.l
[2]);
1208 if ((Atom
)e
->xclient
.data
.l
[2] ==
1209 prop_atoms
.net_wm_moveresize_size_topleft
||
1210 (Atom
)e
->xclient
.data
.l
[2] ==
1211 prop_atoms
.net_wm_moveresize_size_top
||
1212 (Atom
)e
->xclient
.data
.l
[2] ==
1213 prop_atoms
.net_wm_moveresize_size_topright
||
1214 (Atom
)e
->xclient
.data
.l
[2] ==
1215 prop_atoms
.net_wm_moveresize_size_right
||
1216 (Atom
)e
->xclient
.data
.l
[2] ==
1217 prop_atoms
.net_wm_moveresize_size_right
||
1218 (Atom
)e
->xclient
.data
.l
[2] ==
1219 prop_atoms
.net_wm_moveresize_size_bottomright
||
1220 (Atom
)e
->xclient
.data
.l
[2] ==
1221 prop_atoms
.net_wm_moveresize_size_bottom
||
1222 (Atom
)e
->xclient
.data
.l
[2] ==
1223 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1224 (Atom
)e
->xclient
.data
.l
[2] ==
1225 prop_atoms
.net_wm_moveresize_size_left
||
1226 (Atom
)e
->xclient
.data
.l
[2] ==
1227 prop_atoms
.net_wm_moveresize_move
||
1228 (Atom
)e
->xclient
.data
.l
[2] ==
1229 prop_atoms
.net_wm_moveresize_size_keyboard
||
1230 (Atom
)e
->xclient
.data
.l
[2] ==
1231 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1233 moveresize_start(client
, e
->xclient
.data
.l
[0],
1234 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1235 e
->xclient
.data
.l
[2]);
1237 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1238 prop_atoms
.net_wm_moveresize_cancel
)
1239 moveresize_end(TRUE
);
1240 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1241 gint grav
, x
, y
, w
, h
;
1243 if (e
->xclient
.data
.l
[0] & 0xff)
1244 grav
= e
->xclient
.data
.l
[0] & 0xff;
1246 grav
= client
->gravity
;
1248 if (e
->xclient
.data
.l
[0] & 1 << 8)
1249 x
= e
->xclient
.data
.l
[1];
1252 if (e
->xclient
.data
.l
[0] & 1 << 9)
1253 y
= e
->xclient
.data
.l
[2];
1256 if (e
->xclient
.data
.l
[0] & 1 << 10)
1257 w
= e
->xclient
.data
.l
[3];
1259 w
= client
->area
.width
;
1260 if (e
->xclient
.data
.l
[0] & 1 << 11)
1261 h
= e
->xclient
.data
.l
[4];
1263 h
= client
->area
.height
;
1265 ob_debug("MOVERESIZE x %d %d y %d %d\n",
1266 e
->xclient
.data
.l
[0] & 1 << 8, x
,
1267 e
->xclient
.data
.l
[0] & 1 << 9, y
);
1268 client_convert_gravity(client
, grav
, &x
, &y
, w
, h
);
1269 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1271 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
);
1273 /* ignore enter events caused by these like ob actions do */
1274 event_ignore_enters_leaving_window(client
);
1275 } else if (msgtype
== prop_atoms
.net_restack_window
) {
1276 if (e
->xclient
.data
.l
[0] != 2) {
1277 ob_debug_type(OB_DEBUG_APP_BUGS
,
1278 "_NET_RESTACK_WINDOW sent for window %s with "
1279 "invalid source indication %ld\n",
1280 client
->title
, e
->xclient
.data
.l
[0]);
1282 ObClient
*sibling
= NULL
;
1283 if (e
->xclient
.data
.l
[1]) {
1284 ObWindow
*win
= g_hash_table_lookup(window_map
,
1285 &e
->xclient
.data
.l
[1]);
1286 if (WINDOW_IS_CLIENT(win
) &&
1287 WINDOW_AS_CLIENT(win
) != client
)
1289 sibling
= WINDOW_AS_CLIENT(win
);
1291 if (sibling
== NULL
)
1292 ob_debug_type(OB_DEBUG_APP_BUGS
,
1293 "_NET_RESTACK_WINDOW sent for window %s "
1294 "with invalid sibling 0x%x\n",
1295 client
->title
, e
->xclient
.data
.l
[1]);
1297 if (e
->xclient
.data
.l
[2] == Below
||
1298 e
->xclient
.data
.l
[2] == BottomIf
||
1299 e
->xclient
.data
.l
[2] == Above
||
1300 e
->xclient
.data
.l
[2] == TopIf
||
1301 e
->xclient
.data
.l
[2] == Opposite
)
1303 /* just raise, don't activate */
1304 stacking_restack_request(client
, sibling
,
1305 e
->xclient
.data
.l
[2], FALSE
);
1306 /* send a synthetic ConfigureNotify, cuz this is supposed
1307 to be like a ConfigureRequest. */
1308 client_reconfigure(client
);
1310 ob_debug_type(OB_DEBUG_APP_BUGS
,
1311 "_NET_RESTACK_WINDOW sent for window %s "
1312 "with invalid detail %d\n",
1313 client
->title
, e
->xclient
.data
.l
[2]);
1317 case PropertyNotify
:
1318 /* validate cuz we query stuff off the client here */
1319 if (!client_validate(client
)) break;
1321 /* compress changes to a single property into a single change */
1322 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1326 /* XXX: it would be nice to compress ALL changes to a property,
1327 not just changes in a row without other props between. */
1329 a
= ce
.xproperty
.atom
;
1330 b
= e
->xproperty
.atom
;
1334 if ((a
== prop_atoms
.net_wm_name
||
1335 a
== prop_atoms
.wm_name
||
1336 a
== prop_atoms
.net_wm_icon_name
||
1337 a
== prop_atoms
.wm_icon_name
)
1339 (b
== prop_atoms
.net_wm_name
||
1340 b
== prop_atoms
.wm_name
||
1341 b
== prop_atoms
.net_wm_icon_name
||
1342 b
== prop_atoms
.wm_icon_name
)) {
1345 if (a
== prop_atoms
.net_wm_icon
&&
1346 b
== prop_atoms
.net_wm_icon
)
1349 XPutBackEvent(ob_display
, &ce
);
1353 msgtype
= e
->xproperty
.atom
;
1354 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1355 client_update_normal_hints(client
);
1356 /* normal hints can make a window non-resizable */
1357 client_setup_decor_and_functions(client
);
1358 } else if (msgtype
== XA_WM_HINTS
) {
1359 client_update_wmhints(client
);
1360 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1361 client_update_transient_for(client
);
1362 client_get_type_and_transientness(client
);
1363 /* type may have changed, so update the layer */
1364 client_calc_layer(client
);
1365 client_setup_decor_and_functions(client
);
1366 } else if (msgtype
== prop_atoms
.net_wm_name
||
1367 msgtype
== prop_atoms
.wm_name
||
1368 msgtype
== prop_atoms
.net_wm_icon_name
||
1369 msgtype
== prop_atoms
.wm_icon_name
) {
1370 client_update_title(client
);
1371 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1372 client_update_protocols(client
);
1373 client_setup_decor_and_functions(client
);
1375 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1376 client_update_strut(client
);
1378 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1379 client_update_icons(client
);
1381 else if (msgtype
== prop_atoms
.net_wm_icon_geometry
) {
1382 client_update_icon_geometry(client
);
1384 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1385 client_update_user_time(client
);
1387 else if (msgtype
== prop_atoms
.net_wm_user_time_window
) {
1388 client_update_user_time_window(client
);
1391 else if (msgtype
== prop_atoms
.net_wm_sync_request_counter
) {
1392 client_update_sync_request_counter(client
);
1395 case ColormapNotify
:
1396 client_update_colormap(client
, e
->xcolormap
.colormap
);
1401 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1402 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1403 frame_adjust_shape(client
->frame
);
1409 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1413 if (e
->xbutton
.button
== 1)
1414 stacking_raise(DOCK_AS_WINDOW(s
));
1415 else if (e
->xbutton
.button
== 2)
1416 stacking_lower(DOCK_AS_WINDOW(s
));
1427 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1431 dock_app_drag(app
, &e
->xmotion
);
1434 if (app
->ignore_unmaps
) {
1435 app
->ignore_unmaps
--;
1438 dock_remove(app
, TRUE
);
1441 dock_remove(app
, FALSE
);
1443 case ReparentNotify
:
1444 dock_remove(app
, FALSE
);
1446 case ConfigureNotify
:
1447 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1452 static ObMenuFrame
* find_active_menu()
1455 ObMenuFrame
*ret
= NULL
;
1457 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1466 static ObMenuFrame
* find_active_or_last_menu()
1468 ObMenuFrame
*ret
= NULL
;
1470 ret
= find_active_menu();
1471 if (!ret
&& menu_frame_visible
)
1472 ret
= menu_frame_visible
->data
;
1476 static gboolean
event_handle_menu_keyboard(XEvent
*ev
)
1478 guint keycode
, state
;
1481 gboolean ret
= TRUE
;
1483 keycode
= ev
->xkey
.keycode
;
1484 state
= ev
->xkey
.state
;
1485 unikey
= translate_unichar(keycode
);
1487 frame
= find_active_or_last_menu();
1491 else if (keycode
== ob_keycode(OB_KEY_ESCAPE
) && state
== 0) {
1492 /* Escape closes the active menu */
1493 menu_frame_hide(frame
);
1496 else if (keycode
== ob_keycode(OB_KEY_RETURN
) && (state
== 0 ||
1497 state
== ControlMask
))
1499 /* Enter runs the active item or goes into the submenu.
1500 Control-Enter runs it without closing the menu. */
1502 menu_frame_select_next(frame
->child
);
1504 menu_entry_frame_execute(frame
->selected
, state
, ev
->xkey
.time
);
1507 else if (keycode
== ob_keycode(OB_KEY_LEFT
) && ev
->xkey
.state
== 0) {
1508 /* Left goes to the parent menu */
1509 menu_frame_select(frame
, NULL
, TRUE
);
1512 else if (keycode
== ob_keycode(OB_KEY_RIGHT
) && ev
->xkey
.state
== 0) {
1513 /* Right goes to the selected submenu */
1514 if (frame
->child
) menu_frame_select_next(frame
->child
);
1517 else if (keycode
== ob_keycode(OB_KEY_UP
) && state
== 0) {
1518 menu_frame_select_previous(frame
);
1521 else if (keycode
== ob_keycode(OB_KEY_DOWN
) && state
== 0) {
1522 menu_frame_select_next(frame
);
1525 /* keyboard accelerator shortcuts. */
1526 else if (ev
->xkey
.state
== 0 &&
1527 /* was it a valid key? */
1529 /* don't bother if the menu is empty. */
1534 ObMenuEntryFrame
*found
= NULL
;
1535 guint num_found
= 0;
1537 /* start after the selected one */
1538 start
= frame
->entries
;
1539 if (frame
->selected
) {
1540 for (it
= start
; frame
->selected
!= it
->data
; it
= g_list_next(it
))
1541 g_assert(it
!= NULL
); /* nothing was selected? */
1542 /* next with wraparound */
1543 start
= g_list_next(it
);
1544 if (start
== NULL
) start
= frame
->entries
;
1549 ObMenuEntryFrame
*e
= it
->data
;
1550 gunichar entrykey
= 0;
1552 if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
)
1553 entrykey
= e
->entry
->data
.normal
.shortcut
;
1554 else if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_SUBMENU
)
1555 entrykey
= e
->entry
->data
.submenu
.submenu
->shortcut
;
1557 if (unikey
== entrykey
) {
1558 if (found
== NULL
) found
= e
;
1562 /* next with wraparound */
1563 it
= g_list_next(it
);
1564 if (it
== NULL
) it
= frame
->entries
;
1565 } while (it
!= start
);
1568 if (found
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
&&
1571 menu_frame_select(frame
, found
, TRUE
);
1572 usleep(50000); /* highlight the item for a short bit so the
1573 user can see what happened */
1574 menu_entry_frame_execute(found
, state
, ev
->xkey
.time
);
1576 menu_frame_select(frame
, found
, TRUE
);
1578 menu_frame_select_next(frame
->child
);
1589 static gboolean
event_handle_menu(XEvent
*ev
)
1592 ObMenuEntryFrame
*e
;
1593 gboolean ret
= TRUE
;
1597 if ((ev
->xbutton
.button
< 4 || ev
->xbutton
.button
> 5)
1600 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1601 ev
->xbutton
.y_root
)))
1602 menu_entry_frame_execute(e
, ev
->xbutton
.state
,
1605 menu_frame_hide_all();
1609 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
))) {
1610 if (e
->ignore_enters
)
1613 menu_frame_select(e
->frame
, e
, FALSE
);
1617 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
)) &&
1618 (f
= find_active_menu()) && f
->selected
== e
&&
1619 e
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1621 menu_frame_select(e
->frame
, NULL
, FALSE
);
1625 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1626 ev
->xmotion
.y_root
)))
1627 menu_frame_select(e
->frame
, e
, FALSE
);
1630 ret
= event_handle_menu_keyboard(ev
);
1636 static void event_handle_user_input(ObClient
*client
, XEvent
*e
)
1638 g_assert(e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
1639 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
1640 e
->type
== KeyRelease
);
1642 if (menu_frame_visible
) {
1643 if (event_handle_menu(e
))
1644 /* don't use the event if the menu used it, but if the menu
1645 didn't use it and it's a keypress that is bound, it will
1646 close the menu and be used */
1650 /* if the keyboard interactive action uses the event then dont
1651 use it for bindings. likewise is moveresize uses the event. */
1652 if (!keyboard_process_interactive_grab(e
, &client
) &&
1653 !(moveresize_in_progress
&& moveresize_event(e
)))
1655 if (moveresize_in_progress
)
1656 /* make further actions work on the client being
1658 client
= moveresize_client
;
1660 menu_can_hide
= FALSE
;
1661 ob_main_loop_timeout_add(ob_main_loop
,
1662 config_menu_hide_delay
* 1000,
1663 menu_hide_delay_func
,
1664 NULL
, g_direct_equal
, NULL
);
1666 if (e
->type
== ButtonPress
||
1667 e
->type
== ButtonRelease
||
1668 e
->type
== MotionNotify
)
1670 /* the frame may not be "visible" but they can still click on it
1671 in the case where it is animating before disappearing */
1672 if (!client
|| !frame_iconify_animating(client
->frame
))
1673 mouse_event(client
, e
);
1674 } else if (e
->type
== KeyPress
) {
1675 keyboard_event((focus_cycle_target
? focus_cycle_target
:
1676 (client
? client
: focus_client
)), e
);
1681 static gboolean
menu_hide_delay_func(gpointer data
)
1683 menu_can_hide
= TRUE
;
1684 return FALSE
; /* no repeat */
1687 static void focus_delay_dest(gpointer data
)
1692 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
)
1694 const ObFocusDelayData
*f1
= d1
;
1695 return f1
->client
== d2
;
1698 static gboolean
focus_delay_func(gpointer data
)
1700 ObFocusDelayData
*d
= data
;
1701 Time old
= event_curtime
;
1703 event_curtime
= d
->time
;
1704 if (focus_client
!= d
->client
) {
1705 if (client_focus(d
->client
) && config_focus_raise
)
1706 stacking_raise(CLIENT_AS_WINDOW(d
->client
));
1708 event_curtime
= old
;
1709 return FALSE
; /* no repeat */
1712 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1714 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1718 void event_halt_focus_delay()
1720 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1723 struct ObLookForEnters
1726 gulong looking_for_enter
;
1729 static Bool
event_look_for_enters(Display
*d
, XEvent
*e
, XPointer arg
)
1731 struct ObLookForEnters
*lfe
= (struct ObLookForEnters
*)arg
;
1733 if (lfe
->c
!= NULL
&& e
->type
== LeaveNotify
) {
1734 if (g_hash_table_lookup(window_map
, &e
->xany
.window
) == lfe
->c
)
1735 /* found an event leaving this window */
1736 lfe
->looking_for_enter
= e
->xany
.serial
;
1737 } else if (e
->type
== EnterNotify
&&
1738 (lfe
->c
== NULL
|| e
->xany
.serial
== lfe
->looking_for_enter
))
1742 gboolean ignored
= FALSE
;
1744 /* make sure the serial isn't already being ignored */
1745 for (i
= 0; ignore_enter_serials
[i
] != 0 && !ignored
; ++i
) {
1746 if (ignore_enter_serials
[i
] == e
->xany
.serial
)
1751 /* found an enter for that leave, ignore it if it's going to
1753 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1754 if (win
&& WINDOW_IS_CLIENT(win
))
1755 ++ignore_enter_focus
;
1758 /* add it to the ignored list if there is room */
1759 if (i
< NUM_IGNORE_SERIALS
) {
1760 ignore_enter_serials
[i
] = e
->xany
.serial
;
1761 ignore_enter_serials
[i
+1] = 0;
1764 return False
; /* don't disrupt the queue order, just count them */
1767 void event_ignore_all_queued_enters()
1769 event_ignore_enters_leaving_window(NULL
);
1772 void event_ignore_enters_leaving_window(ObClient
*c
)
1774 struct ObLookForEnters lfe
;
1777 XSync(ob_display
, FALSE
);
1779 /* count the events without disrupting them */
1780 ignore_enter_focus
= 0;
1782 lfe
.looking_for_enter
= 0;
1783 XCheckIfEvent(ob_display
, &e
, event_look_for_enters
, (XPointer
)&lfe
);
1787 static gboolean
is_enter_focus_event_ignored(XEvent
*e
)
1789 g_assert(e
->type
== EnterNotify
);
1791 if (ignore_enter_focus
) {
1794 --ignore_enter_focus
;
1796 /* remove the serial */
1797 for (i
= 0; ignore_enter_serials
[i
] != 0; ++i
) {
1798 if (ignore_enter_serials
[i
] == e
->xany
.serial
) {
1799 for (; ignore_enter_serials
[i
+1] != 0; ++i
)
1800 ignore_enter_serials
[i
] = ignore_enter_serials
[i
+1];
1801 ignore_enter_serials
[i
] = 0;
1809 gboolean
event_time_after(Time t1
, Time t2
)
1811 g_assert(t1
!= CurrentTime
);
1812 g_assert(t2
!= CurrentTime
);
1815 Timestamp values wrap around (after about 49.7 days). The server, given
1816 its current time is represented by timestamp T, always interprets
1817 timestamps from clients by treating half of the timestamp space as being
1818 later in time than T.
1819 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1822 /* TIME_HALF is half of the number space of a Time type variable */
1823 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1825 if (t2
>= TIME_HALF
)
1826 /* t2 is in the second half so t1 might wrap around and be smaller than
1828 return t1
>= t2
|| t1
< (t2
+ TIME_HALF
);
1830 /* t2 is in the first half so t1 has to come after it */
1831 return t1
>= t2
&& t1
< (t2
+ TIME_HALF
);