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
;
274 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
275 /* remove from the state the mask of the modifier key being
276 released, if it is a modifier key being released that is */
277 e
->xkey
.state
&= ~modkeys_keycode_to_mask(e
->xkey
.keycode
);
281 e
->xmotion
.state
= modkeys_only_modifier_masks(e
->xmotion
.state
);
282 /* compress events */
285 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
287 e
->xmotion
.x
= ce
.xmotion
.x
;
288 e
->xmotion
.y
= ce
.xmotion
.y
;
289 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
290 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
297 static gboolean
wanted_focusevent(XEvent
*e
, gboolean in_client_only
)
299 gint mode
= e
->xfocus
.mode
;
300 gint detail
= e
->xfocus
.detail
;
301 Window win
= e
->xany
.window
;
303 if (e
->type
== FocusIn
) {
304 /* These are ones we never want.. */
306 /* This means focus was given by a keyboard/mouse grab. */
307 if (mode
== NotifyGrab
)
309 /* This means focus was given back from a keyboard/mouse grab. */
310 if (mode
== NotifyUngrab
)
313 /* These are the ones we want.. */
315 if (win
== RootWindow(ob_display
, ob_screen
)) {
316 /* If looking for a focus in on a client, then always return
317 FALSE for focus in's to the root window */
320 /* This means focus reverted off of a client */
321 else if (detail
== NotifyPointerRoot
||
322 detail
== NotifyDetailNone
||
323 detail
== NotifyInferior
||
324 /* This means focus got here from another screen */
325 detail
== NotifyNonlinear
)
331 /* It was on a client, was it a valid one?
332 It's possible to get a FocusIn event for a client that was managed
335 if (in_client_only
) {
336 ObWindow
*w
= g_hash_table_lookup(window_map
, &e
->xfocus
.window
);
337 if (!w
|| !WINDOW_IS_CLIENT(w
))
341 /* This means focus reverted to parent from the client (this
342 happens often during iconify animation) */
343 if (detail
== NotifyInferior
)
347 /* This means focus moved from the root window to a client */
348 if (detail
== NotifyVirtual
)
350 /* This means focus moved from one client to another */
351 if (detail
== NotifyNonlinearVirtual
)
357 g_assert(e
->type
== FocusOut
);
359 /* These are ones we never want.. */
361 /* This means focus was taken by a keyboard/mouse grab. */
362 if (mode
== NotifyGrab
)
364 /* This means focus was grabbed on a window and it was released. */
365 if (mode
== NotifyUngrab
)
368 /* Focus left the root window revertedto state */
369 if (win
== RootWindow(ob_display
, ob_screen
))
372 /* These are the ones we want.. */
374 /* This means focus moved from a client to the root window */
375 if (detail
== NotifyVirtual
)
377 /* This means focus moved from one client to another */
378 if (detail
== NotifyNonlinearVirtual
)
386 static Bool
event_look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
388 return e
->type
== FocusIn
&& wanted_focusevent(e
, FALSE
);
391 static Bool
event_look_for_focusin_client(Display
*d
, XEvent
*e
, XPointer arg
)
393 return e
->type
== FocusIn
&& wanted_focusevent(e
, TRUE
);
396 static void print_focusevent(XEvent
*e
)
398 gint mode
= e
->xfocus
.mode
;
399 gint detail
= e
->xfocus
.detail
;
400 Window win
= e
->xany
.window
;
401 const gchar
*modestr
, *detailstr
;
404 case NotifyNormal
: modestr
="NotifyNormal"; break;
405 case NotifyGrab
: modestr
="NotifyGrab"; break;
406 case NotifyUngrab
: modestr
="NotifyUngrab"; break;
407 case NotifyWhileGrabbed
: modestr
="NotifyWhileGrabbed"; break;
410 case NotifyAncestor
: detailstr
="NotifyAncestor"; break;
411 case NotifyVirtual
: detailstr
="NotifyVirtual"; break;
412 case NotifyInferior
: detailstr
="NotifyInferior"; break;
413 case NotifyNonlinear
: detailstr
="NotifyNonlinear"; break;
414 case NotifyNonlinearVirtual
: detailstr
="NotifyNonlinearVirtual"; break;
415 case NotifyPointer
: detailstr
="NotifyPointer"; break;
416 case NotifyPointerRoot
: detailstr
="NotifyPointerRoot"; break;
417 case NotifyDetailNone
: detailstr
="NotifyDetailNone"; break;
420 if (mode
== NotifyGrab
|| mode
== NotifyUngrab
)
425 ob_debug_type(OB_DEBUG_FOCUS
, "Focus%s 0x%x mode=%s detail=%s\n",
426 (e
->xfocus
.type
== FocusIn
? "In" : "Out"),
432 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
437 if (!wanted_focusevent(e
, FALSE
))
442 if (!wanted_focusevent(e
, FALSE
))
449 static void event_process(const XEvent
*ec
, gpointer data
)
452 ObClient
*client
= NULL
;
454 ObDockApp
*dockapp
= NULL
;
455 ObWindow
*obwin
= NULL
;
457 ObEventData
*ed
= data
;
459 /* make a copy we can mangle */
463 window
= event_get_window(e
);
464 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
465 switch (obwin
->type
) {
467 dock
= WINDOW_AS_DOCK(obwin
);
470 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
473 client
= WINDOW_AS_CLIENT(obwin
);
476 case Window_Internal
:
477 /* not to be used for events */
478 g_assert_not_reached();
483 event_set_curtime(e
);
485 if (event_ignore(e
, client
)) {
492 /* deal with it in the kernel */
494 if (menu_frame_visible
&&
495 (e
->type
== EnterNotify
|| e
->type
== LeaveNotify
))
497 /* crossing events for menu */
498 event_handle_menu(e
);
499 } else if (e
->type
== FocusIn
) {
501 e
->xfocus
.detail
== NotifyInferior
)
503 ob_debug_type(OB_DEBUG_FOCUS
,
504 "Focus went to the frame window");
506 focus_left_screen
= FALSE
;
508 focus_fallback(FALSE
, config_focus_under_mouse
, TRUE
, TRUE
);
510 /* We don't get a FocusOut for this case, because it's just moving
511 from our Inferior up to us. This happens when iconifying a
512 window with RevertToParent focus */
513 frame_adjust_focus(client
->frame
, FALSE
);
514 /* focus_set_client(NULL) has already been called */
515 client_calc_layer(client
);
517 else if (e
->xfocus
.detail
== NotifyPointerRoot
||
518 e
->xfocus
.detail
== NotifyDetailNone
||
519 e
->xfocus
.detail
== NotifyInferior
||
520 e
->xfocus
.detail
== NotifyNonlinear
)
524 ob_debug_type(OB_DEBUG_FOCUS
,
525 "Focus went to root or pointer root/none\n");
527 if (e
->xfocus
.detail
== NotifyInferior
||
528 e
->xfocus
.detail
== NotifyNonlinear
)
530 focus_left_screen
= FALSE
;
533 /* If another FocusIn is in the queue then don't fallback yet. This
534 fixes the fun case of:
535 window map -> send focusin
536 window unmap -> get focusout
537 window map -> send focusin
538 get first focus out -> fall back to something (new window
539 hasn't received focus yet, so something else) -> send focusin
540 which means the "something else" is the last thing to get a
541 focusin sent to it, so the new window doesn't end up with focus.
543 But if the other focus in is something like PointerRoot then we
544 still want to fall back.
546 if (XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin_client
,
549 XPutBackEvent(ob_display
, &ce
);
550 ob_debug_type(OB_DEBUG_FOCUS
,
551 " but another FocusIn is coming\n");
553 /* Focus has been reverted.
555 FocusOut events come after UnmapNotify, so we don't need to
556 worry about focusing an invalid window
559 if (!focus_left_screen
)
560 focus_fallback(FALSE
, config_focus_under_mouse
,
566 ob_debug_type(OB_DEBUG_FOCUS
,
567 "Focus went to a window that is already gone\n");
569 /* If you send focus to a window and then it disappears, you can
570 get the FocusIn for it, after it is unmanaged.
571 Just wait for the next FocusOut/FocusIn pair, but make note that
572 the window that was focused no longer is. */
573 focus_set_client(NULL
);
575 else if (client
!= focus_client
) {
576 focus_left_screen
= FALSE
;
577 frame_adjust_focus(client
->frame
, TRUE
);
578 focus_set_client(client
);
579 client_calc_layer(client
);
580 client_bring_helper_windows(client
);
582 } else if (e
->type
== FocusOut
) {
585 /* Look for the followup FocusIn */
586 if (!XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin
, NULL
)) {
587 /* There is no FocusIn, this means focus went to a window that
588 is not being managed, or a window on another screen. */
592 xerror_set_ignore(TRUE
);
593 if (XGetInputFocus(ob_display
, &win
, &i
) != 0 &&
594 XGetGeometry(ob_display
, win
, &root
, &i
,&i
,&u
,&u
,&u
,&u
) != 0 &&
595 root
!= RootWindow(ob_display
, ob_screen
))
597 ob_debug_type(OB_DEBUG_FOCUS
,
598 "Focus went to another screen !\n");
599 focus_left_screen
= TRUE
;
602 ob_debug_type(OB_DEBUG_FOCUS
,
603 "Focus went to a black hole !\n");
604 xerror_set_ignore(FALSE
);
605 /* nothing is focused */
606 focus_set_client(NULL
);
608 /* Focus moved, so process the FocusIn event */
609 ObEventData ed
= { .ignored
= FALSE
};
610 event_process(&ce
, &ed
);
612 /* The FocusIn was ignored, this means it was on a window
613 that isn't a client. */
614 ob_debug_type(OB_DEBUG_FOCUS
,
615 "Focus went to an unmanaged window 0x%x !\n",
617 focus_fallback(TRUE
, config_focus_under_mouse
, TRUE
, TRUE
);
621 if (client
&& client
!= focus_client
) {
622 frame_adjust_focus(client
->frame
, FALSE
);
623 /* focus_set_client(NULL) has already been called in this
624 section or by focus_fallback */
625 client_calc_layer(client
);
629 event_handle_client(client
, e
);
631 event_handle_dockapp(dockapp
, e
);
633 event_handle_dock(dock
, e
);
634 else if (window
== RootWindow(ob_display
, ob_screen
))
635 event_handle_root(e
);
636 else if (e
->type
== MapRequest
)
637 client_manage(window
);
638 else if (e
->type
== MappingNotify
) {
639 /* keyboard layout changes, reconfigure openbox. need to restart the
640 modkeys system, but also to reload the key bindings. */
643 else if (e
->type
== ClientMessage
) {
644 /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
645 windows that are not managed yet. */
646 if (e
->xclient
.message_type
== prop_atoms
.net_request_frame_extents
) {
647 /* Pretend to manage the client, getting information used to
648 determine its decorations */
649 ObClient
*c
= client_fake_manage(e
->xclient
.window
);
652 /* set the frame extents on the window */
653 vals
[0] = c
->frame
->size
.left
;
654 vals
[1] = c
->frame
->size
.right
;
655 vals
[2] = c
->frame
->size
.top
;
656 vals
[3] = c
->frame
->size
.bottom
;
657 PROP_SETA32(e
->xclient
.window
, net_frame_extents
,
660 /* Free the pretend client */
661 client_fake_unmanage(c
);
664 else if (e
->type
== ConfigureRequest
) {
665 /* unhandled configure requests must be used to configure the
669 xwc
.x
= e
->xconfigurerequest
.x
;
670 xwc
.y
= e
->xconfigurerequest
.y
;
671 xwc
.width
= e
->xconfigurerequest
.width
;
672 xwc
.height
= e
->xconfigurerequest
.height
;
673 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
674 xwc
.sibling
= e
->xconfigurerequest
.above
;
675 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
677 /* we are not to be held responsible if someone sends us an
679 xerror_set_ignore(TRUE
);
680 XConfigureWindow(ob_display
, window
,
681 e
->xconfigurerequest
.value_mask
, &xwc
);
682 xerror_set_ignore(FALSE
);
685 else if (extensions_sync
&&
686 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
688 XSyncAlarmNotifyEvent
*se
= (XSyncAlarmNotifyEvent
*)e
;
689 if (se
->alarm
== moveresize_alarm
&& moveresize_in_progress
)
694 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
695 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
696 e
->type
== KeyRelease
)
698 event_handle_user_input(client
, e
);
701 /* if something happens and it's not from an XEvent, then we don't know
703 event_curtime
= CurrentTime
;
706 static void event_handle_root(XEvent
*e
)
712 ob_debug("Another WM has requested to replace us. Exiting.\n");
717 if (e
->xclient
.format
!= 32) break;
719 msgtype
= e
->xclient
.message_type
;
720 if (msgtype
== prop_atoms
.net_current_desktop
) {
721 guint d
= e
->xclient
.data
.l
[0];
722 if (d
< screen_num_desktops
) {
723 event_curtime
= e
->xclient
.data
.l
[1];
724 if (event_curtime
== 0)
725 ob_debug_type(OB_DEBUG_APP_BUGS
,
726 "_NET_CURRENT_DESKTOP message is missing "
728 screen_set_desktop(d
, TRUE
);
730 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
731 guint d
= e
->xclient
.data
.l
[0];
732 if (d
> 0 && d
<= 1000)
733 screen_set_num_desktops(d
);
734 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
735 screen_show_desktop(e
->xclient
.data
.l
[0] != 0, NULL
);
736 } else if (msgtype
== prop_atoms
.ob_control
) {
737 ob_debug("OB_CONTROL: %d\n", e
->xclient
.data
.l
[0]);
738 if (e
->xclient
.data
.l
[0] == 1)
740 else if (e
->xclient
.data
.l
[0] == 2)
742 else if (e
->xclient
.data
.l
[0] == 3)
747 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
) {
748 ob_debug("UPDATE DESKTOP NAMES\n");
749 screen_update_desktop_names();
751 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
752 screen_update_layout();
754 case ConfigureNotify
:
756 XRRUpdateConfiguration(e
);
765 void event_enter_client(ObClient
*client
)
767 g_assert(config_focus_follow
);
769 if (client_enter_focusable(client
) && client_can_focus(client
)) {
770 if (config_focus_delay
) {
771 ObFocusDelayData
*data
;
773 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
775 data
= g_new(ObFocusDelayData
, 1);
776 data
->client
= client
;
777 data
->time
= event_curtime
;
779 ob_main_loop_timeout_add(ob_main_loop
,
780 config_focus_delay
* 1000,
782 data
, focus_delay_cmp
, focus_delay_dest
);
784 ObFocusDelayData data
;
785 data
.client
= client
;
786 data
.time
= event_curtime
;
787 focus_delay_func(&data
);
792 static void event_handle_client(ObClient
*client
, XEvent
*e
)
797 static gint px
= -1, py
= -1;
802 /* save where the press occured for the first button pressed */
804 pb
= e
->xbutton
.button
;
809 /* Wheel buttons don't draw because they are an instant click, so it
810 is a waste of resources to go drawing it.
811 if the user is doing an intereactive thing, or has a menu open then
812 the mouse is grabbed (possibly) and if we get these events we don't
813 want to deal with them
815 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5) &&
818 /* use where the press occured */
819 con
= frame_context(client
, e
->xbutton
.window
, px
, py
);
820 con
= mouse_button_frame_context(con
, e
->xbutton
.button
,
823 if (e
->type
== ButtonRelease
&& e
->xbutton
.button
== pb
)
824 pb
= 0, px
= py
= -1;
827 case OB_FRAME_CONTEXT_MAXIMIZE
:
828 client
->frame
->max_press
= (e
->type
== ButtonPress
);
829 frame_adjust_state(client
->frame
);
831 case OB_FRAME_CONTEXT_CLOSE
:
832 client
->frame
->close_press
= (e
->type
== ButtonPress
);
833 frame_adjust_state(client
->frame
);
835 case OB_FRAME_CONTEXT_ICONIFY
:
836 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
837 frame_adjust_state(client
->frame
);
839 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
840 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
841 frame_adjust_state(client
->frame
);
843 case OB_FRAME_CONTEXT_SHADE
:
844 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
845 frame_adjust_state(client
->frame
);
848 /* nothing changes with clicks for any other contexts */
854 /* when there is a grab on the pointer, we won't get enter/leave
855 notifies, but we still get motion events */
856 if (grab_on_pointer()) break;
858 con
= frame_context(client
, e
->xmotion
.window
,
859 e
->xmotion
.x
, e
->xmotion
.y
);
861 case OB_FRAME_CONTEXT_TITLEBAR
:
862 case OB_FRAME_CONTEXT_TLCORNER
:
863 case OB_FRAME_CONTEXT_TRCORNER
:
864 /* we've left the button area inside the titlebar */
865 if (client
->frame
->max_hover
|| client
->frame
->desk_hover
||
866 client
->frame
->shade_hover
|| client
->frame
->iconify_hover
||
867 client
->frame
->close_hover
)
869 client
->frame
->max_hover
= FALSE
;
870 client
->frame
->desk_hover
= FALSE
;
871 client
->frame
->shade_hover
= FALSE
;
872 client
->frame
->iconify_hover
= FALSE
;
873 client
->frame
->close_hover
= FALSE
;
874 frame_adjust_state(client
->frame
);
877 case OB_FRAME_CONTEXT_MAXIMIZE
:
878 if (!client
->frame
->max_hover
) {
879 client
->frame
->max_hover
= TRUE
;
880 frame_adjust_state(client
->frame
);
883 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
884 if (!client
->frame
->desk_hover
) {
885 client
->frame
->desk_hover
= TRUE
;
886 frame_adjust_state(client
->frame
);
889 case OB_FRAME_CONTEXT_SHADE
:
890 if (!client
->frame
->shade_hover
) {
891 client
->frame
->shade_hover
= TRUE
;
892 frame_adjust_state(client
->frame
);
895 case OB_FRAME_CONTEXT_ICONIFY
:
896 if (!client
->frame
->iconify_hover
) {
897 client
->frame
->iconify_hover
= TRUE
;
898 frame_adjust_state(client
->frame
);
901 case OB_FRAME_CONTEXT_CLOSE
:
902 if (!client
->frame
->close_hover
) {
903 client
->frame
->close_hover
= TRUE
;
904 frame_adjust_state(client
->frame
);
912 con
= frame_context(client
, e
->xcrossing
.window
,
913 e
->xcrossing
.x
, e
->xcrossing
.y
);
915 case OB_FRAME_CONTEXT_TITLEBAR
:
916 case OB_FRAME_CONTEXT_TLCORNER
:
917 case OB_FRAME_CONTEXT_TRCORNER
:
918 /* we've left the button area inside the titlebar */
919 if (client
->frame
->max_hover
|| client
->frame
->desk_hover
||
920 client
->frame
->shade_hover
|| client
->frame
->iconify_hover
||
921 client
->frame
->close_hover
)
923 client
->frame
->max_hover
= FALSE
;
924 client
->frame
->desk_hover
= FALSE
;
925 client
->frame
->shade_hover
= FALSE
;
926 client
->frame
->iconify_hover
= FALSE
;
927 client
->frame
->close_hover
= FALSE
;
928 frame_adjust_state(client
->frame
);
931 case OB_FRAME_CONTEXT_MAXIMIZE
:
932 client
->frame
->max_hover
= FALSE
;
933 frame_adjust_state(client
->frame
);
935 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
936 client
->frame
->desk_hover
= FALSE
;
937 frame_adjust_state(client
->frame
);
939 case OB_FRAME_CONTEXT_SHADE
:
940 client
->frame
->shade_hover
= FALSE
;
941 frame_adjust_state(client
->frame
);
943 case OB_FRAME_CONTEXT_ICONIFY
:
944 client
->frame
->iconify_hover
= FALSE
;
945 frame_adjust_state(client
->frame
);
947 case OB_FRAME_CONTEXT_CLOSE
:
948 client
->frame
->close_hover
= FALSE
;
949 frame_adjust_state(client
->frame
);
951 case OB_FRAME_CONTEXT_FRAME
:
952 /* When the mouse leaves an animating window, don't use the
953 corresponding enter events. Pretend like the animating window
954 doesn't even exist..! */
955 if (frame_iconify_animating(client
->frame
))
956 event_end_ignore_all_enters(event_start_ignore_all_enters());
958 ob_debug_type(OB_DEBUG_FOCUS
,
959 "%sNotify mode %d detail %d on %lx\n",
960 (e
->type
== EnterNotify
? "Enter" : "Leave"),
962 e
->xcrossing
.detail
, (client
?client
->window
:0));
963 if (grab_on_keyboard())
965 if (config_focus_follow
&& config_focus_delay
&&
966 /* leave inferior events can happen when the mouse goes onto
967 the window's border and then into the window before the
969 e
->xcrossing
.detail
!= NotifyInferior
)
971 ob_main_loop_timeout_remove_data(ob_main_loop
,
982 con
= frame_context(client
, e
->xcrossing
.window
,
983 e
->xcrossing
.x
, e
->xcrossing
.y
);
985 case OB_FRAME_CONTEXT_MAXIMIZE
:
986 client
->frame
->max_hover
= TRUE
;
987 frame_adjust_state(client
->frame
);
989 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
990 client
->frame
->desk_hover
= TRUE
;
991 frame_adjust_state(client
->frame
);
993 case OB_FRAME_CONTEXT_SHADE
:
994 client
->frame
->shade_hover
= TRUE
;
995 frame_adjust_state(client
->frame
);
997 case OB_FRAME_CONTEXT_ICONIFY
:
998 client
->frame
->iconify_hover
= TRUE
;
999 frame_adjust_state(client
->frame
);
1001 case OB_FRAME_CONTEXT_CLOSE
:
1002 client
->frame
->close_hover
= TRUE
;
1003 frame_adjust_state(client
->frame
);
1005 case OB_FRAME_CONTEXT_FRAME
:
1006 if (grab_on_keyboard())
1008 if (e
->xcrossing
.mode
== NotifyGrab
||
1009 e
->xcrossing
.mode
== NotifyUngrab
||
1010 /*ignore enters when we're already in the window */
1011 e
->xcrossing
.detail
== NotifyInferior
||
1012 is_enter_focus_event_ignored(e
))
1014 ob_debug_type(OB_DEBUG_FOCUS
,
1015 "%sNotify mode %d detail %d on %lx IGNORED\n",
1016 (e
->type
== EnterNotify
? "Enter" : "Leave"),
1018 e
->xcrossing
.detail
, client
?client
->window
:0);
1021 ob_debug_type(OB_DEBUG_FOCUS
,
1022 "%sNotify mode %d detail %d on %lx, "
1023 "focusing window\n",
1024 (e
->type
== EnterNotify
? "Enter" : "Leave"),
1026 e
->xcrossing
.detail
, (client
?client
->window
:0));
1027 if (config_focus_follow
)
1028 event_enter_client(client
);
1036 case ConfigureRequest
:
1038 /* dont compress these unless you're going to watch for property
1039 notifies in between (these can change what the configure would
1041 also you can't compress stacking events
1045 gboolean move
= FALSE
;
1046 gboolean resize
= FALSE
;
1048 /* get the current area */
1049 RECT_TO_DIMS(client
->area
, x
, y
, w
, h
);
1051 ob_debug("ConfigureRequest for \"%s\" desktop %d wmstate %d "
1053 " x %d y %d w %d h %d b %d\n",
1055 screen_desktop
, client
->wmstate
, client
->frame
->visible
,
1056 x
, y
, w
, h
, client
->border_width
);
1058 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
1059 if (client
->border_width
!= e
->xconfigurerequest
.border_width
) {
1060 client
->border_width
= e
->xconfigurerequest
.border_width
;
1062 /* if the border width is changing then that is the same
1063 as requesting a resize, but we don't actually change
1064 the client's border, so it will change their root
1065 coordiantes (since they include the border width) and
1066 we need to a notify then */
1071 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
1072 ObClient
*sibling
= NULL
;
1073 gulong ignore_start
;
1076 /* get the sibling */
1077 if (e
->xconfigurerequest
.value_mask
& CWSibling
) {
1079 win
= g_hash_table_lookup(window_map
,
1080 &e
->xconfigurerequest
.above
);
1081 if (win
&& WINDOW_IS_CLIENT(win
) &&
1082 WINDOW_AS_CLIENT(win
) != client
)
1084 sibling
= WINDOW_AS_CLIENT(win
);
1087 /* an invalid sibling was specified so don't restack at
1088 all, it won't make sense no matter what we do */
1093 if (!config_focus_under_mouse
)
1094 ignore_start
= event_start_ignore_all_enters();
1095 stacking_restack_request(client
, sibling
,
1096 e
->xconfigurerequest
.detail
);
1097 if (!config_focus_under_mouse
)
1098 event_end_ignore_all_enters(ignore_start
);
1101 /* a stacking change moves the window without resizing */
1105 if ((e
->xconfigurerequest
.value_mask
& CWX
) ||
1106 (e
->xconfigurerequest
.value_mask
& CWY
) ||
1107 (e
->xconfigurerequest
.value_mask
& CWWidth
) ||
1108 (e
->xconfigurerequest
.value_mask
& CWHeight
))
1110 if (e
->xconfigurerequest
.value_mask
& CWX
) {
1111 /* don't allow clients to move shaded windows (fvwm does this)
1113 if (!client
->shaded
)
1114 x
= e
->xconfigurerequest
.x
;
1117 if (e
->xconfigurerequest
.value_mask
& CWY
) {
1118 /* don't allow clients to move shaded windows (fvwm does this)
1120 if (!client
->shaded
)
1121 y
= e
->xconfigurerequest
.y
;
1125 if (e
->xconfigurerequest
.value_mask
& CWWidth
) {
1126 w
= e
->xconfigurerequest
.width
;
1129 if (e
->xconfigurerequest
.value_mask
& CWHeight
) {
1130 h
= e
->xconfigurerequest
.height
;
1135 ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d "
1136 "move %d resize %d\n",
1137 e
->xconfigurerequest
.value_mask
& CWX
, x
,
1138 e
->xconfigurerequest
.value_mask
& CWY
, y
,
1139 e
->xconfigurerequest
.value_mask
& CWWidth
, w
,
1140 e
->xconfigurerequest
.value_mask
& CWHeight
, h
,
1143 /* check for broken apps moving to their root position
1145 XXX remove this some day...that would be nice. right now all
1146 kde apps do this when they try activate themselves on another
1147 desktop. eg. open amarok window on desktop 1, switch to desktop
1148 2, click amarok tray icon. it will move by its decoration size.
1150 if (x
!= client
->area
.x
&&
1151 x
== (client
->frame
->area
.x
+ client
->frame
->size
.left
-
1152 (gint
)client
->border_width
) &&
1153 y
!= client
->area
.y
&&
1154 y
== (client
->frame
->area
.y
+ client
->frame
->size
.top
-
1155 (gint
)client
->border_width
) &&
1156 w
== client
->area
.width
&&
1157 h
== client
->area
.height
)
1159 ob_debug_type(OB_DEBUG_APP_BUGS
,
1160 "Application %s is trying to move via "
1161 "ConfigureRequest to it's root window position "
1162 "but it is not using StaticGravity\n",
1168 /* they still requested a move, so don't change whether a
1169 notify is sent or not */
1175 client_try_configure(client
, &x
, &y
, &w
, &h
, &lw
, &lh
, FALSE
);
1177 /* if x was not given, then use gravity to figure out the new
1178 x. the reference point should not be moved */
1179 if ((e
->xconfigurerequest
.value_mask
& CWWidth
&&
1180 !(e
->xconfigurerequest
.value_mask
& CWX
)))
1181 client_gravity_resize_w(client
, &x
, client
->area
.width
, w
);
1182 /* if y was not given, then use gravity to figure out the new
1183 y. the reference point should not be moved */
1184 if ((e
->xconfigurerequest
.value_mask
& CWHeight
&&
1185 !(e
->xconfigurerequest
.value_mask
& CWY
)))
1186 client_gravity_resize_h(client
, &y
, client
->area
.height
,h
);
1188 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1190 ob_debug("Granting ConfigureRequest x %d y %d w %d h %d\n",
1192 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
, TRUE
);
1197 if (client
->ignore_unmaps
) {
1198 client
->ignore_unmaps
--;
1201 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
1202 "ignores left %d\n",
1203 client
->window
, e
->xunmap
.event
, e
->xunmap
.from_configure
,
1204 client
->ignore_unmaps
);
1205 client_unmanage(client
);
1208 ob_debug("DestroyNotify for window 0x%x\n", client
->window
);
1209 client_unmanage(client
);
1211 case ReparentNotify
:
1212 /* this is when the client is first taken captive in the frame */
1213 if (e
->xreparent
.parent
== client
->frame
->window
) break;
1216 This event is quite rare and is usually handled in unmapHandler.
1217 However, if the window is unmapped when the reparent event occurs,
1218 the window manager never sees it because an unmap event is not sent
1219 to an already unmapped window.
1222 /* we don't want the reparent event, put it back on the stack for the
1223 X server to deal with after we unmanage the window */
1224 XPutBackEvent(ob_display
, e
);
1226 ob_debug("ReparentNotify for window 0x%x\n", client
->window
);
1227 client_unmanage(client
);
1230 ob_debug("MapRequest for 0x%lx\n", client
->window
);
1231 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
1232 does, we don't want it!
1233 it can happen now when the window is on
1234 another desktop, but we still don't
1236 client_activate(client
, FALSE
, TRUE
, TRUE
, TRUE
);
1239 /* validate cuz we query stuff off the client here */
1240 if (!client_validate(client
)) break;
1242 if (e
->xclient
.format
!= 32) return;
1244 msgtype
= e
->xclient
.message_type
;
1245 if (msgtype
== prop_atoms
.wm_change_state
) {
1246 /* compress changes into a single change */
1247 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1249 /* XXX: it would be nice to compress ALL messages of a
1250 type, not just messages in a row without other
1251 message types between. */
1252 if (ce
.xclient
.message_type
!= msgtype
) {
1253 XPutBackEvent(ob_display
, &ce
);
1256 e
->xclient
= ce
.xclient
;
1258 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
1259 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
1260 /* compress changes into a single change */
1261 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1263 /* XXX: it would be nice to compress ALL messages of a
1264 type, not just messages in a row without other
1265 message types between. */
1266 if (ce
.xclient
.message_type
!= msgtype
) {
1267 XPutBackEvent(ob_display
, &ce
);
1270 e
->xclient
= ce
.xclient
;
1272 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
1273 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
1274 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
1276 } else if (msgtype
== prop_atoms
.net_wm_state
) {
1277 gulong ignore_start
;
1279 /* can't compress these */
1280 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1281 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
1282 e
->xclient
.data
.l
[0] == 1 ? "Add" :
1283 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1284 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
1287 /* ignore enter events caused by these like ob actions do */
1288 if (!config_focus_under_mouse
)
1289 ignore_start
= event_start_ignore_all_enters();
1290 client_set_state(client
, e
->xclient
.data
.l
[0],
1291 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
1292 if (!config_focus_under_mouse
)
1293 event_end_ignore_all_enters(ignore_start
);
1294 } else if (msgtype
== prop_atoms
.net_close_window
) {
1295 ob_debug("net_close_window for 0x%lx\n", client
->window
);
1296 client_close(client
);
1297 } else if (msgtype
== prop_atoms
.net_active_window
) {
1298 ob_debug("net_active_window for 0x%lx source=%s\n",
1300 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
1301 (e
->xclient
.data
.l
[0] == 1 ? "application" :
1302 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
1303 /* XXX make use of data.l[2] !? */
1304 if (e
->xclient
.data
.l
[0] == 1 || e
->xclient
.data
.l
[0] == 2) {
1305 /* don't use the user's timestamp for client_focus, cuz if it's
1306 an old broken timestamp (happens all the time) then focus
1307 won't move even though we're trying to move it
1308 event_curtime = e->xclient.data.l[1];*/
1309 if (e
->xclient
.data
.l
[1] == 0)
1310 ob_debug_type(OB_DEBUG_APP_BUGS
,
1311 "_NET_ACTIVE_WINDOW message for window %s is"
1312 " missing a timestamp\n", client
->title
);
1314 ob_debug_type(OB_DEBUG_APP_BUGS
,
1315 "_NET_ACTIVE_WINDOW message for window %s is "
1316 "missing source indication\n");
1317 client_activate(client
, FALSE
, TRUE
, TRUE
,
1318 (e
->xclient
.data
.l
[0] == 0 ||
1319 e
->xclient
.data
.l
[0] == 2));
1320 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
1321 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1322 client
->window
, e
->xclient
.data
.l
[2]);
1323 if ((Atom
)e
->xclient
.data
.l
[2] ==
1324 prop_atoms
.net_wm_moveresize_size_topleft
||
1325 (Atom
)e
->xclient
.data
.l
[2] ==
1326 prop_atoms
.net_wm_moveresize_size_top
||
1327 (Atom
)e
->xclient
.data
.l
[2] ==
1328 prop_atoms
.net_wm_moveresize_size_topright
||
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_right
||
1333 (Atom
)e
->xclient
.data
.l
[2] ==
1334 prop_atoms
.net_wm_moveresize_size_bottomright
||
1335 (Atom
)e
->xclient
.data
.l
[2] ==
1336 prop_atoms
.net_wm_moveresize_size_bottom
||
1337 (Atom
)e
->xclient
.data
.l
[2] ==
1338 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1339 (Atom
)e
->xclient
.data
.l
[2] ==
1340 prop_atoms
.net_wm_moveresize_size_left
||
1341 (Atom
)e
->xclient
.data
.l
[2] ==
1342 prop_atoms
.net_wm_moveresize_move
||
1343 (Atom
)e
->xclient
.data
.l
[2] ==
1344 prop_atoms
.net_wm_moveresize_size_keyboard
||
1345 (Atom
)e
->xclient
.data
.l
[2] ==
1346 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1348 moveresize_start(client
, e
->xclient
.data
.l
[0],
1349 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1350 e
->xclient
.data
.l
[2]);
1352 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1353 prop_atoms
.net_wm_moveresize_cancel
)
1354 moveresize_end(TRUE
);
1355 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1356 gint ograv
, x
, y
, w
, h
;
1358 ograv
= client
->gravity
;
1360 if (e
->xclient
.data
.l
[0] & 0xff)
1361 client
->gravity
= e
->xclient
.data
.l
[0] & 0xff;
1363 if (e
->xclient
.data
.l
[0] & 1 << 8)
1364 x
= e
->xclient
.data
.l
[1];
1367 if (e
->xclient
.data
.l
[0] & 1 << 9)
1368 y
= e
->xclient
.data
.l
[2];
1372 if (e
->xclient
.data
.l
[0] & 1 << 10) {
1373 w
= e
->xclient
.data
.l
[3];
1375 /* if x was not given, then use gravity to figure out the new
1376 x. the reference point should not be moved */
1377 if (!(e
->xclient
.data
.l
[0] & 1 << 8))
1378 client_gravity_resize_w(client
, &x
, client
->area
.width
, w
);
1381 w
= client
->area
.width
;
1383 if (e
->xclient
.data
.l
[0] & 1 << 11) {
1384 h
= e
->xclient
.data
.l
[4];
1386 /* if y was not given, then use gravity to figure out the new
1387 y. the reference point should not be moved */
1388 if (!(e
->xclient
.data
.l
[0] & 1 << 9))
1389 client_gravity_resize_h(client
, &y
, client
->area
.height
,h
);
1392 h
= client
->area
.height
;
1394 ob_debug("MOVERESIZE x %d %d y %d %d (gravity %d)\n",
1395 e
->xclient
.data
.l
[0] & 1 << 8, x
,
1396 e
->xclient
.data
.l
[0] & 1 << 9, y
,
1399 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1401 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
, FALSE
);
1403 client
->gravity
= ograv
;
1404 } else if (msgtype
== prop_atoms
.net_restack_window
) {
1405 if (e
->xclient
.data
.l
[0] != 2) {
1406 ob_debug_type(OB_DEBUG_APP_BUGS
,
1407 "_NET_RESTACK_WINDOW sent for window %s with "
1408 "invalid source indication %ld\n",
1409 client
->title
, e
->xclient
.data
.l
[0]);
1411 ObClient
*sibling
= NULL
;
1412 if (e
->xclient
.data
.l
[1]) {
1413 ObWindow
*win
= g_hash_table_lookup
1414 (window_map
, &e
->xclient
.data
.l
[1]);
1415 if (WINDOW_IS_CLIENT(win
) &&
1416 WINDOW_AS_CLIENT(win
) != client
)
1418 sibling
= WINDOW_AS_CLIENT(win
);
1420 if (sibling
== NULL
)
1421 ob_debug_type(OB_DEBUG_APP_BUGS
,
1422 "_NET_RESTACK_WINDOW sent for window %s "
1423 "with invalid sibling 0x%x\n",
1424 client
->title
, e
->xclient
.data
.l
[1]);
1426 if (e
->xclient
.data
.l
[2] == Below
||
1427 e
->xclient
.data
.l
[2] == BottomIf
||
1428 e
->xclient
.data
.l
[2] == Above
||
1429 e
->xclient
.data
.l
[2] == TopIf
||
1430 e
->xclient
.data
.l
[2] == Opposite
)
1432 gulong ignore_start
;
1434 if (!config_focus_under_mouse
)
1435 ignore_start
= event_start_ignore_all_enters();
1436 /* just raise, don't activate */
1437 stacking_restack_request(client
, sibling
,
1438 e
->xclient
.data
.l
[2]);
1439 if (!config_focus_under_mouse
)
1440 event_end_ignore_all_enters(ignore_start
);
1442 /* send a synthetic ConfigureNotify, cuz this is supposed
1443 to be like a ConfigureRequest. */
1444 client_reconfigure(client
, TRUE
);
1446 ob_debug_type(OB_DEBUG_APP_BUGS
,
1447 "_NET_RESTACK_WINDOW sent for window %s "
1448 "with invalid detail %d\n",
1449 client
->title
, e
->xclient
.data
.l
[2]);
1453 case PropertyNotify
:
1454 /* validate cuz we query stuff off the client here */
1455 if (!client_validate(client
)) break;
1457 /* compress changes to a single property into a single change */
1458 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1462 /* XXX: it would be nice to compress ALL changes to a property,
1463 not just changes in a row without other props between. */
1465 a
= ce
.xproperty
.atom
;
1466 b
= e
->xproperty
.atom
;
1470 if ((a
== prop_atoms
.net_wm_name
||
1471 a
== prop_atoms
.wm_name
||
1472 a
== prop_atoms
.net_wm_icon_name
||
1473 a
== prop_atoms
.wm_icon_name
)
1475 (b
== prop_atoms
.net_wm_name
||
1476 b
== prop_atoms
.wm_name
||
1477 b
== prop_atoms
.net_wm_icon_name
||
1478 b
== prop_atoms
.wm_icon_name
)) {
1481 if (a
== prop_atoms
.net_wm_icon
&&
1482 b
== prop_atoms
.net_wm_icon
)
1485 XPutBackEvent(ob_display
, &ce
);
1489 msgtype
= e
->xproperty
.atom
;
1490 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1491 ob_debug("Update NORMAL hints\n");
1492 client_update_normal_hints(client
);
1493 /* normal hints can make a window non-resizable */
1494 client_setup_decor_and_functions(client
, FALSE
);
1496 /* make sure the client's sizes are within its bounds, but only
1497 reconfigure the window if it needs to. emacs will update its
1498 normal hints every time it receives a conigurenotify */
1499 client_reconfigure(client
, FALSE
);
1500 } else if (msgtype
== XA_WM_HINTS
) {
1501 client_update_wmhints(client
);
1502 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1503 client_update_transient_for(client
);
1504 client_get_type_and_transientness(client
);
1505 /* type may have changed, so update the layer */
1506 client_calc_layer(client
);
1507 client_setup_decor_and_functions(client
, TRUE
);
1508 } else if (msgtype
== prop_atoms
.net_wm_name
||
1509 msgtype
== prop_atoms
.wm_name
||
1510 msgtype
== prop_atoms
.net_wm_icon_name
||
1511 msgtype
== prop_atoms
.wm_icon_name
) {
1512 client_update_title(client
);
1513 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1514 client_update_protocols(client
);
1515 client_setup_decor_and_functions(client
, TRUE
);
1517 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1518 client_update_strut(client
);
1520 else if (msgtype
== prop_atoms
.net_wm_strut_partial
) {
1521 client_update_strut(client
);
1523 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1524 client_update_icons(client
);
1526 else if (msgtype
== prop_atoms
.net_wm_icon_geometry
) {
1527 client_update_icon_geometry(client
);
1529 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1531 if (client
== focus_client
&&
1532 PROP_GET32(client
->window
, net_wm_user_time
, cardinal
, &t
) &&
1533 t
&& !event_time_after(t
, e
->xproperty
.time
) &&
1534 (!event_last_user_time
||
1535 event_time_after(t
, event_last_user_time
)))
1537 event_last_user_time
= t
;
1541 else if (msgtype
== prop_atoms
.net_wm_sync_request_counter
) {
1542 client_update_sync_request_counter(client
);
1546 case ColormapNotify
:
1547 client_update_colormap(client
, e
->xcolormap
.colormap
);
1552 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1553 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1554 frame_adjust_shape(client
->frame
);
1560 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1564 if (e
->xbutton
.button
== 1)
1565 stacking_raise(DOCK_AS_WINDOW(s
));
1566 else if (e
->xbutton
.button
== 2)
1567 stacking_lower(DOCK_AS_WINDOW(s
));
1573 /* don't hide when moving into a dock app */
1574 if (e
->xcrossing
.detail
!= NotifyInferior
)
1580 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1584 dock_app_drag(app
, &e
->xmotion
);
1587 if (app
->ignore_unmaps
) {
1588 app
->ignore_unmaps
--;
1591 dock_remove(app
, TRUE
);
1594 dock_remove(app
, FALSE
);
1596 case ReparentNotify
:
1597 dock_remove(app
, FALSE
);
1599 case ConfigureNotify
:
1600 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1605 static ObMenuFrame
* find_active_menu(void)
1608 ObMenuFrame
*ret
= NULL
;
1610 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1619 static ObMenuFrame
* find_active_or_last_menu(void)
1621 ObMenuFrame
*ret
= NULL
;
1623 ret
= find_active_menu();
1624 if (!ret
&& menu_frame_visible
)
1625 ret
= menu_frame_visible
->data
;
1629 static gboolean
event_handle_menu_keyboard(XEvent
*ev
)
1631 guint keycode
, state
;
1634 gboolean ret
= FALSE
;
1636 keycode
= ev
->xkey
.keycode
;
1637 state
= ev
->xkey
.state
;
1638 unikey
= translate_unichar(keycode
);
1640 frame
= find_active_or_last_menu();
1642 g_assert_not_reached(); /* there is no active menu */
1644 /* Allow control while going thru the menu */
1645 else if (ev
->type
== KeyPress
&& (state
& ~ControlMask
) == 0) {
1646 if (keycode
== ob_keycode(OB_KEY_ESCAPE
)) {
1647 menu_frame_hide_all();
1651 else if (keycode
== ob_keycode(OB_KEY_LEFT
)) {
1652 /* Left goes to the parent menu */
1653 menu_frame_select(frame
, NULL
, TRUE
);
1657 else if (keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1658 /* Right goes to the selected submenu */
1659 if (frame
->child
) menu_frame_select_next(frame
->child
);
1663 else if (keycode
== ob_keycode(OB_KEY_UP
)) {
1664 menu_frame_select_previous(frame
);
1668 else if (keycode
== ob_keycode(OB_KEY_DOWN
)) {
1669 menu_frame_select_next(frame
);
1674 /* Use KeyRelease events for running things so that the key release doesn't
1675 get sent to the focused application.
1677 Allow ControlMask only, and don't bother if the menu is empty */
1678 else if (ev
->type
== KeyRelease
&& (state
& ~ControlMask
) == 0 &&
1681 if (keycode
== ob_keycode(OB_KEY_RETURN
)) {
1682 /* Enter runs the active item or goes into the submenu.
1683 Control-Enter runs it without closing the menu. */
1685 menu_frame_select_next(frame
->child
);
1686 else if (frame
->selected
)
1687 menu_entry_frame_execute(frame
->selected
, state
);
1692 /* keyboard accelerator shortcuts. (if it was a valid key) */
1693 else if (unikey
!= 0) {
1696 ObMenuEntryFrame
*found
= NULL
;
1697 guint num_found
= 0;
1699 /* start after the selected one */
1700 start
= frame
->entries
;
1701 if (frame
->selected
) {
1702 for (it
= start
; frame
->selected
!= it
->data
;
1703 it
= g_list_next(it
))
1704 g_assert(it
!= NULL
); /* nothing was selected? */
1705 /* next with wraparound */
1706 start
= g_list_next(it
);
1707 if (start
== NULL
) start
= frame
->entries
;
1712 ObMenuEntryFrame
*e
= it
->data
;
1713 gunichar entrykey
= 0;
1715 if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
)
1716 entrykey
= e
->entry
->data
.normal
.shortcut
;
1717 else if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_SUBMENU
)
1718 entrykey
= e
->entry
->data
.submenu
.submenu
->shortcut
;
1720 if (unikey
== entrykey
) {
1721 if (found
== NULL
) found
= e
;
1725 /* next with wraparound */
1726 it
= g_list_next(it
);
1727 if (it
== NULL
) it
= frame
->entries
;
1728 } while (it
!= start
);
1731 if (found
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
&&
1734 menu_frame_select(frame
, found
, TRUE
);
1735 usleep(50000); /* highlight the item for a short bit so the
1736 user can see what happened */
1737 menu_entry_frame_execute(found
, state
);
1739 menu_frame_select(frame
, found
, TRUE
);
1741 menu_frame_select_next(frame
->child
);
1752 static gboolean
event_handle_menu(XEvent
*ev
)
1755 ObMenuEntryFrame
*e
;
1756 gboolean ret
= TRUE
;
1760 if (menu_hide_delay_reached() &&
1761 (ev
->xbutton
.button
< 4 || ev
->xbutton
.button
> 5))
1763 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1764 ev
->xbutton
.y_root
)))
1766 menu_frame_select(e
->frame
, e
, TRUE
);
1767 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1770 menu_frame_hide_all();
1774 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
))) {
1775 if (e
->ignore_enters
)
1777 else if (!(f
= find_active_menu()) ||
1779 f
->parent
== e
->frame
||
1780 f
->child
== e
->frame
)
1781 menu_frame_select(e
->frame
, e
, FALSE
);
1785 /*ignore leaves when we're already in the window */
1786 if (ev
->xcrossing
.detail
== NotifyInferior
)
1789 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
)) &&
1790 (f
= find_active_menu()) && f
->selected
== e
&&
1791 e
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1793 menu_frame_select(e
->frame
, NULL
, FALSE
);
1797 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1798 ev
->xmotion
.y_root
)))
1799 if (!(f
= find_active_menu()) ||
1801 f
->parent
== e
->frame
||
1802 f
->child
== e
->frame
)
1803 menu_frame_select(e
->frame
, e
, FALSE
);
1807 ret
= event_handle_menu_keyboard(ev
);
1813 static void event_handle_user_input(ObClient
*client
, XEvent
*e
)
1815 g_assert(e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
1816 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
1817 e
->type
== KeyRelease
);
1819 if (menu_frame_visible
) {
1820 if (event_handle_menu(e
))
1821 /* don't use the event if the menu used it, but if the menu
1822 didn't use it and it's a keypress that is bound, it will
1823 close the menu and be used */
1827 /* if the keyboard interactive action uses the event then dont
1828 use it for bindings. likewise is moveresize uses the event. */
1829 if (!actions_interactive_input_event(e
) && !moveresize_event(e
)) {
1830 if (moveresize_in_progress
)
1831 /* make further actions work on the client being
1833 client
= moveresize_client
;
1835 if (e
->type
== ButtonPress
||
1836 e
->type
== ButtonRelease
||
1837 e
->type
== MotionNotify
)
1839 /* the frame may not be "visible" but they can still click on it
1840 in the case where it is animating before disappearing */
1841 if (!client
|| !frame_iconify_animating(client
->frame
))
1842 mouse_event(client
, e
);
1844 keyboard_event((focus_cycle_target
? focus_cycle_target
:
1845 (client
? client
: focus_client
)), e
);
1849 static void focus_delay_dest(gpointer data
)
1854 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
)
1856 const ObFocusDelayData
*f1
= d1
;
1857 return f1
->client
== d2
;
1860 static gboolean
focus_delay_func(gpointer data
)
1862 ObFocusDelayData
*d
= data
;
1863 Time old
= event_curtime
;
1865 /* don't move focus and kill the menu or the move/resize */
1866 if (menu_frame_visible
|| moveresize_in_progress
) return FALSE
;
1868 event_curtime
= d
->time
;
1869 if (focus_client
!= d
->client
) {
1870 if (client_focus(d
->client
) && config_focus_raise
)
1871 stacking_raise(CLIENT_AS_WINDOW(d
->client
));
1873 event_curtime
= old
;
1874 return FALSE
; /* no repeat */
1877 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1879 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1883 void event_halt_focus_delay(void)
1885 /* ignore all enter events up till now */
1886 event_end_ignore_all_enters(1);
1887 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1890 gulong
event_start_ignore_all_enters(void)
1892 XSync(ob_display
, FALSE
);
1893 return LastKnownRequestProcessed(ob_display
);
1896 void event_end_ignore_all_enters(gulong start
)
1900 g_assert(start
!= 0);
1901 XSync(ob_display
, FALSE
);
1903 r
= g_new(ObSerialRange
, 1);
1905 r
->end
= LastKnownRequestProcessed(ob_display
);
1906 ignore_serials
= g_slist_prepend(ignore_serials
, r
);
1908 /* increment the serial so we don't ignore events we weren't meant to */
1909 XSync(ob_display
, FALSE
);
1912 static gboolean
is_enter_focus_event_ignored(XEvent
*e
)
1916 g_assert(e
->type
== EnterNotify
&&
1917 !(e
->xcrossing
.mode
== NotifyGrab
||
1918 e
->xcrossing
.mode
== NotifyUngrab
||
1919 e
->xcrossing
.detail
== NotifyInferior
));
1921 for (it
= ignore_serials
; it
; it
= next
) {
1922 ObSerialRange
*r
= it
->data
;
1924 next
= g_slist_next(it
);
1926 if ((glong
)(e
->xany
.serial
- r
->end
) > 0) {
1928 ignore_serials
= g_slist_delete_link(ignore_serials
, it
);
1931 else if ((glong
)(e
->xany
.serial
- r
->start
) >= 0)
1937 void event_cancel_all_key_grabs(void)
1939 if (actions_interactive_act_running()) {
1940 actions_interactive_cancel_act();
1941 ob_debug("KILLED interactive action\n");
1943 else if (menu_frame_visible
) {
1944 menu_frame_hide_all();
1945 ob_debug("KILLED open menus\n");
1947 else if (moveresize_in_progress
) {
1948 moveresize_end(TRUE
);
1949 ob_debug("KILLED interactive moveresize\n");
1951 else if (grab_on_keyboard()) {
1953 ob_debug("KILLED active grab on keyboard\n");
1956 ungrab_passive_key();
1959 gboolean
event_time_after(Time t1
, Time t2
)
1961 g_assert(t1
!= CurrentTime
);
1962 g_assert(t2
!= CurrentTime
);
1965 Timestamp values wrap around (after about 49.7 days). The server, given
1966 its current time is represented by timestamp T, always interprets
1967 timestamps from clients by treating half of the timestamp space as being
1968 later in time than T.
1969 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1972 /* TIME_HALF is half of the number space of a Time type variable */
1973 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1975 if (t2
>= TIME_HALF
)
1976 /* t2 is in the second half so t1 might wrap around and be smaller than
1978 return t1
>= t2
|| t1
< (t2
+ TIME_HALF
);
1980 /* t2 is in the first half so t1 has to come after it */
1981 return t1
>= t2
&& t1
< (t2
+ TIME_HALF
);
1984 Time
event_get_server_time(void)
1986 /* Generate a timestamp */
1989 XChangeProperty(ob_display
, screen_support_win
,
1990 prop_atoms
.wm_class
, prop_atoms
.string
,
1991 8, PropModeAppend
, NULL
, 0);
1992 XWindowEvent(ob_display
, screen_support_win
, PropertyChangeMask
, &event
);
1993 return event
.xproperty
.time
;