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.
34 #include "menuframe.h"
40 #include "focus_cycle.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>
81 gulong start
; /* inclusive */
82 gulong end
; /* inclusive */
85 static void event_process(const XEvent
*e
, gpointer data
);
86 static void event_handle_root(XEvent
*e
);
87 static gboolean
event_handle_menu_keyboard(XEvent
*e
);
88 static gboolean
event_handle_menu(XEvent
*e
);
89 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
90 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
91 static void event_handle_client(ObClient
*c
, XEvent
*e
);
92 static void event_handle_user_input(ObClient
*client
, XEvent
*e
);
93 static gboolean
is_enter_focus_event_ignored(XEvent
*e
);
95 static void focus_delay_dest(gpointer data
);
96 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
);
97 static gboolean
focus_delay_func(gpointer data
);
98 static void focus_delay_client_dest(ObClient
*client
, gpointer data
);
100 Time event_curtime
= CurrentTime
;
101 Time event_last_user_time
= CurrentTime
;
103 static gboolean focus_left_screen
= FALSE
;
104 /*! A list of ObSerialRanges which are to be ignored for mouse enter events */
105 static GSList
*ignore_serials
= NULL
;
108 static void ice_handler(gint fd
, gpointer conn
)
111 IceProcessMessages(conn
, NULL
, &b
);
114 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
115 IcePointer
*watch_data
)
120 fd
= IceConnectionNumber(conn
);
121 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
123 ob_main_loop_fd_remove(ob_main_loop
, fd
);
129 void event_startup(gboolean reconfig
)
131 if (reconfig
) return;
133 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
136 IceAddConnectionWatch(ice_watch
, NULL
);
139 client_add_destroy_notify(focus_delay_client_dest
, NULL
);
142 void event_shutdown(gboolean reconfig
)
144 if (reconfig
) return;
147 IceRemoveConnectionWatch(ice_watch
, NULL
);
150 client_remove_destroy_notify(focus_delay_client_dest
);
153 static Window
event_get_window(XEvent
*e
)
160 window
= RootWindow(ob_display
, ob_screen
);
163 window
= e
->xmap
.window
;
166 window
= e
->xunmap
.window
;
169 window
= e
->xdestroywindow
.window
;
171 case ConfigureRequest
:
172 window
= e
->xconfigurerequest
.window
;
174 case ConfigureNotify
:
175 window
= e
->xconfigure
.window
;
179 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
180 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
182 window
= ((XkbBellNotifyEvent
*)e
)->window
;
189 if (extensions_sync
&&
190 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
195 window
= e
->xany
.window
;
200 static void event_set_curtime(XEvent
*e
)
202 Time t
= CurrentTime
;
204 /* grab the lasttime and hack up the state */
220 t
= e
->xproperty
.time
;
224 t
= e
->xcrossing
.time
;
228 if (extensions_sync
&&
229 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
231 t
= ((XSyncAlarmNotifyEvent
*)e
)->time
;
234 /* if more event types are anticipated, get their timestamp
239 /* watch that if we get an event earlier than the last specified user_time,
240 which can happen if the clock goes backwards, we erase the last
241 specified user_time */
242 if (t
&& event_last_user_time
&& event_time_after(event_last_user_time
, t
))
243 event_last_user_time
= CurrentTime
;
248 static void event_hack_mods(XEvent
*e
)
251 XkbStateRec xkb_state
;
257 e
->xbutton
.state
= modkeys_only_modifier_masks(e
->xbutton
.state
);
260 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
264 /* If XKB is present, then the modifiers are all strange from its
265 magic. Our X core protocol stuff won't work, so we use this to
266 find what the modifier state is instead. */
267 if (XkbGetState(ob_display
, XkbUseCoreKbd
, &xkb_state
) == Success
)
268 e
->xkey
.state
= xkb_state
.compat_state
;
272 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
273 /* remove from the state the mask of the modifier key being
274 released, if it is a modifier key being released that is */
275 e
->xkey
.state
&= ~modkeys_keycode_to_mask(e
->xkey
.keycode
);
279 e
->xmotion
.state
= modkeys_only_modifier_masks(e
->xmotion
.state
);
280 /* compress events */
283 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
285 e
->xmotion
.x
= ce
.xmotion
.x
;
286 e
->xmotion
.y
= ce
.xmotion
.y
;
287 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
288 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
295 static gboolean
wanted_focusevent(XEvent
*e
, gboolean in_client_only
)
297 gint mode
= e
->xfocus
.mode
;
298 gint detail
= e
->xfocus
.detail
;
299 Window win
= e
->xany
.window
;
301 if (e
->type
== FocusIn
) {
302 /* These are ones we never want.. */
304 /* This means focus was given by a keyboard/mouse grab. */
305 if (mode
== NotifyGrab
)
307 /* This means focus was given back from a keyboard/mouse grab. */
308 if (mode
== NotifyUngrab
)
311 /* These are the ones we want.. */
313 if (win
== RootWindow(ob_display
, ob_screen
)) {
314 /* If looking for a focus in on a client, then always return
315 FALSE for focus in's to the root window */
318 /* This means focus reverted off of a client */
319 else if (detail
== NotifyPointerRoot
||
320 detail
== NotifyDetailNone
||
321 detail
== NotifyInferior
||
322 /* This means focus got here from another screen */
323 detail
== NotifyNonlinear
)
329 /* It was on a client, was it a valid one?
330 It's possible to get a FocusIn event for a client that was managed
333 if (in_client_only
) {
334 ObWindow
*w
= g_hash_table_lookup(window_map
, &e
->xfocus
.window
);
335 if (!w
|| !WINDOW_IS_CLIENT(w
))
339 /* This means focus reverted to parent from the client (this
340 happens often during iconify animation) */
341 if (detail
== NotifyInferior
)
345 /* This means focus moved from the root window to a client */
346 if (detail
== NotifyVirtual
)
348 /* This means focus moved from one client to another */
349 if (detail
== NotifyNonlinearVirtual
)
355 g_assert(e
->type
== FocusOut
);
357 /* These are ones we never want.. */
359 /* This means focus was taken by a keyboard/mouse grab. */
360 if (mode
== NotifyGrab
)
362 /* This means focus was grabbed on a window and it was released. */
363 if (mode
== NotifyUngrab
)
366 /* Focus left the root window revertedto state */
367 if (win
== RootWindow(ob_display
, ob_screen
))
370 /* These are the ones we want.. */
372 /* This means focus moved from a client to the root window */
373 if (detail
== NotifyVirtual
)
375 /* This means focus moved from one client to another */
376 if (detail
== NotifyNonlinearVirtual
)
384 static Bool
event_look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
386 return e
->type
== FocusIn
&& wanted_focusevent(e
, FALSE
);
389 static Bool
event_look_for_focusin_client(Display
*d
, XEvent
*e
, XPointer arg
)
391 return e
->type
== FocusIn
&& wanted_focusevent(e
, TRUE
);
394 static void print_focusevent(XEvent
*e
)
396 gint mode
= e
->xfocus
.mode
;
397 gint detail
= e
->xfocus
.detail
;
398 Window win
= e
->xany
.window
;
399 const gchar
*modestr
, *detailstr
;
402 case NotifyNormal
: modestr
="NotifyNormal"; break;
403 case NotifyGrab
: modestr
="NotifyGrab"; break;
404 case NotifyUngrab
: modestr
="NotifyUngrab"; break;
405 case NotifyWhileGrabbed
: modestr
="NotifyWhileGrabbed"; break;
408 case NotifyAncestor
: detailstr
="NotifyAncestor"; break;
409 case NotifyVirtual
: detailstr
="NotifyVirtual"; break;
410 case NotifyInferior
: detailstr
="NotifyInferior"; break;
411 case NotifyNonlinear
: detailstr
="NotifyNonlinear"; break;
412 case NotifyNonlinearVirtual
: detailstr
="NotifyNonlinearVirtual"; break;
413 case NotifyPointer
: detailstr
="NotifyPointer"; break;
414 case NotifyPointerRoot
: detailstr
="NotifyPointerRoot"; break;
415 case NotifyDetailNone
: detailstr
="NotifyDetailNone"; break;
418 if (mode
== NotifyGrab
|| mode
== NotifyUngrab
)
423 ob_debug_type(OB_DEBUG_FOCUS
, "Focus%s 0x%x mode=%s detail=%s\n",
424 (e
->xfocus
.type
== FocusIn
? "In" : "Out"),
430 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
435 if (!wanted_focusevent(e
, FALSE
))
440 if (!wanted_focusevent(e
, FALSE
))
447 static void event_process(const XEvent
*ec
, gpointer data
)
450 ObClient
*client
= NULL
;
452 ObDockApp
*dockapp
= NULL
;
453 ObWindow
*obwin
= NULL
;
455 ObEventData
*ed
= data
;
457 /* make a copy we can mangle */
461 window
= event_get_window(e
);
462 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
463 switch (obwin
->type
) {
465 dock
= WINDOW_AS_DOCK(obwin
);
468 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
471 client
= WINDOW_AS_CLIENT(obwin
);
474 case Window_Internal
:
475 /* not to be used for events */
476 g_assert_not_reached();
481 event_set_curtime(e
);
483 if (event_ignore(e
, client
)) {
490 /* deal with it in the kernel */
492 if (menu_frame_visible
&&
493 (e
->type
== EnterNotify
|| e
->type
== LeaveNotify
))
495 /* crossing events for menu */
496 event_handle_menu(e
);
497 } else if (e
->type
== FocusIn
) {
499 e
->xfocus
.detail
== NotifyInferior
)
501 ob_debug_type(OB_DEBUG_FOCUS
,
502 "Focus went to the frame window");
504 focus_left_screen
= FALSE
;
506 focus_fallback(FALSE
, config_focus_under_mouse
, TRUE
, TRUE
);
508 /* We don't get a FocusOut for this case, because it's just moving
509 from our Inferior up to us. This happens when iconifying a
510 window with RevertToParent focus */
511 frame_adjust_focus(client
->frame
, FALSE
);
512 /* focus_set_client(NULL) has already been called */
513 client_calc_layer(client
);
515 else if (e
->xfocus
.detail
== NotifyPointerRoot
||
516 e
->xfocus
.detail
== NotifyDetailNone
||
517 e
->xfocus
.detail
== NotifyInferior
||
518 e
->xfocus
.detail
== NotifyNonlinear
)
522 ob_debug_type(OB_DEBUG_FOCUS
,
523 "Focus went to root or pointer root/none\n");
525 if (e
->xfocus
.detail
== NotifyInferior
||
526 e
->xfocus
.detail
== NotifyNonlinear
)
528 focus_left_screen
= FALSE
;
531 /* If another FocusIn is in the queue then don't fallback yet. This
532 fixes the fun case of:
533 window map -> send focusin
534 window unmap -> get focusout
535 window map -> send focusin
536 get first focus out -> fall back to something (new window
537 hasn't received focus yet, so something else) -> send focusin
538 which means the "something else" is the last thing to get a
539 focusin sent to it, so the new window doesn't end up with focus.
541 But if the other focus in is something like PointerRoot then we
542 still want to fall back.
544 if (XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin_client
,
547 XPutBackEvent(ob_display
, &ce
);
548 ob_debug_type(OB_DEBUG_FOCUS
,
549 " but another FocusIn is coming\n");
551 /* Focus has been reverted.
553 FocusOut events come after UnmapNotify, so we don't need to
554 worry about focusing an invalid window
557 if (!focus_left_screen
)
558 focus_fallback(FALSE
, config_focus_under_mouse
,
564 ob_debug_type(OB_DEBUG_FOCUS
,
565 "Focus went to a window that is already gone\n");
567 /* If you send focus to a window and then it disappears, you can
568 get the FocusIn for it, after it is unmanaged.
569 Just wait for the next FocusOut/FocusIn pair, but make note that
570 the window that was focused no longer is. */
571 focus_set_client(NULL
);
573 else if (client
!= focus_client
) {
574 focus_left_screen
= FALSE
;
575 frame_adjust_focus(client
->frame
, TRUE
);
576 focus_set_client(client
);
577 client_calc_layer(client
);
578 client_bring_helper_windows(client
);
580 } else if (e
->type
== FocusOut
) {
583 /* Look for the followup FocusIn */
584 if (!XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin
, NULL
)) {
585 /* There is no FocusIn, this means focus went to a window that
586 is not being managed, or a window on another screen. */
590 xerror_set_ignore(TRUE
);
591 if (XGetInputFocus(ob_display
, &win
, &i
) != 0 &&
592 XGetGeometry(ob_display
, win
, &root
, &i
,&i
,&u
,&u
,&u
,&u
) != 0 &&
593 root
!= RootWindow(ob_display
, ob_screen
))
595 ob_debug_type(OB_DEBUG_FOCUS
,
596 "Focus went to another screen !\n");
597 focus_left_screen
= TRUE
;
600 ob_debug_type(OB_DEBUG_FOCUS
,
601 "Focus went to a black hole !\n");
602 xerror_set_ignore(FALSE
);
603 /* nothing is focused */
604 focus_set_client(NULL
);
606 /* Focus moved, so process the FocusIn event */
607 ObEventData ed
= { .ignored
= FALSE
};
608 event_process(&ce
, &ed
);
610 /* The FocusIn was ignored, this means it was on a window
611 that isn't a client. */
612 ob_debug_type(OB_DEBUG_FOCUS
,
613 "Focus went to an unmanaged window 0x%x !\n",
615 focus_fallback(TRUE
, config_focus_under_mouse
, TRUE
, TRUE
);
619 if (client
&& client
!= focus_client
) {
620 frame_adjust_focus(client
->frame
, FALSE
);
621 /* focus_set_client(NULL) has already been called in this
622 section or by focus_fallback */
623 client_calc_layer(client
);
627 event_handle_client(client
, e
);
629 event_handle_dockapp(dockapp
, e
);
631 event_handle_dock(dock
, e
);
632 else if (window
== RootWindow(ob_display
, ob_screen
))
633 event_handle_root(e
);
634 else if (e
->type
== MapRequest
)
635 client_manage(window
);
636 else if (e
->type
== MappingNotify
) {
637 /* keyboard layout changes, reconfigure openbox. need to restart the
638 modkeys system, but also to reload the key bindings. */
641 else if (e
->type
== ClientMessage
) {
642 /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
643 windows that are not managed yet. */
644 if (e
->xclient
.message_type
== prop_atoms
.net_request_frame_extents
) {
645 /* Pretend to manage the client, getting information used to
646 determine its decorations */
647 ObClient
*c
= client_fake_manage(e
->xclient
.window
);
650 /* set the frame extents on the window */
651 vals
[0] = c
->frame
->size
.left
;
652 vals
[1] = c
->frame
->size
.right
;
653 vals
[2] = c
->frame
->size
.top
;
654 vals
[3] = c
->frame
->size
.bottom
;
655 PROP_SETA32(e
->xclient
.window
, net_frame_extents
,
658 /* Free the pretend client */
659 client_fake_unmanage(c
);
662 else if (e
->type
== ConfigureRequest
) {
663 /* unhandled configure requests must be used to configure the
667 xwc
.x
= e
->xconfigurerequest
.x
;
668 xwc
.y
= e
->xconfigurerequest
.y
;
669 xwc
.width
= e
->xconfigurerequest
.width
;
670 xwc
.height
= e
->xconfigurerequest
.height
;
671 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
672 xwc
.sibling
= e
->xconfigurerequest
.above
;
673 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
675 /* we are not to be held responsible if someone sends us an
677 xerror_set_ignore(TRUE
);
678 XConfigureWindow(ob_display
, window
,
679 e
->xconfigurerequest
.value_mask
, &xwc
);
680 xerror_set_ignore(FALSE
);
683 else if (extensions_sync
&&
684 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
686 XSyncAlarmNotifyEvent
*se
= (XSyncAlarmNotifyEvent
*)e
;
687 if (se
->alarm
== moveresize_alarm
&& moveresize_in_progress
)
692 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
693 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
694 e
->type
== KeyRelease
)
696 event_handle_user_input(client
, e
);
699 /* if something happens and it's not from an XEvent, then we don't know
701 event_curtime
= CurrentTime
;
704 static void event_handle_root(XEvent
*e
)
710 ob_debug("Another WM has requested to replace us. Exiting.\n");
715 if (e
->xclient
.format
!= 32) break;
717 msgtype
= e
->xclient
.message_type
;
718 if (msgtype
== prop_atoms
.net_current_desktop
) {
719 guint d
= e
->xclient
.data
.l
[0];
720 if (d
< screen_num_desktops
) {
721 event_curtime
= e
->xclient
.data
.l
[1];
722 if (event_curtime
== 0)
723 ob_debug_type(OB_DEBUG_APP_BUGS
,
724 "_NET_CURRENT_DESKTOP message is missing "
726 screen_set_desktop(d
, TRUE
);
728 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
729 guint d
= e
->xclient
.data
.l
[0];
730 if (d
> 0 && d
<= 1000)
731 screen_set_num_desktops(d
);
732 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
733 screen_show_desktop(e
->xclient
.data
.l
[0] != 0, NULL
);
734 } else if (msgtype
== prop_atoms
.ob_control
) {
735 ob_debug("OB_CONTROL: %d\n", e
->xclient
.data
.l
[0]);
736 if (e
->xclient
.data
.l
[0] == 1)
738 else if (e
->xclient
.data
.l
[0] == 2)
740 else if (e
->xclient
.data
.l
[0] == 3)
745 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
) {
746 ob_debug("UPDATE DESKTOP NAMES\n");
747 screen_update_desktop_names();
749 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
750 screen_update_layout();
752 case ConfigureNotify
:
754 XRRUpdateConfiguration(e
);
763 void event_enter_client(ObClient
*client
)
765 g_assert(config_focus_follow
);
767 if (client_enter_focusable(client
) && client_can_focus(client
)) {
768 if (config_focus_delay
) {
769 ObFocusDelayData
*data
;
771 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
773 data
= g_new(ObFocusDelayData
, 1);
774 data
->client
= client
;
775 data
->time
= event_curtime
;
777 ob_main_loop_timeout_add(ob_main_loop
,
778 config_focus_delay
* 1000,
780 data
, focus_delay_cmp
, focus_delay_dest
);
782 ObFocusDelayData data
;
783 data
.client
= client
;
784 data
.time
= event_curtime
;
785 focus_delay_func(&data
);
790 static void event_handle_client(ObClient
*client
, XEvent
*e
)
795 static gint px
= -1, py
= -1;
800 /* save where the press occured for the first button pressed */
802 pb
= e
->xbutton
.button
;
807 /* Wheel buttons don't draw because they are an instant click, so it
808 is a waste of resources to go drawing it.
809 if the user is doing an intereactive thing, or has a menu open then
810 the mouse is grabbed (possibly) and if we get these events we don't
811 want to deal with them
813 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5) &&
816 /* use where the press occured */
817 con
= frame_context(client
, e
->xbutton
.window
, px
, py
);
818 con
= mouse_button_frame_context(con
, e
->xbutton
.button
,
821 if (e
->type
== ButtonRelease
&& e
->xbutton
.button
== pb
)
822 pb
= 0, px
= py
= -1;
825 case OB_FRAME_CONTEXT_MAXIMIZE
:
826 client
->frame
->max_press
= (e
->type
== ButtonPress
);
827 frame_adjust_state(client
->frame
);
829 case OB_FRAME_CONTEXT_CLOSE
:
830 client
->frame
->close_press
= (e
->type
== ButtonPress
);
831 frame_adjust_state(client
->frame
);
833 case OB_FRAME_CONTEXT_ICONIFY
:
834 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
835 frame_adjust_state(client
->frame
);
837 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
838 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
839 frame_adjust_state(client
->frame
);
841 case OB_FRAME_CONTEXT_SHADE
:
842 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
843 frame_adjust_state(client
->frame
);
846 /* nothing changes with clicks for any other contexts */
852 /* when there is a grab on the pointer, we won't get enter/leave
853 notifies, but we still get motion events */
854 if (grab_on_pointer()) break;
856 con
= frame_context(client
, e
->xmotion
.window
,
857 e
->xmotion
.x
, e
->xmotion
.y
);
859 case OB_FRAME_CONTEXT_TITLEBAR
:
860 case OB_FRAME_CONTEXT_TLCORNER
:
861 case OB_FRAME_CONTEXT_TRCORNER
:
862 /* we've left the button area inside the titlebar */
863 if (client
->frame
->max_hover
|| client
->frame
->desk_hover
||
864 client
->frame
->shade_hover
|| client
->frame
->iconify_hover
||
865 client
->frame
->close_hover
)
867 client
->frame
->max_hover
= FALSE
;
868 client
->frame
->desk_hover
= FALSE
;
869 client
->frame
->shade_hover
= FALSE
;
870 client
->frame
->iconify_hover
= FALSE
;
871 client
->frame
->close_hover
= FALSE
;
872 frame_adjust_state(client
->frame
);
875 case OB_FRAME_CONTEXT_MAXIMIZE
:
876 if (!client
->frame
->max_hover
) {
877 client
->frame
->max_hover
= TRUE
;
878 frame_adjust_state(client
->frame
);
881 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
882 if (!client
->frame
->desk_hover
) {
883 client
->frame
->desk_hover
= TRUE
;
884 frame_adjust_state(client
->frame
);
887 case OB_FRAME_CONTEXT_SHADE
:
888 if (!client
->frame
->shade_hover
) {
889 client
->frame
->shade_hover
= TRUE
;
890 frame_adjust_state(client
->frame
);
893 case OB_FRAME_CONTEXT_ICONIFY
:
894 if (!client
->frame
->iconify_hover
) {
895 client
->frame
->iconify_hover
= TRUE
;
896 frame_adjust_state(client
->frame
);
899 case OB_FRAME_CONTEXT_CLOSE
:
900 if (!client
->frame
->close_hover
) {
901 client
->frame
->close_hover
= TRUE
;
902 frame_adjust_state(client
->frame
);
910 con
= frame_context(client
, e
->xcrossing
.window
,
911 e
->xcrossing
.x
, e
->xcrossing
.y
);
913 case OB_FRAME_CONTEXT_TITLEBAR
:
914 case OB_FRAME_CONTEXT_TLCORNER
:
915 case OB_FRAME_CONTEXT_TRCORNER
:
916 /* we've left the button area inside the titlebar */
917 if (client
->frame
->max_hover
|| client
->frame
->desk_hover
||
918 client
->frame
->shade_hover
|| client
->frame
->iconify_hover
||
919 client
->frame
->close_hover
)
921 client
->frame
->max_hover
= FALSE
;
922 client
->frame
->desk_hover
= FALSE
;
923 client
->frame
->shade_hover
= FALSE
;
924 client
->frame
->iconify_hover
= FALSE
;
925 client
->frame
->close_hover
= FALSE
;
926 frame_adjust_state(client
->frame
);
929 case OB_FRAME_CONTEXT_MAXIMIZE
:
930 client
->frame
->max_hover
= FALSE
;
931 frame_adjust_state(client
->frame
);
933 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
934 client
->frame
->desk_hover
= FALSE
;
935 frame_adjust_state(client
->frame
);
937 case OB_FRAME_CONTEXT_SHADE
:
938 client
->frame
->shade_hover
= FALSE
;
939 frame_adjust_state(client
->frame
);
941 case OB_FRAME_CONTEXT_ICONIFY
:
942 client
->frame
->iconify_hover
= FALSE
;
943 frame_adjust_state(client
->frame
);
945 case OB_FRAME_CONTEXT_CLOSE
:
946 client
->frame
->close_hover
= FALSE
;
947 frame_adjust_state(client
->frame
);
949 case OB_FRAME_CONTEXT_FRAME
:
950 /* When the mouse leaves an animating window, don't use the
951 corresponding enter events. Pretend like the animating window
952 doesn't even exist..! */
953 if (frame_iconify_animating(client
->frame
))
954 event_end_ignore_all_enters(event_start_ignore_all_enters());
956 ob_debug_type(OB_DEBUG_FOCUS
,
957 "%sNotify mode %d detail %d on %lx\n",
958 (e
->type
== EnterNotify
? "Enter" : "Leave"),
960 e
->xcrossing
.detail
, (client
?client
->window
:0));
961 if (grab_on_keyboard())
963 if (config_focus_follow
&& config_focus_delay
&&
964 /* leave inferior events can happen when the mouse goes onto
965 the window's border and then into the window before the
967 e
->xcrossing
.detail
!= NotifyInferior
)
969 ob_main_loop_timeout_remove_data(ob_main_loop
,
980 con
= frame_context(client
, e
->xcrossing
.window
,
981 e
->xcrossing
.x
, e
->xcrossing
.y
);
983 case OB_FRAME_CONTEXT_MAXIMIZE
:
984 client
->frame
->max_hover
= TRUE
;
985 frame_adjust_state(client
->frame
);
987 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
988 client
->frame
->desk_hover
= TRUE
;
989 frame_adjust_state(client
->frame
);
991 case OB_FRAME_CONTEXT_SHADE
:
992 client
->frame
->shade_hover
= TRUE
;
993 frame_adjust_state(client
->frame
);
995 case OB_FRAME_CONTEXT_ICONIFY
:
996 client
->frame
->iconify_hover
= TRUE
;
997 frame_adjust_state(client
->frame
);
999 case OB_FRAME_CONTEXT_CLOSE
:
1000 client
->frame
->close_hover
= TRUE
;
1001 frame_adjust_state(client
->frame
);
1003 case OB_FRAME_CONTEXT_FRAME
:
1004 if (grab_on_keyboard())
1006 if (e
->xcrossing
.mode
== NotifyGrab
||
1007 e
->xcrossing
.mode
== NotifyUngrab
||
1008 /*ignore enters when we're already in the window */
1009 e
->xcrossing
.detail
== NotifyInferior
||
1010 is_enter_focus_event_ignored(e
))
1012 ob_debug_type(OB_DEBUG_FOCUS
,
1013 "%sNotify mode %d detail %d on %lx IGNORED\n",
1014 (e
->type
== EnterNotify
? "Enter" : "Leave"),
1016 e
->xcrossing
.detail
, client
?client
->window
:0);
1019 ob_debug_type(OB_DEBUG_FOCUS
,
1020 "%sNotify mode %d detail %d on %lx, "
1021 "focusing window\n",
1022 (e
->type
== EnterNotify
? "Enter" : "Leave"),
1024 e
->xcrossing
.detail
, (client
?client
->window
:0));
1025 if (config_focus_follow
)
1026 event_enter_client(client
);
1034 case ConfigureRequest
:
1036 /* dont compress these unless you're going to watch for property
1037 notifies in between (these can change what the configure would
1039 also you can't compress stacking events
1043 gboolean move
= FALSE
;
1044 gboolean resize
= FALSE
;
1046 /* get the current area */
1047 RECT_TO_DIMS(client
->area
, x
, y
, w
, h
);
1049 ob_debug("ConfigureRequest for \"%s\" desktop %d wmstate %d "
1051 " x %d y %d w %d h %d b %d\n",
1053 screen_desktop
, client
->wmstate
, client
->frame
->visible
,
1054 x
, y
, w
, h
, client
->border_width
);
1056 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
1057 if (client
->border_width
!= e
->xconfigurerequest
.border_width
) {
1058 client
->border_width
= e
->xconfigurerequest
.border_width
;
1060 /* if the border width is changing then that is the same
1061 as requesting a resize, but we don't actually change
1062 the client's border, so it will change their root
1063 coordiantes (since they include the border width) and
1064 we need to a notify then */
1069 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
1070 ObClient
*sibling
= NULL
;
1071 gulong ignore_start
;
1074 /* get the sibling */
1075 if (e
->xconfigurerequest
.value_mask
& CWSibling
) {
1077 win
= g_hash_table_lookup(window_map
,
1078 &e
->xconfigurerequest
.above
);
1079 if (win
&& WINDOW_IS_CLIENT(win
) &&
1080 WINDOW_AS_CLIENT(win
) != client
)
1082 sibling
= WINDOW_AS_CLIENT(win
);
1085 /* an invalid sibling was specified so don't restack at
1086 all, it won't make sense no matter what we do */
1091 if (!config_focus_under_mouse
)
1092 ignore_start
= event_start_ignore_all_enters();
1093 stacking_restack_request(client
, sibling
,
1094 e
->xconfigurerequest
.detail
);
1095 if (!config_focus_under_mouse
)
1096 event_end_ignore_all_enters(ignore_start
);
1099 /* a stacking change moves the window without resizing */
1103 if ((e
->xconfigurerequest
.value_mask
& CWX
) ||
1104 (e
->xconfigurerequest
.value_mask
& CWY
) ||
1105 (e
->xconfigurerequest
.value_mask
& CWWidth
) ||
1106 (e
->xconfigurerequest
.value_mask
& CWHeight
))
1108 if (e
->xconfigurerequest
.value_mask
& CWX
) {
1109 /* don't allow clients to move shaded windows (fvwm does this)
1111 if (!client
->shaded
)
1112 x
= e
->xconfigurerequest
.x
;
1115 if (e
->xconfigurerequest
.value_mask
& CWY
) {
1116 /* don't allow clients to move shaded windows (fvwm does this)
1118 if (!client
->shaded
)
1119 y
= e
->xconfigurerequest
.y
;
1123 if (e
->xconfigurerequest
.value_mask
& CWWidth
) {
1124 w
= e
->xconfigurerequest
.width
;
1127 if (e
->xconfigurerequest
.value_mask
& CWHeight
) {
1128 h
= e
->xconfigurerequest
.height
;
1133 ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d "
1134 "move %d resize %d\n",
1135 e
->xconfigurerequest
.value_mask
& CWX
, x
,
1136 e
->xconfigurerequest
.value_mask
& CWY
, y
,
1137 e
->xconfigurerequest
.value_mask
& CWWidth
, w
,
1138 e
->xconfigurerequest
.value_mask
& CWHeight
, h
,
1141 /* check for broken apps moving to their root position
1143 XXX remove this some day...that would be nice. right now all
1144 kde apps do this when they try activate themselves on another
1145 desktop. eg. open amarok window on desktop 1, switch to desktop
1146 2, click amarok tray icon. it will move by its decoration size.
1148 if (x
!= client
->area
.x
&&
1149 x
== (client
->frame
->area
.x
+ client
->frame
->size
.left
-
1150 (gint
)client
->border_width
) &&
1151 y
!= client
->area
.y
&&
1152 y
== (client
->frame
->area
.y
+ client
->frame
->size
.top
-
1153 (gint
)client
->border_width
) &&
1154 w
== client
->area
.width
&&
1155 h
== client
->area
.height
)
1157 ob_debug_type(OB_DEBUG_APP_BUGS
,
1158 "Application %s is trying to move via "
1159 "ConfigureRequest to it's root window position "
1160 "but it is not using StaticGravity\n",
1166 /* they still requested a move, so don't change whether a
1167 notify is sent or not */
1173 client_try_configure(client
, &x
, &y
, &w
, &h
, &lw
, &lh
, FALSE
);
1175 /* if x was not given, then use gravity to figure out the new
1176 x. the reference point should not be moved */
1177 if ((e
->xconfigurerequest
.value_mask
& CWWidth
&&
1178 !(e
->xconfigurerequest
.value_mask
& CWX
)))
1179 client_gravity_resize_w(client
, &x
, client
->area
.width
, w
);
1180 /* if y was not given, then use gravity to figure out the new
1181 y. the reference point should not be moved */
1182 if ((e
->xconfigurerequest
.value_mask
& CWHeight
&&
1183 !(e
->xconfigurerequest
.value_mask
& CWY
)))
1184 client_gravity_resize_h(client
, &y
, client
->area
.height
,h
);
1186 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1188 ob_debug("Granting ConfigureRequest x %d y %d w %d h %d\n",
1190 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
, TRUE
);
1195 if (client
->ignore_unmaps
) {
1196 client
->ignore_unmaps
--;
1199 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
1200 "ignores left %d\n",
1201 client
->window
, e
->xunmap
.event
, e
->xunmap
.from_configure
,
1202 client
->ignore_unmaps
);
1203 client_unmanage(client
);
1206 ob_debug("DestroyNotify for window 0x%x\n", client
->window
);
1207 client_unmanage(client
);
1209 case ReparentNotify
:
1210 /* this is when the client is first taken captive in the frame */
1211 if (e
->xreparent
.parent
== client
->frame
->window
) break;
1214 This event is quite rare and is usually handled in unmapHandler.
1215 However, if the window is unmapped when the reparent event occurs,
1216 the window manager never sees it because an unmap event is not sent
1217 to an already unmapped window.
1220 /* we don't want the reparent event, put it back on the stack for the
1221 X server to deal with after we unmanage the window */
1222 XPutBackEvent(ob_display
, e
);
1224 ob_debug("ReparentNotify for window 0x%x\n", client
->window
);
1225 client_unmanage(client
);
1228 ob_debug("MapRequest for 0x%lx\n", client
->window
);
1229 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
1230 does, we don't want it!
1231 it can happen now when the window is on
1232 another desktop, but we still don't
1234 client_activate(client
, FALSE
, TRUE
, TRUE
, TRUE
);
1237 /* validate cuz we query stuff off the client here */
1238 if (!client_validate(client
)) break;
1240 if (e
->xclient
.format
!= 32) return;
1242 msgtype
= e
->xclient
.message_type
;
1243 if (msgtype
== prop_atoms
.wm_change_state
) {
1244 /* compress changes into a single change */
1245 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1247 /* XXX: it would be nice to compress ALL messages of a
1248 type, not just messages in a row without other
1249 message types between. */
1250 if (ce
.xclient
.message_type
!= msgtype
) {
1251 XPutBackEvent(ob_display
, &ce
);
1254 e
->xclient
= ce
.xclient
;
1256 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
1257 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
1258 /* compress changes into a single change */
1259 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1261 /* XXX: it would be nice to compress ALL messages of a
1262 type, not just messages in a row without other
1263 message types between. */
1264 if (ce
.xclient
.message_type
!= msgtype
) {
1265 XPutBackEvent(ob_display
, &ce
);
1268 e
->xclient
= ce
.xclient
;
1270 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
1271 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
1272 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
1274 } else if (msgtype
== prop_atoms
.net_wm_state
) {
1275 gulong ignore_start
;
1277 /* can't compress these */
1278 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1279 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
1280 e
->xclient
.data
.l
[0] == 1 ? "Add" :
1281 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1282 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
1285 /* ignore enter events caused by these like ob actions do */
1286 if (!config_focus_under_mouse
)
1287 ignore_start
= event_start_ignore_all_enters();
1288 client_set_state(client
, e
->xclient
.data
.l
[0],
1289 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
1290 if (!config_focus_under_mouse
)
1291 event_end_ignore_all_enters(ignore_start
);
1292 } else if (msgtype
== prop_atoms
.net_close_window
) {
1293 ob_debug("net_close_window for 0x%lx\n", client
->window
);
1294 client_close(client
);
1295 } else if (msgtype
== prop_atoms
.net_active_window
) {
1296 ob_debug("net_active_window for 0x%lx source=%s\n",
1298 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
1299 (e
->xclient
.data
.l
[0] == 1 ? "application" :
1300 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
1301 /* XXX make use of data.l[2] !? */
1302 if (e
->xclient
.data
.l
[0] == 1 || e
->xclient
.data
.l
[0] == 2) {
1303 /* don't use the user's timestamp for client_focus, cuz if it's
1304 an old broken timestamp (happens all the time) then focus
1305 won't move even though we're trying to move it
1306 event_curtime = e->xclient.data.l[1];*/
1307 if (e
->xclient
.data
.l
[1] == 0)
1308 ob_debug_type(OB_DEBUG_APP_BUGS
,
1309 "_NET_ACTIVE_WINDOW message for window %s is"
1310 " missing a timestamp\n", client
->title
);
1312 ob_debug_type(OB_DEBUG_APP_BUGS
,
1313 "_NET_ACTIVE_WINDOW message for window %s is "
1314 "missing source indication\n");
1315 client_activate(client
, FALSE
, TRUE
, TRUE
,
1316 (e
->xclient
.data
.l
[0] == 0 ||
1317 e
->xclient
.data
.l
[0] == 2));
1318 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
1319 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1320 client
->window
, e
->xclient
.data
.l
[2]);
1321 if ((Atom
)e
->xclient
.data
.l
[2] ==
1322 prop_atoms
.net_wm_moveresize_size_topleft
||
1323 (Atom
)e
->xclient
.data
.l
[2] ==
1324 prop_atoms
.net_wm_moveresize_size_top
||
1325 (Atom
)e
->xclient
.data
.l
[2] ==
1326 prop_atoms
.net_wm_moveresize_size_topright
||
1327 (Atom
)e
->xclient
.data
.l
[2] ==
1328 prop_atoms
.net_wm_moveresize_size_right
||
1329 (Atom
)e
->xclient
.data
.l
[2] ==
1330 prop_atoms
.net_wm_moveresize_size_right
||
1331 (Atom
)e
->xclient
.data
.l
[2] ==
1332 prop_atoms
.net_wm_moveresize_size_bottomright
||
1333 (Atom
)e
->xclient
.data
.l
[2] ==
1334 prop_atoms
.net_wm_moveresize_size_bottom
||
1335 (Atom
)e
->xclient
.data
.l
[2] ==
1336 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1337 (Atom
)e
->xclient
.data
.l
[2] ==
1338 prop_atoms
.net_wm_moveresize_size_left
||
1339 (Atom
)e
->xclient
.data
.l
[2] ==
1340 prop_atoms
.net_wm_moveresize_move
||
1341 (Atom
)e
->xclient
.data
.l
[2] ==
1342 prop_atoms
.net_wm_moveresize_size_keyboard
||
1343 (Atom
)e
->xclient
.data
.l
[2] ==
1344 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1346 moveresize_start(client
, e
->xclient
.data
.l
[0],
1347 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1348 e
->xclient
.data
.l
[2]);
1350 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1351 prop_atoms
.net_wm_moveresize_cancel
)
1352 moveresize_end(TRUE
);
1353 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1354 gint ograv
, x
, y
, w
, h
;
1356 ograv
= client
->gravity
;
1358 if (e
->xclient
.data
.l
[0] & 0xff)
1359 client
->gravity
= e
->xclient
.data
.l
[0] & 0xff;
1361 if (e
->xclient
.data
.l
[0] & 1 << 8)
1362 x
= e
->xclient
.data
.l
[1];
1365 if (e
->xclient
.data
.l
[0] & 1 << 9)
1366 y
= e
->xclient
.data
.l
[2];
1370 if (e
->xclient
.data
.l
[0] & 1 << 10) {
1371 w
= e
->xclient
.data
.l
[3];
1373 /* if x was not given, then use gravity to figure out the new
1374 x. the reference point should not be moved */
1375 if (!(e
->xclient
.data
.l
[0] & 1 << 8))
1376 client_gravity_resize_w(client
, &x
, client
->area
.width
, w
);
1379 w
= client
->area
.width
;
1381 if (e
->xclient
.data
.l
[0] & 1 << 11) {
1382 h
= e
->xclient
.data
.l
[4];
1384 /* if y was not given, then use gravity to figure out the new
1385 y. the reference point should not be moved */
1386 if (!(e
->xclient
.data
.l
[0] & 1 << 9))
1387 client_gravity_resize_h(client
, &y
, client
->area
.height
,h
);
1390 h
= client
->area
.height
;
1392 ob_debug("MOVERESIZE x %d %d y %d %d (gravity %d)\n",
1393 e
->xclient
.data
.l
[0] & 1 << 8, x
,
1394 e
->xclient
.data
.l
[0] & 1 << 9, y
,
1397 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1399 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
, FALSE
);
1401 client
->gravity
= ograv
;
1402 } else if (msgtype
== prop_atoms
.net_restack_window
) {
1403 if (e
->xclient
.data
.l
[0] != 2) {
1404 ob_debug_type(OB_DEBUG_APP_BUGS
,
1405 "_NET_RESTACK_WINDOW sent for window %s with "
1406 "invalid source indication %ld\n",
1407 client
->title
, e
->xclient
.data
.l
[0]);
1409 ObClient
*sibling
= NULL
;
1410 if (e
->xclient
.data
.l
[1]) {
1411 ObWindow
*win
= g_hash_table_lookup
1412 (window_map
, &e
->xclient
.data
.l
[1]);
1413 if (WINDOW_IS_CLIENT(win
) &&
1414 WINDOW_AS_CLIENT(win
) != client
)
1416 sibling
= WINDOW_AS_CLIENT(win
);
1418 if (sibling
== NULL
)
1419 ob_debug_type(OB_DEBUG_APP_BUGS
,
1420 "_NET_RESTACK_WINDOW sent for window %s "
1421 "with invalid sibling 0x%x\n",
1422 client
->title
, e
->xclient
.data
.l
[1]);
1424 if (e
->xclient
.data
.l
[2] == Below
||
1425 e
->xclient
.data
.l
[2] == BottomIf
||
1426 e
->xclient
.data
.l
[2] == Above
||
1427 e
->xclient
.data
.l
[2] == TopIf
||
1428 e
->xclient
.data
.l
[2] == Opposite
)
1430 gulong ignore_start
;
1432 if (!config_focus_under_mouse
)
1433 ignore_start
= event_start_ignore_all_enters();
1434 /* just raise, don't activate */
1435 stacking_restack_request(client
, sibling
,
1436 e
->xclient
.data
.l
[2]);
1437 if (!config_focus_under_mouse
)
1438 event_end_ignore_all_enters(ignore_start
);
1440 /* send a synthetic ConfigureNotify, cuz this is supposed
1441 to be like a ConfigureRequest. */
1442 client_reconfigure(client
, TRUE
);
1444 ob_debug_type(OB_DEBUG_APP_BUGS
,
1445 "_NET_RESTACK_WINDOW sent for window %s "
1446 "with invalid detail %d\n",
1447 client
->title
, e
->xclient
.data
.l
[2]);
1451 case PropertyNotify
:
1452 /* validate cuz we query stuff off the client here */
1453 if (!client_validate(client
)) break;
1455 /* compress changes to a single property into a single change */
1456 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1460 /* XXX: it would be nice to compress ALL changes to a property,
1461 not just changes in a row without other props between. */
1463 a
= ce
.xproperty
.atom
;
1464 b
= e
->xproperty
.atom
;
1468 if ((a
== prop_atoms
.net_wm_name
||
1469 a
== prop_atoms
.wm_name
||
1470 a
== prop_atoms
.net_wm_icon_name
||
1471 a
== prop_atoms
.wm_icon_name
)
1473 (b
== prop_atoms
.net_wm_name
||
1474 b
== prop_atoms
.wm_name
||
1475 b
== prop_atoms
.net_wm_icon_name
||
1476 b
== prop_atoms
.wm_icon_name
)) {
1479 if (a
== prop_atoms
.net_wm_icon
&&
1480 b
== prop_atoms
.net_wm_icon
)
1483 XPutBackEvent(ob_display
, &ce
);
1487 msgtype
= e
->xproperty
.atom
;
1488 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1489 ob_debug("Update NORMAL hints\n");
1490 client_update_normal_hints(client
);
1491 /* normal hints can make a window non-resizable */
1492 client_setup_decor_and_functions(client
, FALSE
);
1494 /* make sure the client's sizes are within its bounds, but only
1495 reconfigure the window if it needs to. emacs will update its
1496 normal hints every time it receives a conigurenotify */
1497 client_reconfigure(client
, FALSE
);
1498 } else if (msgtype
== XA_WM_HINTS
) {
1499 client_update_wmhints(client
);
1500 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1501 client_update_transient_for(client
);
1502 client_get_type_and_transientness(client
);
1503 /* type may have changed, so update the layer */
1504 client_calc_layer(client
);
1505 client_setup_decor_and_functions(client
, TRUE
);
1506 } else if (msgtype
== prop_atoms
.net_wm_name
||
1507 msgtype
== prop_atoms
.wm_name
||
1508 msgtype
== prop_atoms
.net_wm_icon_name
||
1509 msgtype
== prop_atoms
.wm_icon_name
) {
1510 client_update_title(client
);
1511 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1512 client_update_protocols(client
);
1513 client_setup_decor_and_functions(client
, TRUE
);
1515 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1516 client_update_strut(client
);
1518 else if (msgtype
== prop_atoms
.net_wm_strut_partial
) {
1519 client_update_strut(client
);
1521 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1522 client_update_icons(client
);
1524 else if (msgtype
== prop_atoms
.net_wm_icon_geometry
) {
1525 client_update_icon_geometry(client
);
1527 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1529 if (client
== focus_client
&&
1530 PROP_GET32(client
->window
, net_wm_user_time
, cardinal
, &t
) &&
1531 t
&& !event_time_after(t
, e
->xproperty
.time
) &&
1532 (!event_last_user_time
||
1533 event_time_after(t
, event_last_user_time
)))
1535 event_last_user_time
= t
;
1539 else if (msgtype
== prop_atoms
.net_wm_sync_request_counter
) {
1540 client_update_sync_request_counter(client
);
1544 case ColormapNotify
:
1545 client_update_colormap(client
, e
->xcolormap
.colormap
);
1550 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1551 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1552 frame_adjust_shape(client
->frame
);
1558 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1562 if (e
->xbutton
.button
== 1)
1563 stacking_raise(DOCK_AS_WINDOW(s
));
1564 else if (e
->xbutton
.button
== 2)
1565 stacking_lower(DOCK_AS_WINDOW(s
));
1571 /* don't hide when moving into a dock app */
1572 if (e
->xcrossing
.detail
!= NotifyInferior
)
1578 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1582 dock_app_drag(app
, &e
->xmotion
);
1585 if (app
->ignore_unmaps
) {
1586 app
->ignore_unmaps
--;
1589 dock_remove(app
, TRUE
);
1592 dock_remove(app
, FALSE
);
1594 case ReparentNotify
:
1595 dock_remove(app
, FALSE
);
1597 case ConfigureNotify
:
1598 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1603 static ObMenuFrame
* find_active_menu(void)
1606 ObMenuFrame
*ret
= NULL
;
1608 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1617 static ObMenuFrame
* find_active_or_last_menu(void)
1619 ObMenuFrame
*ret
= NULL
;
1621 ret
= find_active_menu();
1622 if (!ret
&& menu_frame_visible
)
1623 ret
= menu_frame_visible
->data
;
1627 static gboolean
event_handle_menu_keyboard(XEvent
*ev
)
1629 guint keycode
, state
;
1632 gboolean ret
= FALSE
;
1634 keycode
= ev
->xkey
.keycode
;
1635 state
= ev
->xkey
.state
;
1636 unikey
= translate_unichar(keycode
);
1638 frame
= find_active_or_last_menu();
1640 g_assert_not_reached(); /* there is no active menu */
1642 /* Allow control while going thru the menu */
1643 else if (ev
->type
== KeyPress
&& (state
& ~ControlMask
) == 0) {
1644 if (keycode
== ob_keycode(OB_KEY_ESCAPE
)) {
1645 menu_frame_hide_all();
1649 else if (keycode
== ob_keycode(OB_KEY_LEFT
)) {
1650 /* Left goes to the parent menu */
1651 menu_frame_select(frame
, NULL
, TRUE
);
1655 else if (keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1656 /* Right goes to the selected submenu */
1657 if (frame
->child
) menu_frame_select_next(frame
->child
);
1661 else if (keycode
== ob_keycode(OB_KEY_UP
)) {
1662 menu_frame_select_previous(frame
);
1666 else if (keycode
== ob_keycode(OB_KEY_DOWN
)) {
1667 menu_frame_select_next(frame
);
1672 /* Use KeyRelease events for running things so that the key release doesn't
1673 get sent to the focused application.
1675 Allow ControlMask only, and don't bother if the menu is empty */
1676 else if (ev
->type
== KeyRelease
&& (state
& ~ControlMask
) == 0 &&
1679 if (keycode
== ob_keycode(OB_KEY_RETURN
)) {
1680 /* Enter runs the active item or goes into the submenu.
1681 Control-Enter runs it without closing the menu. */
1683 menu_frame_select_next(frame
->child
);
1684 else if (frame
->selected
)
1685 menu_entry_frame_execute(frame
->selected
, state
);
1690 /* keyboard accelerator shortcuts. (if it was a valid key) */
1691 else if (unikey
!= 0) {
1694 ObMenuEntryFrame
*found
= NULL
;
1695 guint num_found
= 0;
1697 /* start after the selected one */
1698 start
= frame
->entries
;
1699 if (frame
->selected
) {
1700 for (it
= start
; frame
->selected
!= it
->data
;
1701 it
= g_list_next(it
))
1702 g_assert(it
!= NULL
); /* nothing was selected? */
1703 /* next with wraparound */
1704 start
= g_list_next(it
);
1705 if (start
== NULL
) start
= frame
->entries
;
1710 ObMenuEntryFrame
*e
= it
->data
;
1711 gunichar entrykey
= 0;
1713 if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
)
1714 entrykey
= e
->entry
->data
.normal
.shortcut
;
1715 else if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_SUBMENU
)
1716 entrykey
= e
->entry
->data
.submenu
.submenu
->shortcut
;
1718 if (unikey
== entrykey
) {
1719 if (found
== NULL
) found
= e
;
1723 /* next with wraparound */
1724 it
= g_list_next(it
);
1725 if (it
== NULL
) it
= frame
->entries
;
1726 } while (it
!= start
);
1729 if (found
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
&&
1732 menu_frame_select(frame
, found
, TRUE
);
1733 usleep(50000); /* highlight the item for a short bit so the
1734 user can see what happened */
1735 menu_entry_frame_execute(found
, state
);
1737 menu_frame_select(frame
, found
, TRUE
);
1739 menu_frame_select_next(frame
->child
);
1750 static gboolean
event_handle_menu(XEvent
*ev
)
1753 ObMenuEntryFrame
*e
;
1754 gboolean ret
= TRUE
;
1758 if (menu_hide_delay_reached() &&
1759 (ev
->xbutton
.button
< 4 || ev
->xbutton
.button
> 5))
1761 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1762 ev
->xbutton
.y_root
)))
1764 menu_frame_select(e
->frame
, e
, TRUE
);
1765 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1768 menu_frame_hide_all();
1772 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
))) {
1773 if (e
->ignore_enters
)
1775 else if (!(f
= find_active_menu()) ||
1777 f
->parent
== e
->frame
||
1778 f
->child
== e
->frame
)
1779 menu_frame_select(e
->frame
, e
, FALSE
);
1783 /*ignore leaves when we're already in the window */
1784 if (ev
->xcrossing
.detail
== NotifyInferior
)
1787 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
)) &&
1788 (f
= find_active_menu()) && f
->selected
== e
&&
1789 e
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1791 menu_frame_select(e
->frame
, NULL
, FALSE
);
1795 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1796 ev
->xmotion
.y_root
)))
1797 if (!(f
= find_active_menu()) ||
1799 f
->parent
== e
->frame
||
1800 f
->child
== e
->frame
)
1801 menu_frame_select(e
->frame
, e
, FALSE
);
1805 ret
= event_handle_menu_keyboard(ev
);
1811 static void event_handle_user_input(ObClient
*client
, XEvent
*e
)
1813 g_assert(e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
1814 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
1815 e
->type
== KeyRelease
);
1817 if (menu_frame_visible
) {
1818 if (event_handle_menu(e
))
1819 /* don't use the event if the menu used it, but if the menu
1820 didn't use it and it's a keypress that is bound, it will
1821 close the menu and be used */
1825 /* if the keyboard interactive action uses the event then dont
1826 use it for bindings. likewise is moveresize uses the event. */
1827 if (!actions_interactive_input_event(e
) && !moveresize_event(e
)) {
1828 if (moveresize_in_progress
)
1829 /* make further actions work on the client being
1831 client
= moveresize_client
;
1833 if (e
->type
== ButtonPress
||
1834 e
->type
== ButtonRelease
||
1835 e
->type
== MotionNotify
)
1837 /* the frame may not be "visible" but they can still click on it
1838 in the case where it is animating before disappearing */
1839 if (!client
|| !frame_iconify_animating(client
->frame
))
1840 mouse_event(client
, e
);
1842 keyboard_event((focus_cycle_target
? focus_cycle_target
:
1843 (client
? client
: focus_client
)), e
);
1847 static void focus_delay_dest(gpointer data
)
1852 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
)
1854 const ObFocusDelayData
*f1
= d1
;
1855 return f1
->client
== d2
;
1858 static gboolean
focus_delay_func(gpointer data
)
1860 ObFocusDelayData
*d
= data
;
1861 Time old
= event_curtime
;
1863 /* don't move focus and kill the menu or the move/resize */
1864 if (menu_frame_visible
|| moveresize_in_progress
) return FALSE
;
1866 event_curtime
= d
->time
;
1867 if (focus_client
!= d
->client
) {
1868 if (client_focus(d
->client
) && config_focus_raise
)
1869 stacking_raise(CLIENT_AS_WINDOW(d
->client
));
1871 event_curtime
= old
;
1872 return FALSE
; /* no repeat */
1875 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1877 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1881 void event_halt_focus_delay(void)
1883 /* ignore all enter events up till now */
1884 event_end_ignore_all_enters(1);
1885 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1888 gulong
event_start_ignore_all_enters(void)
1890 XSync(ob_display
, FALSE
);
1891 return LastKnownRequestProcessed(ob_display
);
1894 void event_end_ignore_all_enters(gulong start
)
1898 g_assert(start
!= 0);
1899 XSync(ob_display
, FALSE
);
1901 r
= g_new(ObSerialRange
, 1);
1903 r
->end
= LastKnownRequestProcessed(ob_display
);
1904 ignore_serials
= g_slist_prepend(ignore_serials
, r
);
1906 /* increment the serial so we don't ignore events we weren't meant to */
1907 XSync(ob_display
, FALSE
);
1910 static gboolean
is_enter_focus_event_ignored(XEvent
*e
)
1914 g_assert(e
->type
== EnterNotify
&&
1915 !(e
->xcrossing
.mode
== NotifyGrab
||
1916 e
->xcrossing
.mode
== NotifyUngrab
||
1917 e
->xcrossing
.detail
== NotifyInferior
));
1919 for (it
= ignore_serials
; it
; it
= next
) {
1920 ObSerialRange
*r
= it
->data
;
1922 next
= g_slist_next(it
);
1924 if ((glong
)(e
->xany
.serial
- r
->end
) > 0) {
1926 ignore_serials
= g_slist_delete_link(ignore_serials
, it
);
1929 else if ((glong
)(e
->xany
.serial
- r
->start
) >= 0)
1935 void event_cancel_all_key_grabs(void)
1937 if (actions_interactive_act_running()) {
1938 actions_interactive_cancel_act();
1939 ob_debug("KILLED interactive action\n");
1941 else if (menu_frame_visible
) {
1942 menu_frame_hide_all();
1943 ob_debug("KILLED open menus\n");
1945 else if (moveresize_in_progress
) {
1946 moveresize_end(TRUE
);
1947 ob_debug("KILLED interactive moveresize\n");
1949 else if (grab_on_keyboard()) {
1951 ob_debug("KILLED active grab on keyboard\n");
1954 ungrab_passive_key();
1957 gboolean
event_time_after(Time t1
, Time t2
)
1959 g_assert(t1
!= CurrentTime
);
1960 g_assert(t2
!= CurrentTime
);
1963 Timestamp values wrap around (after about 49.7 days). The server, given
1964 its current time is represented by timestamp T, always interprets
1965 timestamps from clients by treating half of the timestamp space as being
1966 later in time than T.
1967 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1970 /* TIME_HALF is half of the number space of a Time type variable */
1971 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1973 if (t2
>= TIME_HALF
)
1974 /* t2 is in the second half so t1 might wrap around and be smaller than
1976 return t1
>= t2
|| t1
< (t2
+ TIME_HALF
);
1978 /* t2 is in the first half so t1 has to come after it */
1979 return t1
>= t2
&& t1
< (t2
+ TIME_HALF
);
1982 Time
event_get_server_time(void)
1984 /* Generate a timestamp */
1987 XChangeProperty(ob_display
, screen_support_win
,
1988 prop_atoms
.wm_class
, prop_atoms
.string
,
1989 8, PropModeAppend
, NULL
, 0);
1990 XWindowEvent(ob_display
, screen_support_win
, PropertyChangeMask
, &event
);
1991 return event
.xproperty
.time
;