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
)
253 e
->xbutton
.state
= modkeys_only_modifier_masks(e
->xbutton
.state
);
256 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
259 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
260 /* remove from the state the mask of the modifier key being released,
261 if it is a modifier key being released that is */
262 e
->xkey
.state
&= ~modkeys_keycode_to_mask(e
->xkey
.keycode
);
265 e
->xmotion
.state
= modkeys_only_modifier_masks(e
->xmotion
.state
);
266 /* compress events */
269 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
271 e
->xmotion
.x
= ce
.xmotion
.x
;
272 e
->xmotion
.y
= ce
.xmotion
.y
;
273 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
274 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
281 static gboolean
wanted_focusevent(XEvent
*e
, gboolean in_client_only
)
283 gint mode
= e
->xfocus
.mode
;
284 gint detail
= e
->xfocus
.detail
;
285 Window win
= e
->xany
.window
;
287 if (e
->type
== FocusIn
) {
288 /* These are ones we never want.. */
290 /* This means focus was given by a keyboard/mouse grab. */
291 if (mode
== NotifyGrab
)
293 /* This means focus was given back from a keyboard/mouse grab. */
294 if (mode
== NotifyUngrab
)
297 /* These are the ones we want.. */
299 if (win
== RootWindow(ob_display
, ob_screen
)) {
300 /* If looking for a focus in on a client, then always return
301 FALSE for focus in's to the root window */
304 /* This means focus reverted off of a client */
305 else if (detail
== NotifyPointerRoot
||
306 detail
== NotifyDetailNone
||
307 detail
== NotifyInferior
||
308 /* This means focus got here from another screen */
309 detail
== NotifyNonlinear
)
315 /* It was on a client, was it a valid one?
316 It's possible to get a FocusIn event for a client that was managed
319 if (in_client_only
) {
320 ObWindow
*w
= g_hash_table_lookup(window_map
, &e
->xfocus
.window
);
321 if (!w
|| !WINDOW_IS_CLIENT(w
))
325 /* This means focus reverted to parent from the client (this
326 happens often during iconify animation) */
327 if (detail
== NotifyInferior
)
331 /* This means focus moved from the root window to a client */
332 if (detail
== NotifyVirtual
)
334 /* This means focus moved from one client to another */
335 if (detail
== NotifyNonlinearVirtual
)
341 g_assert(e
->type
== FocusOut
);
343 /* These are ones we never want.. */
345 /* This means focus was taken by a keyboard/mouse grab. */
346 if (mode
== NotifyGrab
)
348 /* This means focus was grabbed on a window and it was released. */
349 if (mode
== NotifyUngrab
)
352 /* Focus left the root window revertedto state */
353 if (win
== RootWindow(ob_display
, ob_screen
))
356 /* These are the ones we want.. */
358 /* This means focus moved from a client to the root window */
359 if (detail
== NotifyVirtual
)
361 /* This means focus moved from one client to another */
362 if (detail
== NotifyNonlinearVirtual
)
370 static Bool
event_look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
372 return e
->type
== FocusIn
&& wanted_focusevent(e
, FALSE
);
375 static Bool
event_look_for_focusin_client(Display
*d
, XEvent
*e
, XPointer arg
)
377 return e
->type
== FocusIn
&& wanted_focusevent(e
, TRUE
);
380 static void print_focusevent(XEvent
*e
)
382 gint mode
= e
->xfocus
.mode
;
383 gint detail
= e
->xfocus
.detail
;
384 Window win
= e
->xany
.window
;
385 const gchar
*modestr
, *detailstr
;
388 case NotifyNormal
: modestr
="NotifyNormal"; break;
389 case NotifyGrab
: modestr
="NotifyGrab"; break;
390 case NotifyUngrab
: modestr
="NotifyUngrab"; break;
391 case NotifyWhileGrabbed
: modestr
="NotifyWhileGrabbed"; break;
394 case NotifyAncestor
: detailstr
="NotifyAncestor"; break;
395 case NotifyVirtual
: detailstr
="NotifyVirtual"; break;
396 case NotifyInferior
: detailstr
="NotifyInferior"; break;
397 case NotifyNonlinear
: detailstr
="NotifyNonlinear"; break;
398 case NotifyNonlinearVirtual
: detailstr
="NotifyNonlinearVirtual"; break;
399 case NotifyPointer
: detailstr
="NotifyPointer"; break;
400 case NotifyPointerRoot
: detailstr
="NotifyPointerRoot"; break;
401 case NotifyDetailNone
: detailstr
="NotifyDetailNone"; break;
404 if (mode
== NotifyGrab
|| mode
== NotifyUngrab
)
409 ob_debug_type(OB_DEBUG_FOCUS
, "Focus%s 0x%x mode=%s detail=%s\n",
410 (e
->xfocus
.type
== FocusIn
? "In" : "Out"),
416 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
421 if (!wanted_focusevent(e
, FALSE
))
426 if (!wanted_focusevent(e
, FALSE
))
433 static void event_process(const XEvent
*ec
, gpointer data
)
436 ObClient
*client
= NULL
;
438 ObDockApp
*dockapp
= NULL
;
439 ObWindow
*obwin
= NULL
;
441 ObEventData
*ed
= data
;
443 /* make a copy we can mangle */
447 window
= event_get_window(e
);
448 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
449 switch (obwin
->type
) {
451 dock
= WINDOW_AS_DOCK(obwin
);
454 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
457 client
= WINDOW_AS_CLIENT(obwin
);
460 case Window_Internal
:
461 /* not to be used for events */
462 g_assert_not_reached();
467 event_set_curtime(e
);
469 if (event_ignore(e
, client
)) {
476 /* deal with it in the kernel */
478 if (menu_frame_visible
&&
479 (e
->type
== EnterNotify
|| e
->type
== LeaveNotify
))
481 /* crossing events for menu */
482 event_handle_menu(e
);
483 } else if (e
->type
== FocusIn
) {
485 e
->xfocus
.detail
== NotifyInferior
)
487 ob_debug_type(OB_DEBUG_FOCUS
,
488 "Focus went to the frame window");
490 focus_left_screen
= FALSE
;
492 focus_fallback(FALSE
, config_focus_under_mouse
, TRUE
, TRUE
);
494 /* We don't get a FocusOut for this case, because it's just moving
495 from our Inferior up to us. This happens when iconifying a
496 window with RevertToParent focus */
497 frame_adjust_focus(client
->frame
, FALSE
);
498 /* focus_set_client(NULL) has already been called */
499 client_calc_layer(client
);
501 else if (e
->xfocus
.detail
== NotifyPointerRoot
||
502 e
->xfocus
.detail
== NotifyDetailNone
||
503 e
->xfocus
.detail
== NotifyInferior
||
504 e
->xfocus
.detail
== NotifyNonlinear
)
508 ob_debug_type(OB_DEBUG_FOCUS
,
509 "Focus went to root or pointer root/none\n");
511 if (e
->xfocus
.detail
== NotifyInferior
||
512 e
->xfocus
.detail
== NotifyNonlinear
)
514 focus_left_screen
= FALSE
;
517 /* If another FocusIn is in the queue then don't fallback yet. This
518 fixes the fun case of:
519 window map -> send focusin
520 window unmap -> get focusout
521 window map -> send focusin
522 get first focus out -> fall back to something (new window
523 hasn't received focus yet, so something else) -> send focusin
524 which means the "something else" is the last thing to get a
525 focusin sent to it, so the new window doesn't end up with focus.
527 But if the other focus in is something like PointerRoot then we
528 still want to fall back.
530 if (XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin_client
,
533 XPutBackEvent(ob_display
, &ce
);
534 ob_debug_type(OB_DEBUG_FOCUS
,
535 " but another FocusIn is coming\n");
537 /* Focus has been reverted.
539 FocusOut events come after UnmapNotify, so we don't need to
540 worry about focusing an invalid window
543 if (!focus_left_screen
)
544 focus_fallback(FALSE
, config_focus_under_mouse
,
550 ob_debug_type(OB_DEBUG_FOCUS
,
551 "Focus went to a window that is already gone\n");
553 /* If you send focus to a window and then it disappears, you can
554 get the FocusIn for it, after it is unmanaged.
555 Just wait for the next FocusOut/FocusIn pair, but make note that
556 the window that was focused no longer is. */
557 focus_set_client(NULL
);
559 else if (client
!= focus_client
) {
560 focus_left_screen
= FALSE
;
561 frame_adjust_focus(client
->frame
, TRUE
);
562 focus_set_client(client
);
563 client_calc_layer(client
);
564 client_bring_helper_windows(client
);
566 } else if (e
->type
== FocusOut
) {
569 /* Look for the followup FocusIn */
570 if (!XCheckIfEvent(ob_display
, &ce
, event_look_for_focusin
, NULL
)) {
571 /* There is no FocusIn, this means focus went to a window that
572 is not being managed, or a window on another screen. */
576 xerror_set_ignore(TRUE
);
577 if (XGetInputFocus(ob_display
, &win
, &i
) != 0 &&
578 XGetGeometry(ob_display
, win
, &root
, &i
,&i
,&u
,&u
,&u
,&u
) != 0 &&
579 root
!= RootWindow(ob_display
, ob_screen
))
581 ob_debug_type(OB_DEBUG_FOCUS
,
582 "Focus went to another screen !\n");
583 focus_left_screen
= TRUE
;
586 ob_debug_type(OB_DEBUG_FOCUS
,
587 "Focus went to a black hole !\n");
588 xerror_set_ignore(FALSE
);
589 /* nothing is focused */
590 focus_set_client(NULL
);
592 /* Focus moved, so process the FocusIn event */
593 ObEventData ed
= { .ignored
= FALSE
};
594 event_process(&ce
, &ed
);
596 /* The FocusIn was ignored, this means it was on a window
597 that isn't a client. */
598 ob_debug_type(OB_DEBUG_FOCUS
,
599 "Focus went to an unmanaged window 0x%x !\n",
601 focus_fallback(TRUE
, config_focus_under_mouse
, TRUE
, TRUE
);
605 if (client
&& client
!= focus_client
) {
606 frame_adjust_focus(client
->frame
, FALSE
);
607 /* focus_set_client(NULL) has already been called in this
608 section or by focus_fallback */
609 client_calc_layer(client
);
613 event_handle_client(client
, e
);
615 event_handle_dockapp(dockapp
, e
);
617 event_handle_dock(dock
, e
);
618 else if (window
== RootWindow(ob_display
, ob_screen
))
619 event_handle_root(e
);
620 else if (e
->type
== MapRequest
)
621 client_manage(window
);
622 else if (e
->type
== ClientMessage
) {
623 /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
624 windows that are not managed yet. */
625 if (e
->xclient
.message_type
== prop_atoms
.net_request_frame_extents
) {
626 /* Pretend to manage the client, getting information used to
627 determine its decorations */
628 ObClient
*c
= client_fake_manage(e
->xclient
.window
);
631 /* set the frame extents on the window */
632 vals
[0] = c
->frame
->size
.left
;
633 vals
[1] = c
->frame
->size
.right
;
634 vals
[2] = c
->frame
->size
.top
;
635 vals
[3] = c
->frame
->size
.bottom
;
636 PROP_SETA32(e
->xclient
.window
, net_frame_extents
,
639 /* Free the pretend client */
640 client_fake_unmanage(c
);
643 else if (e
->type
== ConfigureRequest
) {
644 /* unhandled configure requests must be used to configure the
648 xwc
.x
= e
->xconfigurerequest
.x
;
649 xwc
.y
= e
->xconfigurerequest
.y
;
650 xwc
.width
= e
->xconfigurerequest
.width
;
651 xwc
.height
= e
->xconfigurerequest
.height
;
652 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
653 xwc
.sibling
= e
->xconfigurerequest
.above
;
654 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
656 /* we are not to be held responsible if someone sends us an
658 xerror_set_ignore(TRUE
);
659 XConfigureWindow(ob_display
, window
,
660 e
->xconfigurerequest
.value_mask
, &xwc
);
661 xerror_set_ignore(FALSE
);
664 else if (extensions_sync
&&
665 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
667 XSyncAlarmNotifyEvent
*se
= (XSyncAlarmNotifyEvent
*)e
;
668 if (se
->alarm
== moveresize_alarm
&& moveresize_in_progress
)
673 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
674 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
675 e
->type
== KeyRelease
)
677 event_handle_user_input(client
, e
);
680 /* if something happens and it's not from an XEvent, then we don't know
682 event_curtime
= CurrentTime
;
685 static void event_handle_root(XEvent
*e
)
691 ob_debug("Another WM has requested to replace us. Exiting.\n");
696 if (e
->xclient
.format
!= 32) break;
698 msgtype
= e
->xclient
.message_type
;
699 if (msgtype
== prop_atoms
.net_current_desktop
) {
700 guint d
= e
->xclient
.data
.l
[0];
701 if (d
< screen_num_desktops
) {
702 event_curtime
= e
->xclient
.data
.l
[1];
703 if (event_curtime
== 0)
704 ob_debug_type(OB_DEBUG_APP_BUGS
,
705 "_NET_CURRENT_DESKTOP message is missing "
707 screen_set_desktop(d
, TRUE
);
709 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
710 guint d
= e
->xclient
.data
.l
[0];
711 if (d
> 0 && d
<= 1000)
712 screen_set_num_desktops(d
);
713 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
714 screen_show_desktop(e
->xclient
.data
.l
[0] != 0, NULL
);
715 } else if (msgtype
== prop_atoms
.ob_control
) {
716 ob_debug("OB_CONTROL: %d\n", e
->xclient
.data
.l
[0]);
717 if (e
->xclient
.data
.l
[0] == 1)
719 else if (e
->xclient
.data
.l
[0] == 2)
721 else if (e
->xclient
.data
.l
[0] == 3)
726 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
) {
727 ob_debug("UPDATE DESKTOP NAMES\n");
728 screen_update_desktop_names();
730 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
731 screen_update_layout();
733 case ConfigureNotify
:
735 XRRUpdateConfiguration(e
);
744 void event_enter_client(ObClient
*client
)
746 g_assert(config_focus_follow
);
748 if (client_enter_focusable(client
) && client_can_focus(client
)) {
749 if (config_focus_delay
) {
750 ObFocusDelayData
*data
;
752 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
754 data
= g_new(ObFocusDelayData
, 1);
755 data
->client
= client
;
756 data
->time
= event_curtime
;
758 ob_main_loop_timeout_add(ob_main_loop
,
759 config_focus_delay
* 1000,
761 data
, focus_delay_cmp
, focus_delay_dest
);
763 ObFocusDelayData data
;
764 data
.client
= client
;
765 data
.time
= event_curtime
;
766 focus_delay_func(&data
);
771 static void event_handle_client(ObClient
*client
, XEvent
*e
)
776 static gint px
= -1, py
= -1;
781 /* save where the press occured for the first button pressed */
783 pb
= e
->xbutton
.button
;
788 /* Wheel buttons don't draw because they are an instant click, so it
789 is a waste of resources to go drawing it.
790 if the user is doing an intereactive thing, or has a menu open then
791 the mouse is grabbed (possibly) and if we get these events we don't
792 want to deal with them
794 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5) &&
797 /* use where the press occured */
798 con
= frame_context(client
, e
->xbutton
.window
, px
, py
);
799 con
= mouse_button_frame_context(con
, e
->xbutton
.button
,
802 if (e
->type
== ButtonRelease
&& e
->xbutton
.button
== pb
)
803 pb
= 0, px
= py
= -1;
806 case OB_FRAME_CONTEXT_MAXIMIZE
:
807 client
->frame
->max_press
= (e
->type
== ButtonPress
);
808 frame_adjust_state(client
->frame
);
810 case OB_FRAME_CONTEXT_CLOSE
:
811 client
->frame
->close_press
= (e
->type
== ButtonPress
);
812 frame_adjust_state(client
->frame
);
814 case OB_FRAME_CONTEXT_ICONIFY
:
815 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
816 frame_adjust_state(client
->frame
);
818 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
819 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
820 frame_adjust_state(client
->frame
);
822 case OB_FRAME_CONTEXT_SHADE
:
823 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
824 frame_adjust_state(client
->frame
);
827 /* nothing changes with clicks for any other contexts */
833 /* when there is a grab on the pointer, we won't get enter/leave
834 notifies, but we still get motion events */
835 if (grab_on_pointer()) break;
837 con
= frame_context(client
, e
->xmotion
.window
,
838 e
->xmotion
.x
, e
->xmotion
.y
);
840 case OB_FRAME_CONTEXT_TITLEBAR
:
841 case OB_FRAME_CONTEXT_TLCORNER
:
842 case OB_FRAME_CONTEXT_TRCORNER
:
843 /* we've left the button area inside the titlebar */
844 if (client
->frame
->max_hover
|| client
->frame
->desk_hover
||
845 client
->frame
->shade_hover
|| client
->frame
->iconify_hover
||
846 client
->frame
->close_hover
)
848 client
->frame
->max_hover
= FALSE
;
849 client
->frame
->desk_hover
= FALSE
;
850 client
->frame
->shade_hover
= FALSE
;
851 client
->frame
->iconify_hover
= FALSE
;
852 client
->frame
->close_hover
= FALSE
;
853 frame_adjust_state(client
->frame
);
856 case OB_FRAME_CONTEXT_MAXIMIZE
:
857 if (!client
->frame
->max_hover
) {
858 client
->frame
->max_hover
= TRUE
;
859 frame_adjust_state(client
->frame
);
862 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
863 if (!client
->frame
->desk_hover
) {
864 client
->frame
->desk_hover
= TRUE
;
865 frame_adjust_state(client
->frame
);
868 case OB_FRAME_CONTEXT_SHADE
:
869 if (!client
->frame
->shade_hover
) {
870 client
->frame
->shade_hover
= TRUE
;
871 frame_adjust_state(client
->frame
);
874 case OB_FRAME_CONTEXT_ICONIFY
:
875 if (!client
->frame
->iconify_hover
) {
876 client
->frame
->iconify_hover
= TRUE
;
877 frame_adjust_state(client
->frame
);
880 case OB_FRAME_CONTEXT_CLOSE
:
881 if (!client
->frame
->close_hover
) {
882 client
->frame
->close_hover
= TRUE
;
883 frame_adjust_state(client
->frame
);
891 con
= frame_context(client
, e
->xcrossing
.window
,
892 e
->xcrossing
.x
, e
->xcrossing
.y
);
894 case OB_FRAME_CONTEXT_TITLEBAR
:
895 case OB_FRAME_CONTEXT_TLCORNER
:
896 case OB_FRAME_CONTEXT_TRCORNER
:
897 /* we've left the button area inside the titlebar */
898 if (client
->frame
->max_hover
|| client
->frame
->desk_hover
||
899 client
->frame
->shade_hover
|| client
->frame
->iconify_hover
||
900 client
->frame
->close_hover
)
902 client
->frame
->max_hover
= FALSE
;
903 client
->frame
->desk_hover
= FALSE
;
904 client
->frame
->shade_hover
= FALSE
;
905 client
->frame
->iconify_hover
= FALSE
;
906 client
->frame
->close_hover
= FALSE
;
907 frame_adjust_state(client
->frame
);
910 case OB_FRAME_CONTEXT_MAXIMIZE
:
911 client
->frame
->max_hover
= FALSE
;
912 frame_adjust_state(client
->frame
);
914 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
915 client
->frame
->desk_hover
= FALSE
;
916 frame_adjust_state(client
->frame
);
918 case OB_FRAME_CONTEXT_SHADE
:
919 client
->frame
->shade_hover
= FALSE
;
920 frame_adjust_state(client
->frame
);
922 case OB_FRAME_CONTEXT_ICONIFY
:
923 client
->frame
->iconify_hover
= FALSE
;
924 frame_adjust_state(client
->frame
);
926 case OB_FRAME_CONTEXT_CLOSE
:
927 client
->frame
->close_hover
= FALSE
;
928 frame_adjust_state(client
->frame
);
930 case OB_FRAME_CONTEXT_FRAME
:
931 /* When the mouse leaves an animating window, don't use the
932 corresponding enter events. Pretend like the animating window
933 doesn't even exist..! */
934 if (frame_iconify_animating(client
->frame
))
935 event_end_ignore_all_enters(event_start_ignore_all_enters());
937 ob_debug_type(OB_DEBUG_FOCUS
,
938 "%sNotify mode %d detail %d on %lx\n",
939 (e
->type
== EnterNotify
? "Enter" : "Leave"),
941 e
->xcrossing
.detail
, (client
?client
->window
:0));
942 if (grab_on_keyboard())
944 if (config_focus_follow
&& config_focus_delay
&&
945 /* leave inferior events can happen when the mouse goes onto
946 the window's border and then into the window before the
948 e
->xcrossing
.detail
!= NotifyInferior
)
950 ob_main_loop_timeout_remove_data(ob_main_loop
,
961 con
= frame_context(client
, e
->xcrossing
.window
,
962 e
->xcrossing
.x
, e
->xcrossing
.y
);
964 case OB_FRAME_CONTEXT_MAXIMIZE
:
965 client
->frame
->max_hover
= TRUE
;
966 frame_adjust_state(client
->frame
);
968 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
969 client
->frame
->desk_hover
= TRUE
;
970 frame_adjust_state(client
->frame
);
972 case OB_FRAME_CONTEXT_SHADE
:
973 client
->frame
->shade_hover
= TRUE
;
974 frame_adjust_state(client
->frame
);
976 case OB_FRAME_CONTEXT_ICONIFY
:
977 client
->frame
->iconify_hover
= TRUE
;
978 frame_adjust_state(client
->frame
);
980 case OB_FRAME_CONTEXT_CLOSE
:
981 client
->frame
->close_hover
= TRUE
;
982 frame_adjust_state(client
->frame
);
984 case OB_FRAME_CONTEXT_FRAME
:
985 if (grab_on_keyboard())
987 if (e
->xcrossing
.mode
== NotifyGrab
||
988 e
->xcrossing
.mode
== NotifyUngrab
||
989 /*ignore enters when we're already in the window */
990 e
->xcrossing
.detail
== NotifyInferior
||
991 is_enter_focus_event_ignored(e
))
993 ob_debug_type(OB_DEBUG_FOCUS
,
994 "%sNotify mode %d detail %d on %lx IGNORED\n",
995 (e
->type
== EnterNotify
? "Enter" : "Leave"),
997 e
->xcrossing
.detail
, client
?client
->window
:0);
1000 ob_debug_type(OB_DEBUG_FOCUS
,
1001 "%sNotify mode %d detail %d on %lx, "
1002 "focusing window\n",
1003 (e
->type
== EnterNotify
? "Enter" : "Leave"),
1005 e
->xcrossing
.detail
, (client
?client
->window
:0));
1006 if (config_focus_follow
)
1007 event_enter_client(client
);
1015 case ConfigureRequest
:
1017 /* dont compress these unless you're going to watch for property
1018 notifies in between (these can change what the configure would
1020 also you can't compress stacking events
1024 gboolean move
= FALSE
;
1025 gboolean resize
= FALSE
;
1027 /* get the current area */
1028 RECT_TO_DIMS(client
->area
, x
, y
, w
, h
);
1030 ob_debug("ConfigureRequest for \"%s\" desktop %d wmstate %d "
1032 " x %d y %d w %d h %d b %d\n",
1034 screen_desktop
, client
->wmstate
, client
->frame
->visible
,
1035 x
, y
, w
, h
, client
->border_width
);
1037 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
1038 if (client
->border_width
!= e
->xconfigurerequest
.border_width
) {
1039 client
->border_width
= e
->xconfigurerequest
.border_width
;
1041 /* if the border width is changing then that is the same
1042 as requesting a resize, but we don't actually change
1043 the client's border, so it will change their root
1044 coordiantes (since they include the border width) and
1045 we need to a notify then */
1050 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
1051 ObClient
*sibling
= NULL
;
1052 gulong ignore_start
;
1055 /* get the sibling */
1056 if (e
->xconfigurerequest
.value_mask
& CWSibling
) {
1058 win
= g_hash_table_lookup(window_map
,
1059 &e
->xconfigurerequest
.above
);
1060 if (win
&& WINDOW_IS_CLIENT(win
) &&
1061 WINDOW_AS_CLIENT(win
) != client
)
1063 sibling
= WINDOW_AS_CLIENT(win
);
1066 /* an invalid sibling was specified so don't restack at
1067 all, it won't make sense no matter what we do */
1072 if (!config_focus_under_mouse
)
1073 ignore_start
= event_start_ignore_all_enters();
1074 stacking_restack_request(client
, sibling
,
1075 e
->xconfigurerequest
.detail
);
1076 if (!config_focus_under_mouse
)
1077 event_end_ignore_all_enters(ignore_start
);
1080 /* a stacking change moves the window without resizing */
1084 if ((e
->xconfigurerequest
.value_mask
& CWX
) ||
1085 (e
->xconfigurerequest
.value_mask
& CWY
) ||
1086 (e
->xconfigurerequest
.value_mask
& CWWidth
) ||
1087 (e
->xconfigurerequest
.value_mask
& CWHeight
))
1089 if (e
->xconfigurerequest
.value_mask
& CWX
) {
1090 /* don't allow clients to move shaded windows (fvwm does this)
1092 if (!client
->shaded
)
1093 x
= e
->xconfigurerequest
.x
;
1096 if (e
->xconfigurerequest
.value_mask
& CWY
) {
1097 /* don't allow clients to move shaded windows (fvwm does this)
1099 if (!client
->shaded
)
1100 y
= e
->xconfigurerequest
.y
;
1104 if (e
->xconfigurerequest
.value_mask
& CWWidth
) {
1105 w
= e
->xconfigurerequest
.width
;
1108 if (e
->xconfigurerequest
.value_mask
& CWHeight
) {
1109 h
= e
->xconfigurerequest
.height
;
1114 ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d "
1115 "move %d resize %d\n",
1116 e
->xconfigurerequest
.value_mask
& CWX
, x
,
1117 e
->xconfigurerequest
.value_mask
& CWY
, y
,
1118 e
->xconfigurerequest
.value_mask
& CWWidth
, w
,
1119 e
->xconfigurerequest
.value_mask
& CWHeight
, h
,
1122 /* check for broken apps moving to their root position
1124 XXX remove this some day...that would be nice. right now all
1125 kde apps do this when they try activate themselves on another
1126 desktop. eg. open amarok window on desktop 1, switch to desktop
1127 2, click amarok tray icon. it will move by its decoration size.
1129 if (x
!= client
->area
.x
&&
1130 x
== (client
->frame
->area
.x
+ client
->frame
->size
.left
-
1131 (gint
)client
->border_width
) &&
1132 y
!= client
->area
.y
&&
1133 y
== (client
->frame
->area
.y
+ client
->frame
->size
.top
-
1134 (gint
)client
->border_width
) &&
1135 w
== client
->area
.width
&&
1136 h
== client
->area
.height
)
1138 ob_debug_type(OB_DEBUG_APP_BUGS
,
1139 "Application %s is trying to move via "
1140 "ConfigureRequest to it's root window position "
1141 "but it is not using StaticGravity\n",
1147 /* they still requested a move, so don't change whether a
1148 notify is sent or not */
1154 client_try_configure(client
, &x
, &y
, &w
, &h
, &lw
, &lh
, FALSE
);
1156 /* if x was not given, then use gravity to figure out the new
1157 x. the reference point should not be moved */
1158 if ((e
->xconfigurerequest
.value_mask
& CWWidth
&&
1159 !(e
->xconfigurerequest
.value_mask
& CWX
)))
1160 client_gravity_resize_w(client
, &x
, client
->area
.width
, w
);
1161 /* if y was not given, then use gravity to figure out the new
1162 y. the reference point should not be moved */
1163 if ((e
->xconfigurerequest
.value_mask
& CWHeight
&&
1164 !(e
->xconfigurerequest
.value_mask
& CWY
)))
1165 client_gravity_resize_h(client
, &y
, client
->area
.height
,h
);
1167 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1169 ob_debug("Granting ConfigureRequest x %d y %d w %d h %d\n",
1171 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
, TRUE
);
1176 if (client
->ignore_unmaps
) {
1177 client
->ignore_unmaps
--;
1180 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
1181 "ignores left %d\n",
1182 client
->window
, e
->xunmap
.event
, e
->xunmap
.from_configure
,
1183 client
->ignore_unmaps
);
1184 client_unmanage(client
);
1187 ob_debug("DestroyNotify for window 0x%x\n", client
->window
);
1188 client_unmanage(client
);
1190 case ReparentNotify
:
1191 /* this is when the client is first taken captive in the frame */
1192 if (e
->xreparent
.parent
== client
->frame
->window
) break;
1195 This event is quite rare and is usually handled in unmapHandler.
1196 However, if the window is unmapped when the reparent event occurs,
1197 the window manager never sees it because an unmap event is not sent
1198 to an already unmapped window.
1201 /* we don't want the reparent event, put it back on the stack for the
1202 X server to deal with after we unmanage the window */
1203 XPutBackEvent(ob_display
, e
);
1205 ob_debug("ReparentNotify for window 0x%x\n", client
->window
);
1206 client_unmanage(client
);
1209 ob_debug("MapRequest for 0x%lx\n", client
->window
);
1210 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
1211 does, we don't want it!
1212 it can happen now when the window is on
1213 another desktop, but we still don't
1215 client_activate(client
, FALSE
, TRUE
, TRUE
, TRUE
);
1218 /* validate cuz we query stuff off the client here */
1219 if (!client_validate(client
)) break;
1221 if (e
->xclient
.format
!= 32) return;
1223 msgtype
= e
->xclient
.message_type
;
1224 if (msgtype
== prop_atoms
.wm_change_state
) {
1225 /* compress changes into a single change */
1226 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1228 /* XXX: it would be nice to compress ALL messages of a
1229 type, not just messages in a row without other
1230 message types between. */
1231 if (ce
.xclient
.message_type
!= msgtype
) {
1232 XPutBackEvent(ob_display
, &ce
);
1235 e
->xclient
= ce
.xclient
;
1237 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
1238 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
1239 /* compress changes into a single change */
1240 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1242 /* XXX: it would be nice to compress ALL messages of a
1243 type, not just messages in a row without other
1244 message types between. */
1245 if (ce
.xclient
.message_type
!= msgtype
) {
1246 XPutBackEvent(ob_display
, &ce
);
1249 e
->xclient
= ce
.xclient
;
1251 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
1252 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
1253 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
1255 } else if (msgtype
== prop_atoms
.net_wm_state
) {
1256 gulong ignore_start
;
1258 /* can't compress these */
1259 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1260 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
1261 e
->xclient
.data
.l
[0] == 1 ? "Add" :
1262 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1263 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
1266 /* ignore enter events caused by these like ob actions do */
1267 if (!config_focus_under_mouse
)
1268 ignore_start
= event_start_ignore_all_enters();
1269 client_set_state(client
, e
->xclient
.data
.l
[0],
1270 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
1271 if (!config_focus_under_mouse
)
1272 event_end_ignore_all_enters(ignore_start
);
1273 } else if (msgtype
== prop_atoms
.net_close_window
) {
1274 ob_debug("net_close_window for 0x%lx\n", client
->window
);
1275 client_close(client
);
1276 } else if (msgtype
== prop_atoms
.net_active_window
) {
1277 ob_debug("net_active_window for 0x%lx source=%s\n",
1279 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
1280 (e
->xclient
.data
.l
[0] == 1 ? "application" :
1281 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
1282 /* XXX make use of data.l[2] !? */
1283 if (e
->xclient
.data
.l
[0] == 1 || e
->xclient
.data
.l
[0] == 2) {
1284 /* don't use the user's timestamp for client_focus, cuz if it's
1285 an old broken timestamp (happens all the time) then focus
1286 won't move even though we're trying to move it
1287 event_curtime = e->xclient.data.l[1];*/
1288 if (e
->xclient
.data
.l
[1] == 0)
1289 ob_debug_type(OB_DEBUG_APP_BUGS
,
1290 "_NET_ACTIVE_WINDOW message for window %s is"
1291 " missing a timestamp\n", client
->title
);
1293 ob_debug_type(OB_DEBUG_APP_BUGS
,
1294 "_NET_ACTIVE_WINDOW message for window %s is "
1295 "missing source indication\n");
1296 client_activate(client
, FALSE
, TRUE
, TRUE
,
1297 (e
->xclient
.data
.l
[0] == 0 ||
1298 e
->xclient
.data
.l
[0] == 2));
1299 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
1300 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1301 client
->window
, e
->xclient
.data
.l
[2]);
1302 if ((Atom
)e
->xclient
.data
.l
[2] ==
1303 prop_atoms
.net_wm_moveresize_size_topleft
||
1304 (Atom
)e
->xclient
.data
.l
[2] ==
1305 prop_atoms
.net_wm_moveresize_size_top
||
1306 (Atom
)e
->xclient
.data
.l
[2] ==
1307 prop_atoms
.net_wm_moveresize_size_topright
||
1308 (Atom
)e
->xclient
.data
.l
[2] ==
1309 prop_atoms
.net_wm_moveresize_size_right
||
1310 (Atom
)e
->xclient
.data
.l
[2] ==
1311 prop_atoms
.net_wm_moveresize_size_right
||
1312 (Atom
)e
->xclient
.data
.l
[2] ==
1313 prop_atoms
.net_wm_moveresize_size_bottomright
||
1314 (Atom
)e
->xclient
.data
.l
[2] ==
1315 prop_atoms
.net_wm_moveresize_size_bottom
||
1316 (Atom
)e
->xclient
.data
.l
[2] ==
1317 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1318 (Atom
)e
->xclient
.data
.l
[2] ==
1319 prop_atoms
.net_wm_moveresize_size_left
||
1320 (Atom
)e
->xclient
.data
.l
[2] ==
1321 prop_atoms
.net_wm_moveresize_move
||
1322 (Atom
)e
->xclient
.data
.l
[2] ==
1323 prop_atoms
.net_wm_moveresize_size_keyboard
||
1324 (Atom
)e
->xclient
.data
.l
[2] ==
1325 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1327 moveresize_start(client
, e
->xclient
.data
.l
[0],
1328 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1329 e
->xclient
.data
.l
[2]);
1331 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1332 prop_atoms
.net_wm_moveresize_cancel
)
1333 moveresize_end(TRUE
);
1334 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1335 gint ograv
, x
, y
, w
, h
;
1337 ograv
= client
->gravity
;
1339 if (e
->xclient
.data
.l
[0] & 0xff)
1340 client
->gravity
= e
->xclient
.data
.l
[0] & 0xff;
1342 if (e
->xclient
.data
.l
[0] & 1 << 8)
1343 x
= e
->xclient
.data
.l
[1];
1346 if (e
->xclient
.data
.l
[0] & 1 << 9)
1347 y
= e
->xclient
.data
.l
[2];
1351 if (e
->xclient
.data
.l
[0] & 1 << 10) {
1352 w
= e
->xclient
.data
.l
[3];
1354 /* if x was not given, then use gravity to figure out the new
1355 x. the reference point should not be moved */
1356 if (!(e
->xclient
.data
.l
[0] & 1 << 8))
1357 client_gravity_resize_w(client
, &x
, client
->area
.width
, w
);
1360 w
= client
->area
.width
;
1362 if (e
->xclient
.data
.l
[0] & 1 << 11) {
1363 h
= e
->xclient
.data
.l
[4];
1365 /* if y was not given, then use gravity to figure out the new
1366 y. the reference point should not be moved */
1367 if (!(e
->xclient
.data
.l
[0] & 1 << 9))
1368 client_gravity_resize_h(client
, &y
, client
->area
.height
,h
);
1371 h
= client
->area
.height
;
1373 ob_debug("MOVERESIZE x %d %d y %d %d (gravity %d)\n",
1374 e
->xclient
.data
.l
[0] & 1 << 8, x
,
1375 e
->xclient
.data
.l
[0] & 1 << 9, y
,
1378 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1380 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
, FALSE
);
1382 client
->gravity
= ograv
;
1383 } else if (msgtype
== prop_atoms
.net_restack_window
) {
1384 if (e
->xclient
.data
.l
[0] != 2) {
1385 ob_debug_type(OB_DEBUG_APP_BUGS
,
1386 "_NET_RESTACK_WINDOW sent for window %s with "
1387 "invalid source indication %ld\n",
1388 client
->title
, e
->xclient
.data
.l
[0]);
1390 ObClient
*sibling
= NULL
;
1391 if (e
->xclient
.data
.l
[1]) {
1392 ObWindow
*win
= g_hash_table_lookup
1393 (window_map
, &e
->xclient
.data
.l
[1]);
1394 if (WINDOW_IS_CLIENT(win
) &&
1395 WINDOW_AS_CLIENT(win
) != client
)
1397 sibling
= WINDOW_AS_CLIENT(win
);
1399 if (sibling
== NULL
)
1400 ob_debug_type(OB_DEBUG_APP_BUGS
,
1401 "_NET_RESTACK_WINDOW sent for window %s "
1402 "with invalid sibling 0x%x\n",
1403 client
->title
, e
->xclient
.data
.l
[1]);
1405 if (e
->xclient
.data
.l
[2] == Below
||
1406 e
->xclient
.data
.l
[2] == BottomIf
||
1407 e
->xclient
.data
.l
[2] == Above
||
1408 e
->xclient
.data
.l
[2] == TopIf
||
1409 e
->xclient
.data
.l
[2] == Opposite
)
1411 gulong ignore_start
;
1413 if (!config_focus_under_mouse
)
1414 ignore_start
= event_start_ignore_all_enters();
1415 /* just raise, don't activate */
1416 stacking_restack_request(client
, sibling
,
1417 e
->xclient
.data
.l
[2]);
1418 if (!config_focus_under_mouse
)
1419 event_end_ignore_all_enters(ignore_start
);
1421 /* send a synthetic ConfigureNotify, cuz this is supposed
1422 to be like a ConfigureRequest. */
1423 client_reconfigure(client
, TRUE
);
1425 ob_debug_type(OB_DEBUG_APP_BUGS
,
1426 "_NET_RESTACK_WINDOW sent for window %s "
1427 "with invalid detail %d\n",
1428 client
->title
, e
->xclient
.data
.l
[2]);
1432 case PropertyNotify
:
1433 /* validate cuz we query stuff off the client here */
1434 if (!client_validate(client
)) break;
1436 /* compress changes to a single property into a single change */
1437 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1441 /* XXX: it would be nice to compress ALL changes to a property,
1442 not just changes in a row without other props between. */
1444 a
= ce
.xproperty
.atom
;
1445 b
= e
->xproperty
.atom
;
1449 if ((a
== prop_atoms
.net_wm_name
||
1450 a
== prop_atoms
.wm_name
||
1451 a
== prop_atoms
.net_wm_icon_name
||
1452 a
== prop_atoms
.wm_icon_name
)
1454 (b
== prop_atoms
.net_wm_name
||
1455 b
== prop_atoms
.wm_name
||
1456 b
== prop_atoms
.net_wm_icon_name
||
1457 b
== prop_atoms
.wm_icon_name
)) {
1460 if (a
== prop_atoms
.net_wm_icon
&&
1461 b
== prop_atoms
.net_wm_icon
)
1464 XPutBackEvent(ob_display
, &ce
);
1468 msgtype
= e
->xproperty
.atom
;
1469 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1470 ob_debug("Update NORMAL hints\n");
1471 client_update_normal_hints(client
);
1472 /* normal hints can make a window non-resizable */
1473 client_setup_decor_and_functions(client
, FALSE
);
1475 /* make sure the client's sizes are within its bounds, but only
1476 reconfigure the window if it needs to. emacs will update its
1477 normal hints every time it receives a conigurenotify */
1478 client_reconfigure(client
, FALSE
);
1479 } else if (msgtype
== XA_WM_HINTS
) {
1480 client_update_wmhints(client
);
1481 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1482 client_update_transient_for(client
);
1483 client_get_type_and_transientness(client
);
1484 /* type may have changed, so update the layer */
1485 client_calc_layer(client
);
1486 client_setup_decor_and_functions(client
, TRUE
);
1487 } else if (msgtype
== prop_atoms
.net_wm_name
||
1488 msgtype
== prop_atoms
.wm_name
||
1489 msgtype
== prop_atoms
.net_wm_icon_name
||
1490 msgtype
== prop_atoms
.wm_icon_name
) {
1491 client_update_title(client
);
1492 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1493 client_update_protocols(client
);
1494 client_setup_decor_and_functions(client
, TRUE
);
1496 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1497 client_update_strut(client
);
1499 else if (msgtype
== prop_atoms
.net_wm_strut_partial
) {
1500 client_update_strut(client
);
1502 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1503 client_update_icons(client
);
1505 else if (msgtype
== prop_atoms
.net_wm_icon_geometry
) {
1506 client_update_icon_geometry(client
);
1508 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1510 if (client
== focus_client
&&
1511 PROP_GET32(client
->window
, net_wm_user_time
, cardinal
, &t
) &&
1512 t
&& !event_time_after(t
, e
->xproperty
.time
) &&
1513 (!event_last_user_time
||
1514 event_time_after(t
, event_last_user_time
)))
1516 event_last_user_time
= t
;
1520 else if (msgtype
== prop_atoms
.net_wm_sync_request_counter
) {
1521 client_update_sync_request_counter(client
);
1525 case ColormapNotify
:
1526 client_update_colormap(client
, e
->xcolormap
.colormap
);
1531 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1532 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1533 frame_adjust_shape(client
->frame
);
1539 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1543 if (e
->xbutton
.button
== 1)
1544 stacking_raise(DOCK_AS_WINDOW(s
));
1545 else if (e
->xbutton
.button
== 2)
1546 stacking_lower(DOCK_AS_WINDOW(s
));
1552 /* don't hide when moving into a dock app */
1553 if (e
->xcrossing
.detail
!= NotifyInferior
)
1559 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1563 dock_app_drag(app
, &e
->xmotion
);
1566 if (app
->ignore_unmaps
) {
1567 app
->ignore_unmaps
--;
1570 dock_remove(app
, TRUE
);
1573 dock_remove(app
, FALSE
);
1575 case ReparentNotify
:
1576 dock_remove(app
, FALSE
);
1578 case ConfigureNotify
:
1579 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1584 static ObMenuFrame
* find_active_menu(void)
1587 ObMenuFrame
*ret
= NULL
;
1589 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1598 static ObMenuFrame
* find_active_or_last_menu(void)
1600 ObMenuFrame
*ret
= NULL
;
1602 ret
= find_active_menu();
1603 if (!ret
&& menu_frame_visible
)
1604 ret
= menu_frame_visible
->data
;
1608 static gboolean
event_handle_menu_keyboard(XEvent
*ev
)
1610 guint keycode
, state
;
1613 gboolean ret
= FALSE
;
1615 keycode
= ev
->xkey
.keycode
;
1616 state
= ev
->xkey
.state
;
1617 unikey
= translate_unichar(keycode
);
1619 frame
= find_active_or_last_menu();
1621 g_assert_not_reached(); /* there is no active menu */
1623 /* Allow control while going thru the menu */
1624 else if (ev
->type
== KeyPress
&& (state
& ~ControlMask
) == 0) {
1625 if (keycode
== ob_keycode(OB_KEY_ESCAPE
)) {
1626 menu_frame_hide_all();
1630 else if (keycode
== ob_keycode(OB_KEY_LEFT
)) {
1631 /* Left goes to the parent menu */
1632 menu_frame_select(frame
, NULL
, TRUE
);
1636 else if (keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1637 /* Right goes to the selected submenu */
1638 if (frame
->child
) menu_frame_select_next(frame
->child
);
1642 else if (keycode
== ob_keycode(OB_KEY_UP
)) {
1643 menu_frame_select_previous(frame
);
1647 else if (keycode
== ob_keycode(OB_KEY_DOWN
)) {
1648 menu_frame_select_next(frame
);
1653 /* Use KeyRelease events for running things so that the key release doesn't
1654 get sent to the focused application.
1656 Allow ControlMask only, and don't bother if the menu is empty */
1657 else if (ev
->type
== KeyRelease
&& (state
& ~ControlMask
) == 0 &&
1660 if (keycode
== ob_keycode(OB_KEY_RETURN
)) {
1661 /* Enter runs the active item or goes into the submenu.
1662 Control-Enter runs it without closing the menu. */
1664 menu_frame_select_next(frame
->child
);
1665 else if (frame
->selected
)
1666 menu_entry_frame_execute(frame
->selected
, state
);
1671 /* keyboard accelerator shortcuts. (if it was a valid key) */
1672 else if (unikey
!= 0) {
1675 ObMenuEntryFrame
*found
= NULL
;
1676 guint num_found
= 0;
1678 /* start after the selected one */
1679 start
= frame
->entries
;
1680 if (frame
->selected
) {
1681 for (it
= start
; frame
->selected
!= it
->data
;
1682 it
= g_list_next(it
))
1683 g_assert(it
!= NULL
); /* nothing was selected? */
1684 /* next with wraparound */
1685 start
= g_list_next(it
);
1686 if (start
== NULL
) start
= frame
->entries
;
1691 ObMenuEntryFrame
*e
= it
->data
;
1692 gunichar entrykey
= 0;
1694 if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
)
1695 entrykey
= e
->entry
->data
.normal
.shortcut
;
1696 else if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_SUBMENU
)
1697 entrykey
= e
->entry
->data
.submenu
.submenu
->shortcut
;
1699 if (unikey
== entrykey
) {
1700 if (found
== NULL
) found
= e
;
1704 /* next with wraparound */
1705 it
= g_list_next(it
);
1706 if (it
== NULL
) it
= frame
->entries
;
1707 } while (it
!= start
);
1710 if (found
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
&&
1713 menu_frame_select(frame
, found
, TRUE
);
1714 usleep(50000); /* highlight the item for a short bit so the
1715 user can see what happened */
1716 menu_entry_frame_execute(found
, state
);
1718 menu_frame_select(frame
, found
, TRUE
);
1720 menu_frame_select_next(frame
->child
);
1731 static gboolean
event_handle_menu(XEvent
*ev
)
1734 ObMenuEntryFrame
*e
;
1735 gboolean ret
= TRUE
;
1739 if (menu_hide_delay_reached() &&
1740 (ev
->xbutton
.button
< 4 || ev
->xbutton
.button
> 5))
1742 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1743 ev
->xbutton
.y_root
)))
1745 menu_frame_select(e
->frame
, e
, TRUE
);
1746 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1749 menu_frame_hide_all();
1753 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
))) {
1754 if (e
->ignore_enters
)
1756 else if (!(f
= find_active_menu()) ||
1758 f
->parent
== e
->frame
||
1759 f
->child
== e
->frame
)
1760 menu_frame_select(e
->frame
, e
, FALSE
);
1764 /*ignore leaves when we're already in the window */
1765 if (ev
->xcrossing
.detail
== NotifyInferior
)
1768 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
)) &&
1769 (f
= find_active_menu()) && f
->selected
== e
&&
1770 e
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1772 menu_frame_select(e
->frame
, NULL
, FALSE
);
1776 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1777 ev
->xmotion
.y_root
)))
1778 if (!(f
= find_active_menu()) ||
1780 f
->parent
== e
->frame
||
1781 f
->child
== e
->frame
)
1782 menu_frame_select(e
->frame
, e
, FALSE
);
1786 ret
= event_handle_menu_keyboard(ev
);
1792 static void event_handle_user_input(ObClient
*client
, XEvent
*e
)
1794 g_assert(e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
1795 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
1796 e
->type
== KeyRelease
);
1798 if (menu_frame_visible
) {
1799 if (event_handle_menu(e
))
1800 /* don't use the event if the menu used it, but if the menu
1801 didn't use it and it's a keypress that is bound, it will
1802 close the menu and be used */
1806 /* if the keyboard interactive action uses the event then dont
1807 use it for bindings. likewise is moveresize uses the event. */
1808 if (!actions_interactive_input_event(e
) && !moveresize_event(e
)) {
1809 if (moveresize_in_progress
)
1810 /* make further actions work on the client being
1812 client
= moveresize_client
;
1814 if (e
->type
== ButtonPress
||
1815 e
->type
== ButtonRelease
||
1816 e
->type
== MotionNotify
)
1818 /* the frame may not be "visible" but they can still click on it
1819 in the case where it is animating before disappearing */
1820 if (!client
|| !frame_iconify_animating(client
->frame
))
1821 mouse_event(client
, e
);
1823 keyboard_event((focus_cycle_target
? focus_cycle_target
:
1824 (client
? client
: focus_client
)), e
);
1828 static void focus_delay_dest(gpointer data
)
1833 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
)
1835 const ObFocusDelayData
*f1
= d1
;
1836 return f1
->client
== d2
;
1839 static gboolean
focus_delay_func(gpointer data
)
1841 ObFocusDelayData
*d
= data
;
1842 Time old
= event_curtime
;
1844 /* don't move focus and kill the menu or the move/resize */
1845 if (menu_frame_visible
|| moveresize_in_progress
) return FALSE
;
1847 event_curtime
= d
->time
;
1848 if (focus_client
!= d
->client
) {
1849 if (client_focus(d
->client
) && config_focus_raise
)
1850 stacking_raise(CLIENT_AS_WINDOW(d
->client
));
1852 event_curtime
= old
;
1853 return FALSE
; /* no repeat */
1856 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1858 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1862 void event_halt_focus_delay(void)
1864 /* ignore all enter events up till now */
1865 event_end_ignore_all_enters(1);
1866 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1869 gulong
event_start_ignore_all_enters(void)
1871 XSync(ob_display
, FALSE
);
1872 return LastKnownRequestProcessed(ob_display
);
1875 void event_end_ignore_all_enters(gulong start
)
1879 g_assert(start
!= 0);
1880 XSync(ob_display
, FALSE
);
1882 r
= g_new(ObSerialRange
, 1);
1884 r
->end
= LastKnownRequestProcessed(ob_display
);
1885 ignore_serials
= g_slist_prepend(ignore_serials
, r
);
1887 /* increment the serial so we don't ignore events we weren't meant to */
1888 XSync(ob_display
, FALSE
);
1891 static gboolean
is_enter_focus_event_ignored(XEvent
*e
)
1895 g_assert(e
->type
== EnterNotify
&&
1896 !(e
->xcrossing
.mode
== NotifyGrab
||
1897 e
->xcrossing
.mode
== NotifyUngrab
||
1898 e
->xcrossing
.detail
== NotifyInferior
));
1900 for (it
= ignore_serials
; it
; it
= next
) {
1901 ObSerialRange
*r
= it
->data
;
1903 next
= g_slist_next(it
);
1905 if ((glong
)(e
->xany
.serial
- r
->end
) > 0) {
1907 ignore_serials
= g_slist_delete_link(ignore_serials
, it
);
1910 else if ((glong
)(e
->xany
.serial
- r
->start
) >= 0)
1916 void event_cancel_all_key_grabs(void)
1918 if (actions_interactive_act_running()) {
1919 actions_interactive_cancel_act();
1920 ob_debug("KILLED interactive action\n");
1922 else if (menu_frame_visible
) {
1923 menu_frame_hide_all();
1924 ob_debug("KILLED open menus\n");
1926 else if (moveresize_in_progress
) {
1927 moveresize_end(TRUE
);
1928 ob_debug("KILLED interactive moveresize\n");
1930 else if (grab_on_keyboard()) {
1932 ob_debug("KILLED active grab on keyboard\n");
1935 ungrab_passive_key();
1938 gboolean
event_time_after(Time t1
, Time t2
)
1940 g_assert(t1
!= CurrentTime
);
1941 g_assert(t2
!= CurrentTime
);
1944 Timestamp values wrap around (after about 49.7 days). The server, given
1945 its current time is represented by timestamp T, always interprets
1946 timestamps from clients by treating half of the timestamp space as being
1947 later in time than T.
1948 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1951 /* TIME_HALF is half of the number space of a Time type variable */
1952 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1954 if (t2
>= TIME_HALF
)
1955 /* t2 is in the second half so t1 might wrap around and be smaller than
1957 return t1
>= t2
|| t1
< (t2
+ TIME_HALF
);
1959 /* t2 is in the first half so t1 has to come after it */
1960 return t1
>= t2
&& t1
< (t2
+ TIME_HALF
);
1963 Time
event_get_server_time(void)
1965 /* Generate a timestamp */
1968 XChangeProperty(ob_display
, screen_support_win
,
1969 prop_atoms
.wm_class
, prop_atoms
.string
,
1970 8, PropModeAppend
, NULL
, 0);
1971 XWindowEvent(ob_display
, screen_support_win
, PropertyChangeMask
, &event
);
1972 return event
.xproperty
.time
;