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 /* The time for the current event being processed */
101 Time event_curtime
= 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
242 static void event_hack_mods(XEvent
*e
)
247 e
->xbutton
.state
= modkeys_only_modifier_masks(e
->xbutton
.state
);
250 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
253 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
254 /* remove from the state the mask of the modifier key being released,
255 if it is a modifier key being released that is */
256 e
->xkey
.state
&= ~modkeys_keycode_to_mask(e
->xkey
.keycode
);
259 e
->xmotion
.state
= modkeys_only_modifier_masks(e
->xmotion
.state
);
260 /* compress events */
263 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
265 e
->xmotion
.x
= ce
.xmotion
.x
;
266 e
->xmotion
.y
= ce
.xmotion
.y
;
267 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
268 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
275 static gboolean
wanted_focusevent(XEvent
*e
, gboolean in_client_only
)
277 gint mode
= e
->xfocus
.mode
;
278 gint detail
= e
->xfocus
.detail
;
279 Window win
= e
->xany
.window
;
281 if (e
->type
== FocusIn
) {
282 /* These are ones we never want.. */
284 /* This means focus was given by a keyboard/mouse grab. */
285 if (mode
== NotifyGrab
)
287 /* This means focus was given back from a keyboard/mouse grab. */
288 if (mode
== NotifyUngrab
)
291 /* These are the ones we want.. */
293 if (win
== RootWindow(ob_display
, ob_screen
)) {
294 /* If looking for a focus in on a client, then always return
295 FALSE for focus in's to the root window */
298 /* This means focus reverted off of a client */
299 else if (detail
== NotifyPointerRoot
||
300 detail
== NotifyDetailNone
||
301 detail
== NotifyInferior
||
302 /* This means focus got here from another screen */
303 detail
== NotifyNonlinear
)
309 /* It was on a client, was it a valid one?
310 It's possible to get a FocusIn event for a client that was managed
313 if (in_client_only
) {
314 ObWindow
*w
= g_hash_table_lookup(window_map
, &e
->xfocus
.window
);
315 if (!w
|| !WINDOW_IS_CLIENT(w
))
319 /* This means focus reverted to parent from the client (this
320 happens often during iconify animation) */
321 if (detail
== NotifyInferior
)
325 /* This means focus moved from the root window to a client */
326 if (detail
== NotifyVirtual
)
328 /* This means focus moved from one client to another */
329 if (detail
== NotifyNonlinearVirtual
)
335 g_assert(e
->type
== FocusOut
);
337 /* These are ones we never want.. */
339 /* This means focus was taken by a keyboard/mouse grab. */
340 if (mode
== NotifyGrab
)
342 /* This means focus was grabbed on a window and it was released. */
343 if (mode
== NotifyUngrab
)
346 /* Focus left the root window revertedto state */
347 if (win
== RootWindow(ob_display
, ob_screen
))
350 /* These are the ones we want.. */
352 /* This means focus moved from a client to the root window */
353 if (detail
== NotifyVirtual
)
355 /* This means focus moved from one client to another */
356 if (detail
== NotifyNonlinearVirtual
)
364 static Bool
event_look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
366 return e
->type
== FocusIn
&& wanted_focusevent(e
, FALSE
);
369 static Bool
event_look_for_focusin_client(Display
*d
, XEvent
*e
, XPointer arg
)
371 return e
->type
== FocusIn
&& wanted_focusevent(e
, TRUE
);
374 static void print_focusevent(XEvent
*e
)
376 gint mode
= e
->xfocus
.mode
;
377 gint detail
= e
->xfocus
.detail
;
378 Window win
= e
->xany
.window
;
379 const gchar
*modestr
, *detailstr
;
382 case NotifyNormal
: modestr
="NotifyNormal"; break;
383 case NotifyGrab
: modestr
="NotifyGrab"; break;
384 case NotifyUngrab
: modestr
="NotifyUngrab"; break;
385 case NotifyWhileGrabbed
: modestr
="NotifyWhileGrabbed"; break;
388 case NotifyAncestor
: detailstr
="NotifyAncestor"; break;
389 case NotifyVirtual
: detailstr
="NotifyVirtual"; break;
390 case NotifyInferior
: detailstr
="NotifyInferior"; break;
391 case NotifyNonlinear
: detailstr
="NotifyNonlinear"; break;
392 case NotifyNonlinearVirtual
: detailstr
="NotifyNonlinearVirtual"; break;
393 case NotifyPointer
: detailstr
="NotifyPointer"; break;
394 case NotifyPointerRoot
: detailstr
="NotifyPointerRoot"; break;
395 case NotifyDetailNone
: detailstr
="NotifyDetailNone"; break;
398 if (mode
== NotifyGrab
|| mode
== NotifyUngrab
)
403 ob_debug_type(OB_DEBUG_FOCUS
, "Focus%s 0x%x mode=%s detail=%s\n",
404 (e
->xfocus
.type
== FocusIn
? "In" : "Out"),
410 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
415 if (!wanted_focusevent(e
, FALSE
))
420 if (!wanted_focusevent(e
, FALSE
))
427 static void event_process(const XEvent
*ec
, gpointer data
)
430 ObClient
*client
= NULL
;
432 ObDockApp
*dockapp
= NULL
;
433 ObWindow
*obwin
= NULL
;
435 ObEventData
*ed
= data
;
437 /* make a copy we can mangle */
441 window
= event_get_window(e
);
442 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
443 switch (obwin
->type
) {
445 dock
= WINDOW_AS_DOCK(obwin
);
448 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
451 client
= WINDOW_AS_CLIENT(obwin
);
454 case Window_Internal
:
455 /* not to be used for events */
456 g_assert_not_reached();
461 event_set_curtime(e
);
463 if (event_ignore(e
, client
)) {
470 /* deal with it in the kernel */
472 if (menu_frame_visible
&&
473 (e
->type
== EnterNotify
|| e
->type
== LeaveNotify
))
475 /* crossing events for menu */
476 event_handle_menu(e
);
477 } else if (e
->type
== FocusIn
) {
479 e
->xfocus
.detail
== NotifyInferior
)
481 ob_debug_type(OB_DEBUG_FOCUS
,
482 "Focus went to the frame window");
484 focus_left_screen
= FALSE
;
486 focus_fallback(FALSE
, config_focus_under_mouse
, TRUE
);
488 /* We don't get a FocusOut for this case, because it's just moving
489 from our Inferior up to us. This happens when iconifying a
490 window with RevertToParent focus */
491 frame_adjust_focus(client
->frame
, FALSE
);
492 /* focus_set_client(NULL) has already been called */
493 client_calc_layer(client
);
495 if (e
->xfocus
.detail
== NotifyPointerRoot
||
496 e
->xfocus
.detail
== NotifyDetailNone
||
497 e
->xfocus
.detail
== NotifyInferior
||
498 e
->xfocus
.detail
== NotifyNonlinear
)
502 ob_debug_type(OB_DEBUG_FOCUS
,
503 "Focus went to root or pointer root/none\n");
505 if (e
->xfocus
.detail
== NotifyInferior
||
506 e
->xfocus
.detail
== NotifyNonlinear
)
508 focus_left_screen
= FALSE
;
511 /* If another FocusIn is in the queue then don't fallback yet. This
512 fixes the fun case of:
513 window map -> send focusin
514 window unmap -> get focusout
515 window map -> send focusin
516 get first focus out -> fall back to something (new window
517 hasn't received focus yet, so something else) -> send focusin
518 which means the "something else" is the last thing to get a
519 focusin sent to it, so the new window doesn't end up with focus.
521 But if the other focus in is something like PointerRoot then we
522 still want to fall back.
524 if (XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin_client
,
527 XPutBackEvent(ob_display
, &ce
);
528 ob_debug_type(OB_DEBUG_FOCUS
,
529 " but another FocusIn is coming\n");
531 /* Focus has been reverted.
533 FocusOut events come after UnmapNotify, so we don't need to
534 worry about focusing an invalid window
537 if (!focus_left_screen
)
538 focus_fallback(FALSE
, config_focus_under_mouse
, TRUE
);
543 ob_debug_type(OB_DEBUG_FOCUS
,
544 "Focus went to a window that is already gone\n");
546 /* If you send focus to a window and then it disappears, you can
547 get the FocusIn for it, after it is unmanaged.
548 Just wait for the next FocusOut/FocusIn pair, but make note that
549 the window that was focused no longer is. */
550 focus_set_client(NULL
);
552 else if (client
!= focus_client
) {
553 focus_left_screen
= FALSE
;
554 frame_adjust_focus(client
->frame
, TRUE
);
555 focus_set_client(client
);
556 client_calc_layer(client
);
557 client_bring_helper_windows(client
);
559 } else if (e
->type
== FocusOut
) {
562 /* Look for the followup FocusIn */
563 if (!XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin
, NULL
)) {
564 /* There is no FocusIn, this means focus went to a window that
565 is not being managed, or a window on another screen. */
569 xerror_set_ignore(TRUE
);
570 if (XGetInputFocus(ob_display
, &win
, &i
) != 0 &&
571 XGetGeometry(ob_display
, win
, &root
, &i
,&i
,&u
,&u
,&u
,&u
) != 0 &&
572 root
!= RootWindow(ob_display
, ob_screen
))
574 ob_debug_type(OB_DEBUG_FOCUS
,
575 "Focus went to another screen !\n");
576 focus_left_screen
= TRUE
;
579 ob_debug_type(OB_DEBUG_FOCUS
,
580 "Focus went to a black hole !\n");
581 xerror_set_ignore(FALSE
);
582 /* nothing is focused */
583 focus_set_client(NULL
);
585 /* Focus moved, so process the FocusIn event */
586 ObEventData ed
= { .ignored
= FALSE
};
587 event_process(&ce
, &ed
);
589 /* The FocusIn was ignored, this means it was on a window
590 that isn't a client. */
591 ob_debug_type(OB_DEBUG_FOCUS
,
592 "Focus went to an unmanaged window 0x%x !\n",
594 focus_fallback(TRUE
, config_focus_under_mouse
, TRUE
);
598 if (client
&& client
!= focus_client
) {
599 frame_adjust_focus(client
->frame
, FALSE
);
600 /* focus_set_client(NULL) has already been called in this
601 section or by focus_fallback */
602 client_calc_layer(client
);
606 event_handle_client(client
, e
);
608 event_handle_dockapp(dockapp
, e
);
610 event_handle_dock(dock
, e
);
611 else if (window
== RootWindow(ob_display
, ob_screen
))
612 event_handle_root(e
);
613 else if (e
->type
== MapRequest
)
614 client_manage(window
);
615 else if (e
->type
== ClientMessage
) {
616 /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
617 windows that are not managed yet. */
618 if (e
->xclient
.message_type
== prop_atoms
.net_request_frame_extents
) {
619 /* Pretend to manage the client, getting information used to
620 determine its decorations */
621 ObClient
*c
= client_fake_manage(e
->xclient
.window
);
624 /* set the frame extents on the window */
625 vals
[0] = c
->frame
->size
.left
;
626 vals
[1] = c
->frame
->size
.right
;
627 vals
[2] = c
->frame
->size
.top
;
628 vals
[3] = c
->frame
->size
.bottom
;
629 PROP_SETA32(e
->xclient
.window
, net_frame_extents
,
632 /* Free the pretend client */
633 client_fake_unmanage(c
);
636 else if (e
->type
== ConfigureRequest
) {
637 /* unhandled configure requests must be used to configure the
641 xwc
.x
= e
->xconfigurerequest
.x
;
642 xwc
.y
= e
->xconfigurerequest
.y
;
643 xwc
.width
= e
->xconfigurerequest
.width
;
644 xwc
.height
= e
->xconfigurerequest
.height
;
645 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
646 xwc
.sibling
= e
->xconfigurerequest
.above
;
647 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
649 /* we are not to be held responsible if someone sends us an
651 xerror_set_ignore(TRUE
);
652 XConfigureWindow(ob_display
, window
,
653 e
->xconfigurerequest
.value_mask
, &xwc
);
654 xerror_set_ignore(FALSE
);
657 else if (extensions_sync
&&
658 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
660 XSyncAlarmNotifyEvent
*se
= (XSyncAlarmNotifyEvent
*)e
;
661 if (se
->alarm
== moveresize_alarm
&& moveresize_in_progress
)
666 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
667 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
668 e
->type
== KeyRelease
)
670 event_handle_user_input(client
, e
);
673 /* if something happens and it's not from an XEvent, then we don't know
675 event_curtime
= CurrentTime
;
678 static void event_handle_root(XEvent
*e
)
684 ob_debug("Another WM has requested to replace us. Exiting.\n");
689 if (e
->xclient
.format
!= 32) break;
691 msgtype
= e
->xclient
.message_type
;
692 if (msgtype
== prop_atoms
.net_current_desktop
) {
693 guint d
= e
->xclient
.data
.l
[0];
694 if (d
< screen_num_desktops
) {
695 event_curtime
= e
->xclient
.data
.l
[1];
696 if (event_curtime
== 0)
697 ob_debug_type(OB_DEBUG_APP_BUGS
,
698 "_NET_CURRENT_DESKTOP message is missing "
700 screen_set_desktop(d
, TRUE
);
702 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
703 guint d
= e
->xclient
.data
.l
[0];
704 if (d
> 0 && d
<= 1000)
705 screen_set_num_desktops(d
);
706 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
707 screen_show_desktop(e
->xclient
.data
.l
[0] != 0, NULL
);
708 } else if (msgtype
== prop_atoms
.ob_control
) {
709 ob_debug("OB_CONTROL: %d\n", e
->xclient
.data
.l
[0]);
710 if (e
->xclient
.data
.l
[0] == 1)
712 else if (e
->xclient
.data
.l
[0] == 2)
717 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
) {
718 ob_debug("UPDATE DESKTOP NAMES\n");
719 screen_update_desktop_names();
721 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
722 screen_update_layout();
724 case ConfigureNotify
:
726 XRRUpdateConfiguration(e
);
735 void event_enter_client(ObClient
*client
)
737 g_assert(config_focus_follow
);
739 if (client_enter_focusable(client
) && client_can_focus(client
)) {
740 if (config_focus_delay
) {
741 ObFocusDelayData
*data
;
743 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
745 data
= g_new(ObFocusDelayData
, 1);
746 data
->client
= client
;
747 data
->time
= event_curtime
;
749 ob_main_loop_timeout_add(ob_main_loop
,
752 data
, focus_delay_cmp
, focus_delay_dest
);
754 ObFocusDelayData data
;
755 data
.client
= client
;
756 data
.time
= event_curtime
;
757 focus_delay_func(&data
);
762 static void event_handle_client(ObClient
*client
, XEvent
*e
)
767 static gint px
= -1, py
= -1;
772 /* save where the press occured for the first button pressed */
774 pb
= e
->xbutton
.button
;
779 /* Wheel buttons don't draw because they are an instant click, so it
780 is a waste of resources to go drawing it.
781 if the user is doing an intereactive thing, or has a menu open then
782 the mouse is grabbed (possibly) and if we get these events we don't
783 want to deal with them
785 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5) &&
788 /* use where the press occured */
789 con
= frame_context(client
, e
->xbutton
.window
, px
, py
);
790 con
= mouse_button_frame_context(con
, e
->xbutton
.button
,
793 if (e
->type
== ButtonRelease
&& e
->xbutton
.button
== pb
)
794 pb
= 0, px
= py
= -1;
797 case OB_FRAME_CONTEXT_MAXIMIZE
:
798 client
->frame
->max_press
= (e
->type
== ButtonPress
);
799 frame_adjust_state(client
->frame
);
801 case OB_FRAME_CONTEXT_CLOSE
:
802 client
->frame
->close_press
= (e
->type
== ButtonPress
);
803 frame_adjust_state(client
->frame
);
805 case OB_FRAME_CONTEXT_ICONIFY
:
806 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
807 frame_adjust_state(client
->frame
);
809 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
810 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
811 frame_adjust_state(client
->frame
);
813 case OB_FRAME_CONTEXT_SHADE
:
814 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
815 frame_adjust_state(client
->frame
);
818 /* nothing changes with clicks for any other contexts */
824 /* when there is a grab on the pointer, we won't get enter/leave
825 notifies, but we still get motion events */
826 if (grab_on_pointer()) break;
828 con
= frame_context(client
, e
->xmotion
.window
,
829 e
->xmotion
.x
, e
->xmotion
.y
);
831 case OB_FRAME_CONTEXT_TITLEBAR
:
832 case OB_FRAME_CONTEXT_TLCORNER
:
833 case OB_FRAME_CONTEXT_TRCORNER
:
834 /* we've left the button area inside the titlebar */
835 if (client
->frame
->max_hover
|| client
->frame
->desk_hover
||
836 client
->frame
->shade_hover
|| client
->frame
->iconify_hover
||
837 client
->frame
->close_hover
)
839 client
->frame
->max_hover
= FALSE
;
840 client
->frame
->desk_hover
= FALSE
;
841 client
->frame
->shade_hover
= FALSE
;
842 client
->frame
->iconify_hover
= FALSE
;
843 client
->frame
->close_hover
= FALSE
;
844 frame_adjust_state(client
->frame
);
847 case OB_FRAME_CONTEXT_MAXIMIZE
:
848 if (!client
->frame
->max_hover
) {
849 client
->frame
->max_hover
= TRUE
;
850 frame_adjust_state(client
->frame
);
853 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
854 if (!client
->frame
->desk_hover
) {
855 client
->frame
->desk_hover
= TRUE
;
856 frame_adjust_state(client
->frame
);
859 case OB_FRAME_CONTEXT_SHADE
:
860 if (!client
->frame
->shade_hover
) {
861 client
->frame
->shade_hover
= TRUE
;
862 frame_adjust_state(client
->frame
);
865 case OB_FRAME_CONTEXT_ICONIFY
:
866 if (!client
->frame
->iconify_hover
) {
867 client
->frame
->iconify_hover
= TRUE
;
868 frame_adjust_state(client
->frame
);
871 case OB_FRAME_CONTEXT_CLOSE
:
872 if (!client
->frame
->close_hover
) {
873 client
->frame
->close_hover
= TRUE
;
874 frame_adjust_state(client
->frame
);
882 con
= frame_context(client
, e
->xcrossing
.window
,
883 e
->xcrossing
.x
, e
->xcrossing
.y
);
885 case OB_FRAME_CONTEXT_TITLEBAR
:
886 case OB_FRAME_CONTEXT_TLCORNER
:
887 case OB_FRAME_CONTEXT_TRCORNER
:
888 /* we've left the button area inside the titlebar */
889 if (client
->frame
->max_hover
|| client
->frame
->desk_hover
||
890 client
->frame
->shade_hover
|| client
->frame
->iconify_hover
||
891 client
->frame
->close_hover
)
893 client
->frame
->max_hover
= FALSE
;
894 client
->frame
->desk_hover
= FALSE
;
895 client
->frame
->shade_hover
= FALSE
;
896 client
->frame
->iconify_hover
= FALSE
;
897 client
->frame
->close_hover
= FALSE
;
898 frame_adjust_state(client
->frame
);
901 case OB_FRAME_CONTEXT_MAXIMIZE
:
902 client
->frame
->max_hover
= FALSE
;
903 frame_adjust_state(client
->frame
);
905 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
906 client
->frame
->desk_hover
= FALSE
;
907 frame_adjust_state(client
->frame
);
909 case OB_FRAME_CONTEXT_SHADE
:
910 client
->frame
->shade_hover
= FALSE
;
911 frame_adjust_state(client
->frame
);
913 case OB_FRAME_CONTEXT_ICONIFY
:
914 client
->frame
->iconify_hover
= FALSE
;
915 frame_adjust_state(client
->frame
);
917 case OB_FRAME_CONTEXT_CLOSE
:
918 client
->frame
->close_hover
= FALSE
;
919 frame_adjust_state(client
->frame
);
921 case OB_FRAME_CONTEXT_FRAME
:
922 /* When the mouse leaves an animating window, don't use the
923 corresponding enter events. Pretend like the animating window
924 doesn't even exist..! */
925 if (frame_iconify_animating(client
->frame
))
926 event_end_ignore_all_enters(event_start_ignore_all_enters());
928 ob_debug_type(OB_DEBUG_FOCUS
,
929 "%sNotify mode %d detail %d on %lx\n",
930 (e
->type
== EnterNotify
? "Enter" : "Leave"),
932 e
->xcrossing
.detail
, (client
?client
->window
:0));
933 if (grab_on_keyboard())
935 if (config_focus_follow
&& config_focus_delay
&&
936 /* leave inferior events can happen when the mouse goes onto
937 the window's border and then into the window before the
939 e
->xcrossing
.detail
!= NotifyInferior
)
941 ob_main_loop_timeout_remove_data(ob_main_loop
,
952 con
= frame_context(client
, e
->xcrossing
.window
,
953 e
->xcrossing
.x
, e
->xcrossing
.y
);
955 case OB_FRAME_CONTEXT_MAXIMIZE
:
956 client
->frame
->max_hover
= TRUE
;
957 frame_adjust_state(client
->frame
);
959 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
960 client
->frame
->desk_hover
= TRUE
;
961 frame_adjust_state(client
->frame
);
963 case OB_FRAME_CONTEXT_SHADE
:
964 client
->frame
->shade_hover
= TRUE
;
965 frame_adjust_state(client
->frame
);
967 case OB_FRAME_CONTEXT_ICONIFY
:
968 client
->frame
->iconify_hover
= TRUE
;
969 frame_adjust_state(client
->frame
);
971 case OB_FRAME_CONTEXT_CLOSE
:
972 client
->frame
->close_hover
= TRUE
;
973 frame_adjust_state(client
->frame
);
975 case OB_FRAME_CONTEXT_FRAME
:
976 if (grab_on_keyboard())
978 if (e
->xcrossing
.mode
== NotifyGrab
||
979 e
->xcrossing
.mode
== NotifyUngrab
||
980 /*ignore enters when we're already in the window */
981 e
->xcrossing
.detail
== NotifyInferior
||
982 is_enter_focus_event_ignored(e
))
984 ob_debug_type(OB_DEBUG_FOCUS
,
985 "%sNotify mode %d detail %d on %lx IGNORED\n",
986 (e
->type
== EnterNotify
? "Enter" : "Leave"),
988 e
->xcrossing
.detail
, client
?client
->window
:0);
991 ob_debug_type(OB_DEBUG_FOCUS
,
992 "%sNotify mode %d detail %d on %lx, "
994 (e
->type
== EnterNotify
? "Enter" : "Leave"),
996 e
->xcrossing
.detail
, (client
?client
->window
:0));
997 if (config_focus_follow
)
998 event_enter_client(client
);
1006 case ConfigureRequest
:
1008 /* dont compress these unless you're going to watch for property
1009 notifies in between (these can change what the configure would
1011 also you can't compress stacking events
1015 gboolean move
= FALSE
;
1016 gboolean resize
= FALSE
;
1018 /* get the current area */
1019 RECT_TO_DIMS(client
->area
, x
, y
, w
, h
);
1021 ob_debug("ConfigureRequest for \"%s\" desktop %d wmstate %d "
1023 " x %d y %d w %d h %d b %d\n",
1025 screen_desktop
, client
->wmstate
, client
->frame
->visible
,
1026 x
, y
, w
, h
, client
->border_width
);
1028 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
1029 if (client
->border_width
!= e
->xconfigurerequest
.border_width
) {
1030 client
->border_width
= e
->xconfigurerequest
.border_width
;
1032 /* if the border width is changing then that is the same
1033 as requesting a resize, but we don't actually change
1034 the client's border, so it will change their root
1035 coordiantes (since they include the border width) and
1036 we need to a notify then */
1041 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
1042 ObClient
*sibling
= NULL
;
1043 gulong ignore_start
;
1046 /* get the sibling */
1047 if (e
->xconfigurerequest
.value_mask
& CWSibling
) {
1049 win
= g_hash_table_lookup(window_map
,
1050 &e
->xconfigurerequest
.above
);
1051 if (win
&& WINDOW_IS_CLIENT(win
) &&
1052 WINDOW_AS_CLIENT(win
) != client
)
1054 sibling
= WINDOW_AS_CLIENT(win
);
1057 /* an invalid sibling was specified so don't restack at
1058 all, it won't make sense no matter what we do */
1063 if (!config_focus_under_mouse
)
1064 ignore_start
= event_start_ignore_all_enters();
1065 stacking_restack_request(client
, sibling
,
1066 e
->xconfigurerequest
.detail
);
1067 if (!config_focus_under_mouse
)
1068 event_end_ignore_all_enters(ignore_start
);
1071 /* a stacking change moves the window without resizing */
1075 if ((e
->xconfigurerequest
.value_mask
& CWX
) ||
1076 (e
->xconfigurerequest
.value_mask
& CWY
) ||
1077 (e
->xconfigurerequest
.value_mask
& CWWidth
) ||
1078 (e
->xconfigurerequest
.value_mask
& CWHeight
))
1080 if (e
->xconfigurerequest
.value_mask
& CWX
) {
1081 /* don't allow clients to move shaded windows (fvwm does this)
1083 if (!client
->shaded
)
1084 x
= e
->xconfigurerequest
.x
;
1087 if (e
->xconfigurerequest
.value_mask
& CWY
) {
1088 /* don't allow clients to move shaded windows (fvwm does this)
1090 if (!client
->shaded
)
1091 y
= e
->xconfigurerequest
.y
;
1095 if (e
->xconfigurerequest
.value_mask
& CWWidth
) {
1096 w
= e
->xconfigurerequest
.width
;
1099 if (e
->xconfigurerequest
.value_mask
& CWHeight
) {
1100 h
= e
->xconfigurerequest
.height
;
1105 ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d "
1106 "move %d resize %d\n",
1107 e
->xconfigurerequest
.value_mask
& CWX
, x
,
1108 e
->xconfigurerequest
.value_mask
& CWY
, y
,
1109 e
->xconfigurerequest
.value_mask
& CWWidth
, w
,
1110 e
->xconfigurerequest
.value_mask
& CWHeight
, h
,
1113 /* check for broken apps moving to their root position
1115 XXX remove this some day...that would be nice. right now all
1116 kde apps do this when they try activate themselves on another
1117 desktop. eg. open amarok window on desktop 1, switch to desktop
1118 2, click amarok tray icon. it will move by its decoration size.
1120 if (x
!= client
->area
.x
&&
1121 x
== (client
->frame
->area
.x
+ client
->frame
->size
.left
-
1122 (gint
)client
->border_width
) &&
1123 y
!= client
->area
.y
&&
1124 y
== (client
->frame
->area
.y
+ client
->frame
->size
.top
-
1125 (gint
)client
->border_width
) &&
1126 w
== client
->area
.width
&&
1127 h
== client
->area
.height
)
1129 ob_debug_type(OB_DEBUG_APP_BUGS
,
1130 "Application %s is trying to move via "
1131 "ConfigureRequest to it's root window position "
1132 "but it is not using StaticGravity\n",
1138 /* they still requested a move, so don't change whether a
1139 notify is sent or not */
1145 client_try_configure(client
, &x
, &y
, &w
, &h
, &lw
, &lh
, FALSE
);
1147 /* if x was not given, then use gravity to figure out the new
1148 x. the reference point should not be moved */
1149 if ((e
->xconfigurerequest
.value_mask
& CWWidth
&&
1150 !(e
->xconfigurerequest
.value_mask
& CWX
)))
1151 client_gravity_resize_w(client
, &x
, client
->area
.width
, w
);
1152 /* if y was not given, then use gravity to figure out the new
1153 y. the reference point should not be moved */
1154 if ((e
->xconfigurerequest
.value_mask
& CWHeight
&&
1155 !(e
->xconfigurerequest
.value_mask
& CWY
)))
1156 client_gravity_resize_h(client
, &y
, client
->area
.height
,h
);
1158 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1160 ob_debug("Granting ConfigureRequest x %d y %d w %d h %d\n",
1162 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
, TRUE
);
1167 if (client
->ignore_unmaps
) {
1168 client
->ignore_unmaps
--;
1171 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
1172 "ignores left %d\n",
1173 client
->window
, e
->xunmap
.event
, e
->xunmap
.from_configure
,
1174 client
->ignore_unmaps
);
1175 client_unmanage(client
);
1178 ob_debug("DestroyNotify for window 0x%x\n", client
->window
);
1179 client_unmanage(client
);
1181 case ReparentNotify
:
1182 /* this is when the client is first taken captive in the frame */
1183 if (e
->xreparent
.parent
== client
->frame
->window
) break;
1186 This event is quite rare and is usually handled in unmapHandler.
1187 However, if the window is unmapped when the reparent event occurs,
1188 the window manager never sees it because an unmap event is not sent
1189 to an already unmapped window.
1192 /* we don't want the reparent event, put it back on the stack for the
1193 X server to deal with after we unmanage the window */
1194 XPutBackEvent(ob_display
, e
);
1196 ob_debug("ReparentNotify for window 0x%x\n", client
->window
);
1197 client_unmanage(client
);
1200 ob_debug("MapRequest for 0x%lx\n", client
->window
);
1201 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
1202 does, we don't want it!
1203 it can happen now when the window is on
1204 another desktop, but we still don't
1206 client_activate(client
, FALSE
, TRUE
, TRUE
, TRUE
);
1209 /* validate cuz we query stuff off the client here */
1210 if (!client_validate(client
)) break;
1212 if (e
->xclient
.format
!= 32) return;
1214 msgtype
= e
->xclient
.message_type
;
1215 if (msgtype
== prop_atoms
.wm_change_state
) {
1216 /* compress changes into a single change */
1217 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1219 /* XXX: it would be nice to compress ALL messages of a
1220 type, not just messages in a row without other
1221 message types between. */
1222 if (ce
.xclient
.message_type
!= msgtype
) {
1223 XPutBackEvent(ob_display
, &ce
);
1226 e
->xclient
= ce
.xclient
;
1228 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
1229 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
1230 /* compress changes into a single change */
1231 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1233 /* XXX: it would be nice to compress ALL messages of a
1234 type, not just messages in a row without other
1235 message types between. */
1236 if (ce
.xclient
.message_type
!= msgtype
) {
1237 XPutBackEvent(ob_display
, &ce
);
1240 e
->xclient
= ce
.xclient
;
1242 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
1243 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
1244 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
1246 } else if (msgtype
== prop_atoms
.net_wm_state
) {
1247 gulong ignore_start
;
1249 /* can't compress these */
1250 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1251 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
1252 e
->xclient
.data
.l
[0] == 1 ? "Add" :
1253 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1254 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
1257 /* ignore enter events caused by these like ob actions do */
1258 if (!config_focus_under_mouse
)
1259 ignore_start
= event_start_ignore_all_enters();
1260 client_set_state(client
, e
->xclient
.data
.l
[0],
1261 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
1262 if (!config_focus_under_mouse
)
1263 event_end_ignore_all_enters(ignore_start
);
1264 } else if (msgtype
== prop_atoms
.net_close_window
) {
1265 ob_debug("net_close_window for 0x%lx\n", client
->window
);
1266 client_close(client
);
1267 } else if (msgtype
== prop_atoms
.net_active_window
) {
1268 ob_debug("net_active_window for 0x%lx source=%s\n",
1270 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
1271 (e
->xclient
.data
.l
[0] == 1 ? "application" :
1272 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
1273 /* XXX make use of data.l[2] !? */
1274 if (e
->xclient
.data
.l
[0] == 1 || e
->xclient
.data
.l
[0] == 2) {
1275 /* don't use the user's timestamp for client_focus, cuz if it's
1276 an old broken timestamp (happens all the time) then focus
1277 won't move even though we're trying to move it
1278 event_curtime = e->xclient.data.l[1];*/
1279 if (e
->xclient
.data
.l
[1] == 0)
1280 ob_debug_type(OB_DEBUG_APP_BUGS
,
1281 "_NET_ACTIVE_WINDOW message for window %s is"
1282 " missing a timestamp\n", client
->title
);
1284 ob_debug_type(OB_DEBUG_APP_BUGS
,
1285 "_NET_ACTIVE_WINDOW message for window %s is "
1286 "missing source indication\n");
1287 client_activate(client
, FALSE
, TRUE
, TRUE
,
1288 (e
->xclient
.data
.l
[0] == 0 ||
1289 e
->xclient
.data
.l
[0] == 2));
1290 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
1291 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1292 client
->window
, e
->xclient
.data
.l
[2]);
1293 if ((Atom
)e
->xclient
.data
.l
[2] ==
1294 prop_atoms
.net_wm_moveresize_size_topleft
||
1295 (Atom
)e
->xclient
.data
.l
[2] ==
1296 prop_atoms
.net_wm_moveresize_size_top
||
1297 (Atom
)e
->xclient
.data
.l
[2] ==
1298 prop_atoms
.net_wm_moveresize_size_topright
||
1299 (Atom
)e
->xclient
.data
.l
[2] ==
1300 prop_atoms
.net_wm_moveresize_size_right
||
1301 (Atom
)e
->xclient
.data
.l
[2] ==
1302 prop_atoms
.net_wm_moveresize_size_right
||
1303 (Atom
)e
->xclient
.data
.l
[2] ==
1304 prop_atoms
.net_wm_moveresize_size_bottomright
||
1305 (Atom
)e
->xclient
.data
.l
[2] ==
1306 prop_atoms
.net_wm_moveresize_size_bottom
||
1307 (Atom
)e
->xclient
.data
.l
[2] ==
1308 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1309 (Atom
)e
->xclient
.data
.l
[2] ==
1310 prop_atoms
.net_wm_moveresize_size_left
||
1311 (Atom
)e
->xclient
.data
.l
[2] ==
1312 prop_atoms
.net_wm_moveresize_move
||
1313 (Atom
)e
->xclient
.data
.l
[2] ==
1314 prop_atoms
.net_wm_moveresize_size_keyboard
||
1315 (Atom
)e
->xclient
.data
.l
[2] ==
1316 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1318 moveresize_start(client
, e
->xclient
.data
.l
[0],
1319 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1320 e
->xclient
.data
.l
[2]);
1322 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1323 prop_atoms
.net_wm_moveresize_cancel
)
1324 moveresize_end(TRUE
);
1325 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1326 gint ograv
, x
, y
, w
, h
;
1328 ograv
= client
->gravity
;
1330 if (e
->xclient
.data
.l
[0] & 0xff)
1331 client
->gravity
= e
->xclient
.data
.l
[0] & 0xff;
1333 if (e
->xclient
.data
.l
[0] & 1 << 8)
1334 x
= e
->xclient
.data
.l
[1];
1337 if (e
->xclient
.data
.l
[0] & 1 << 9)
1338 y
= e
->xclient
.data
.l
[2];
1342 if (e
->xclient
.data
.l
[0] & 1 << 10) {
1343 w
= e
->xclient
.data
.l
[3];
1345 /* if x was not given, then use gravity to figure out the new
1346 x. the reference point should not be moved */
1347 if (!(e
->xclient
.data
.l
[0] & 1 << 8))
1348 client_gravity_resize_w(client
, &x
, client
->area
.width
, w
);
1351 w
= client
->area
.width
;
1353 if (e
->xclient
.data
.l
[0] & 1 << 11) {
1354 h
= e
->xclient
.data
.l
[4];
1356 /* if y was not given, then use gravity to figure out the new
1357 y. the reference point should not be moved */
1358 if (!(e
->xclient
.data
.l
[0] & 1 << 9))
1359 client_gravity_resize_h(client
, &y
, client
->area
.height
,h
);
1362 h
= client
->area
.height
;
1364 ob_debug("MOVERESIZE x %d %d y %d %d (gravity %d)\n",
1365 e
->xclient
.data
.l
[0] & 1 << 8, x
,
1366 e
->xclient
.data
.l
[0] & 1 << 9, y
,
1369 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1371 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
, FALSE
);
1373 client
->gravity
= ograv
;
1374 } else if (msgtype
== prop_atoms
.net_restack_window
) {
1375 if (e
->xclient
.data
.l
[0] != 2) {
1376 ob_debug_type(OB_DEBUG_APP_BUGS
,
1377 "_NET_RESTACK_WINDOW sent for window %s with "
1378 "invalid source indication %ld\n",
1379 client
->title
, e
->xclient
.data
.l
[0]);
1381 ObClient
*sibling
= NULL
;
1382 if (e
->xclient
.data
.l
[1]) {
1383 ObWindow
*win
= g_hash_table_lookup
1384 (window_map
, &e
->xclient
.data
.l
[1]);
1385 if (WINDOW_IS_CLIENT(win
) &&
1386 WINDOW_AS_CLIENT(win
) != client
)
1388 sibling
= WINDOW_AS_CLIENT(win
);
1390 if (sibling
== NULL
)
1391 ob_debug_type(OB_DEBUG_APP_BUGS
,
1392 "_NET_RESTACK_WINDOW sent for window %s "
1393 "with invalid sibling 0x%x\n",
1394 client
->title
, e
->xclient
.data
.l
[1]);
1396 if (e
->xclient
.data
.l
[2] == Below
||
1397 e
->xclient
.data
.l
[2] == BottomIf
||
1398 e
->xclient
.data
.l
[2] == Above
||
1399 e
->xclient
.data
.l
[2] == TopIf
||
1400 e
->xclient
.data
.l
[2] == Opposite
)
1402 gulong ignore_start
;
1404 if (!config_focus_under_mouse
)
1405 ignore_start
= event_start_ignore_all_enters();
1406 /* just raise, don't activate */
1407 stacking_restack_request(client
, sibling
,
1408 e
->xclient
.data
.l
[2]);
1409 if (!config_focus_under_mouse
)
1410 event_end_ignore_all_enters(ignore_start
);
1412 /* send a synthetic ConfigureNotify, cuz this is supposed
1413 to be like a ConfigureRequest. */
1414 client_reconfigure(client
, TRUE
);
1416 ob_debug_type(OB_DEBUG_APP_BUGS
,
1417 "_NET_RESTACK_WINDOW sent for window %s "
1418 "with invalid detail %d\n",
1419 client
->title
, e
->xclient
.data
.l
[2]);
1423 case PropertyNotify
:
1424 /* validate cuz we query stuff off the client here */
1425 if (!client_validate(client
)) break;
1427 /* compress changes to a single property into a single change */
1428 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1432 /* XXX: it would be nice to compress ALL changes to a property,
1433 not just changes in a row without other props between. */
1435 a
= ce
.xproperty
.atom
;
1436 b
= e
->xproperty
.atom
;
1440 if ((a
== prop_atoms
.net_wm_name
||
1441 a
== prop_atoms
.wm_name
||
1442 a
== prop_atoms
.net_wm_icon_name
||
1443 a
== prop_atoms
.wm_icon_name
)
1445 (b
== prop_atoms
.net_wm_name
||
1446 b
== prop_atoms
.wm_name
||
1447 b
== prop_atoms
.net_wm_icon_name
||
1448 b
== prop_atoms
.wm_icon_name
)) {
1451 if (a
== prop_atoms
.net_wm_icon
&&
1452 b
== prop_atoms
.net_wm_icon
)
1455 XPutBackEvent(ob_display
, &ce
);
1459 msgtype
= e
->xproperty
.atom
;
1460 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1461 ob_debug("Update NORMAL hints\n");
1462 client_update_normal_hints(client
);
1463 /* normal hints can make a window non-resizable */
1464 client_setup_decor_and_functions(client
, FALSE
);
1466 /* make sure the client's sizes are within its bounds, but only
1467 reconfigure the window if it needs to. emacs will update its
1468 normal hints every time it receives a conigurenotify */
1469 client_reconfigure(client
, FALSE
);
1470 } else if (msgtype
== XA_WM_HINTS
) {
1471 client_update_wmhints(client
);
1472 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1473 client_update_transient_for(client
);
1474 client_get_type_and_transientness(client
);
1475 /* type may have changed, so update the layer */
1476 client_calc_layer(client
);
1477 client_setup_decor_and_functions(client
, TRUE
);
1478 } else if (msgtype
== prop_atoms
.net_wm_name
||
1479 msgtype
== prop_atoms
.wm_name
||
1480 msgtype
== prop_atoms
.net_wm_icon_name
||
1481 msgtype
== prop_atoms
.wm_icon_name
) {
1482 client_update_title(client
);
1483 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1484 client_update_protocols(client
);
1485 client_setup_decor_and_functions(client
, TRUE
);
1487 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1488 client_update_strut(client
);
1490 else if (msgtype
== prop_atoms
.net_wm_strut_partial
) {
1491 client_update_strut(client
);
1493 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1494 client_update_icons(client
);
1496 else if (msgtype
== prop_atoms
.net_wm_icon_geometry
) {
1497 client_update_icon_geometry(client
);
1500 else if (msgtype
== prop_atoms
.net_wm_sync_request_counter
) {
1501 client_update_sync_request_counter(client
);
1505 case ColormapNotify
:
1506 client_update_colormap(client
, e
->xcolormap
.colormap
);
1511 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1512 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1513 frame_adjust_shape(client
->frame
);
1519 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1523 if (e
->xbutton
.button
== 1)
1524 stacking_raise(DOCK_AS_WINDOW(s
));
1525 else if (e
->xbutton
.button
== 2)
1526 stacking_lower(DOCK_AS_WINDOW(s
));
1532 /* don't hide when moving into a dock app */
1533 if (e
->xcrossing
.detail
!= NotifyInferior
)
1539 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1543 dock_app_drag(app
, &e
->xmotion
);
1546 if (app
->ignore_unmaps
) {
1547 app
->ignore_unmaps
--;
1550 dock_remove(app
, TRUE
);
1553 dock_remove(app
, FALSE
);
1555 case ReparentNotify
:
1556 dock_remove(app
, FALSE
);
1558 case ConfigureNotify
:
1559 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1564 static ObMenuFrame
* find_active_menu()
1567 ObMenuFrame
*ret
= NULL
;
1569 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1578 static ObMenuFrame
* find_active_or_last_menu()
1580 ObMenuFrame
*ret
= NULL
;
1582 ret
= find_active_menu();
1583 if (!ret
&& menu_frame_visible
)
1584 ret
= menu_frame_visible
->data
;
1588 static gboolean
event_handle_menu_keyboard(XEvent
*ev
)
1590 guint keycode
, state
;
1593 gboolean ret
= FALSE
;
1595 keycode
= ev
->xkey
.keycode
;
1596 state
= ev
->xkey
.state
;
1597 unikey
= translate_unichar(keycode
);
1599 frame
= find_active_or_last_menu();
1601 g_assert_not_reached(); /* there is no active menu */
1603 /* Allow control while going thru the menu */
1604 else if (ev
->type
== KeyPress
&& (state
& ~ControlMask
) == 0) {
1605 if (keycode
== ob_keycode(OB_KEY_ESCAPE
)) {
1606 menu_frame_hide_all();
1610 else if (keycode
== ob_keycode(OB_KEY_LEFT
)) {
1611 /* Left goes to the parent menu */
1612 menu_frame_select(frame
, NULL
, TRUE
);
1616 else if (keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1617 /* Right goes to the selected submenu */
1618 if (frame
->child
) menu_frame_select_next(frame
->child
);
1622 else if (keycode
== ob_keycode(OB_KEY_UP
)) {
1623 menu_frame_select_previous(frame
);
1627 else if (keycode
== ob_keycode(OB_KEY_DOWN
)) {
1628 menu_frame_select_next(frame
);
1633 /* Use KeyRelease events for running things so that the key release doesn't
1634 get sent to the focused application.
1636 Allow ControlMask only, and don't bother if the menu is empty */
1637 else if (ev
->type
== KeyRelease
&& (state
& ~ControlMask
) == 0 &&
1640 if (keycode
== ob_keycode(OB_KEY_RETURN
)) {
1641 /* Enter runs the active item or goes into the submenu.
1642 Control-Enter runs it without closing the menu. */
1644 menu_frame_select_next(frame
->child
);
1645 else if (frame
->selected
)
1646 menu_entry_frame_execute(frame
->selected
, state
);
1651 /* keyboard accelerator shortcuts. (if it was a valid key) */
1652 else if (unikey
!= 0) {
1655 ObMenuEntryFrame
*found
= NULL
;
1656 guint num_found
= 0;
1658 /* start after the selected one */
1659 start
= frame
->entries
;
1660 if (frame
->selected
) {
1661 for (it
= start
; frame
->selected
!= it
->data
;
1662 it
= g_list_next(it
))
1663 g_assert(it
!= NULL
); /* nothing was selected? */
1664 /* next with wraparound */
1665 start
= g_list_next(it
);
1666 if (start
== NULL
) start
= frame
->entries
;
1671 ObMenuEntryFrame
*e
= it
->data
;
1672 gunichar entrykey
= 0;
1674 if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
)
1675 entrykey
= e
->entry
->data
.normal
.shortcut
;
1676 else if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_SUBMENU
)
1677 entrykey
= e
->entry
->data
.submenu
.submenu
->shortcut
;
1679 if (unikey
== entrykey
) {
1680 if (found
== NULL
) found
= e
;
1684 /* next with wraparound */
1685 it
= g_list_next(it
);
1686 if (it
== NULL
) it
= frame
->entries
;
1687 } while (it
!= start
);
1690 if (found
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
&&
1693 menu_frame_select(frame
, found
, TRUE
);
1694 usleep(50000); /* highlight the item for a short bit so the
1695 user can see what happened */
1696 menu_entry_frame_execute(found
, state
);
1698 menu_frame_select(frame
, found
, TRUE
);
1700 menu_frame_select_next(frame
->child
);
1711 static gboolean
event_handle_menu(XEvent
*ev
)
1714 ObMenuEntryFrame
*e
;
1715 gboolean ret
= TRUE
;
1719 if (menu_hide_delay_reached() &&
1720 (ev
->xbutton
.button
< 4 || ev
->xbutton
.button
> 5))
1722 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1723 ev
->xbutton
.y_root
)))
1725 menu_frame_select(e
->frame
, e
, TRUE
);
1726 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1729 menu_frame_hide_all();
1733 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
))) {
1734 if (e
->ignore_enters
)
1736 else if (!(f
= find_active_menu()) ||
1738 f
->parent
== e
->frame
||
1739 f
->child
== e
->frame
)
1740 menu_frame_select(e
->frame
, e
, FALSE
);
1744 /*ignore leaves when we're already in the window */
1745 if (ev
->xcrossing
.detail
== NotifyInferior
)
1748 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
)) &&
1749 (f
= find_active_menu()) && f
->selected
== e
&&
1750 e
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1752 menu_frame_select(e
->frame
, NULL
, FALSE
);
1756 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1757 ev
->xmotion
.y_root
)))
1758 if (!(f
= find_active_menu()) ||
1760 f
->parent
== e
->frame
||
1761 f
->child
== e
->frame
)
1762 menu_frame_select(e
->frame
, e
, FALSE
);
1766 ret
= event_handle_menu_keyboard(ev
);
1772 static void event_handle_user_input(ObClient
*client
, XEvent
*e
)
1774 g_assert(e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
1775 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
1776 e
->type
== KeyRelease
);
1778 if (menu_frame_visible
) {
1779 if (event_handle_menu(e
))
1780 /* don't use the event if the menu used it, but if the menu
1781 didn't use it and it's a keypress that is bound, it will
1782 close the menu and be used */
1786 /* if the keyboard interactive action uses the event then dont
1787 use it for bindings. likewise is moveresize uses the event. */
1788 if (!actions_interactive_input_event(e
) && !moveresize_event(e
)) {
1789 if (moveresize_in_progress
)
1790 /* make further actions work on the client being
1792 client
= moveresize_client
;
1794 if (e
->type
== ButtonPress
||
1795 e
->type
== ButtonRelease
||
1796 e
->type
== MotionNotify
)
1798 /* the frame may not be "visible" but they can still click on it
1799 in the case where it is animating before disappearing */
1800 if (!client
|| !frame_iconify_animating(client
->frame
))
1801 mouse_event(client
, e
);
1803 keyboard_event((focus_cycle_target
? focus_cycle_target
:
1804 (client
? client
: focus_client
)), e
);
1808 static void focus_delay_dest(gpointer data
)
1813 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
)
1815 const ObFocusDelayData
*f1
= d1
;
1816 return f1
->client
== d2
;
1819 static gboolean
focus_delay_func(gpointer data
)
1821 ObFocusDelayData
*d
= data
;
1822 Time old
= event_curtime
;
1824 event_curtime
= d
->time
;
1825 if (focus_client
!= d
->client
) {
1826 if (client_focus(d
->client
) && config_focus_raise
)
1827 stacking_raise(CLIENT_AS_WINDOW(d
->client
));
1829 event_curtime
= old
;
1830 return FALSE
; /* no repeat */
1833 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1835 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1839 void event_halt_focus_delay()
1841 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1844 gulong
event_start_ignore_all_enters()
1846 XSync(ob_display
, FALSE
);
1847 return LastKnownRequestProcessed(ob_display
);
1850 void event_end_ignore_all_enters(gulong start
)
1854 g_assert(start
!= 0);
1855 XSync(ob_display
, FALSE
);
1857 r
= g_new(ObSerialRange
, 1);
1859 r
->end
= LastKnownRequestProcessed(ob_display
);
1860 ignore_serials
= g_slist_prepend(ignore_serials
, r
);
1862 /* increment the serial so we don't ignore events we weren't meant to */
1863 XSync(ob_display
, FALSE
);
1866 static gboolean
is_enter_focus_event_ignored(XEvent
*e
)
1870 g_assert(e
->type
== EnterNotify
&&
1871 !(e
->xcrossing
.mode
== NotifyGrab
||
1872 e
->xcrossing
.mode
== NotifyUngrab
||
1873 e
->xcrossing
.detail
== NotifyInferior
));
1875 for (it
= ignore_serials
; it
; it
= next
) {
1876 ObSerialRange
*r
= it
->data
;
1878 next
= g_slist_next(it
);
1880 if ((glong
)(e
->xany
.serial
- r
->end
) > 0) {
1882 ignore_serials
= g_slist_delete_link(ignore_serials
, it
);
1885 else if ((glong
)(e
->xany
.serial
- r
->start
) >= 0)
1891 void event_cancel_all_key_grabs()
1893 if (actions_interactive_act_running()) {
1894 actions_interactive_cancel_act();
1895 ob_debug("KILLED interactive action\n");
1897 else if (menu_frame_visible
) {
1898 menu_frame_hide_all();
1899 ob_debug("KILLED open menus\n");
1901 else if (moveresize_in_progress
) {
1902 moveresize_end(TRUE
);
1903 ob_debug("KILLED interactive moveresize\n");
1905 else if (grab_on_keyboard()) {
1907 ob_debug("KILLED active grab on keyboard\n");
1910 ungrab_passive_key();
1913 gboolean
event_time_after(Time t1
, Time t2
)
1915 g_assert(t1
!= CurrentTime
);
1916 g_assert(t2
!= CurrentTime
);
1919 Timestamp values wrap around (after about 49.7 days). The server, given
1920 its current time is represented by timestamp T, always interprets
1921 timestamps from clients by treating half of the timestamp space as being
1922 later in time than T.
1923 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1926 /* TIME_HALF is half of the number space of a Time type variable */
1927 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1929 if (t2
>= TIME_HALF
)
1930 /* t2 is in the second half so t1 might wrap around and be smaller than
1932 return t1
>= t2
|| t1
< (t2
+ TIME_HALF
);
1934 /* t2 is in the first half so t1 has to come after it */
1935 return t1
>= t2
&& t1
< (t2
+ TIME_HALF
);