From 892cb8a8dd6bb480dfcdc0e5495baaca383ef5d9 Mon Sep 17 00:00:00 2001 From: Elijah Newren Date: Thu, 23 Dec 2004 06:44:56 +0000 Subject: [PATCH] Wrap XSetInputFocus, making display->expected_focus_window a little more 2004-12-22 Elijah Newren Wrap XSetInputFocus, making display->expected_focus_window a little more reliable (see #154598) * src/display.h: (struct _MetaDisplay): add a large comment about the expected_focus_window, add a last_focus_time field, (XSERVER_TIME_IS_BEFORE): new macro moved from window.c but fixed for 64-bit systems, (meta_display_set_input_focus_window): new function * src/display.c (meta_display_open): initialize last_focus_time, add a comment about brokenness of trying to set intial focus window, (meta_display_set_input_focus_window): new function that wraps XSetInputFocus, (meta_display_focus_the_no_focus_window): make this function closer to a wrapping of XSetInputFocus for the no_focus_window. * src/window.c (XSERVER_TIME_IS_LATER): remove this macro in favor of the improved one added to display.h * src/display.c (meta_display_open): * src/window.c (meta_window_focus): use meta_display_focus_the_no_focus_window and meta_display_set_input_focus instead of XSetInputFocus --- ChangeLog | 26 ++++++++++++++++++++++++++ src/display.c | 44 +++++++++++++++++++++++++++++++++++++++----- src/display.h | 39 ++++++++++++++++++++++++++++++++++++++- src/window.c | 34 ++++++++++------------------------ 4 files changed, 113 insertions(+), 30 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5e9f06686..776063934 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +2004-12-22 Elijah Newren + + Wrap XSetInputFocus, making display->expected_focus_window a + little more reliable (see #154598) + + * src/display.h: (struct _MetaDisplay): add a large comment about + the expected_focus_window, add a last_focus_time field, + (XSERVER_TIME_IS_BEFORE): new macro moved from window.c but fixed + for 64-bit systems, (meta_display_set_input_focus_window): new + function + + * src/display.c (meta_display_open): initialize last_focus_time, + add a comment about brokenness of trying to set intial focus + window, (meta_display_set_input_focus_window): new function that + wraps XSetInputFocus, + (meta_display_focus_the_no_focus_window): make this function + closer to a wrapping of XSetInputFocus for the no_focus_window. + + * src/window.c (XSERVER_TIME_IS_LATER): remove this macro in favor + of the improved one added to display.h + + * src/display.c (meta_display_open): + * src/window.c (meta_window_focus): + use meta_display_focus_the_no_focus_window and + meta_display_set_input_focus instead of XSetInputFocus + 2004-12-22 Elijah Newren * src/core.c (meta_core_user_lower_and_unfocus): diff --git a/src/display.c b/src/display.c index e07359513..c17d82b52 100644 --- a/src/display.c +++ b/src/display.c @@ -632,6 +632,7 @@ meta_display_open (const char *name) timestamp = event.xproperty.time; } + display->last_focus_time = timestamp; display->compositor = meta_compositor_new (display); screens = NULL; @@ -676,20 +677,28 @@ meta_display_open (const char *name) /* kinda bogus because GetInputFocus has no possible errors */ meta_error_trap_push (display); + /* FIXME: This is totally broken; see comment 9 of bug 88194 about this */ focus = None; ret_to = RevertToPointerRoot; XGetInputFocus (display->xdisplay, &focus, &ret_to); /* Force a new FocusIn (does this work?) */ - if (focus == None || focus == PointerRoot) - focus = display->no_focus_window; /* Use the same timestamp that was passed to meta_screen_new(), * as it is the most recent timestamp. */ - XSetInputFocus (display->xdisplay, focus, RevertToPointerRoot, - timestamp); - + if (focus == None || focus == PointerRoot) + meta_display_focus_the_no_focus_window (display, timestamp); + else + { + MetaWindow * window; + window = meta_display_lookup_x_window (display, focus); + if (window) + meta_display_set_input_focus_window (display, window, FALSE, timestamp); + else + meta_display_focus_the_no_focus_window (display, timestamp); + } + meta_error_trap_pop (display, FALSE); } @@ -4596,15 +4605,40 @@ meta_display_focus_sentinel_clear (MetaDisplay *display) return (display->sentinel_counter == 0); } +void +meta_display_set_input_focus_window (MetaDisplay *display, + MetaWindow *window, + gboolean focus_frame, + Time timestamp) +{ + if (XSERVER_TIME_IS_BEFORE (timestamp, display->last_focus_time)) + return; + + XSetInputFocus (display->xdisplay, + focus_frame ? window->frame->xwindow : window->xwindow, + RevertToPointerRoot, + timestamp); + display->expected_focus_window = window; + display->last_focus_time = timestamp; + + if (window != display->autoraise_window) + meta_display_remove_autoraise_callback (window->display); +} + void meta_display_focus_the_no_focus_window (MetaDisplay *display, Time timestamp) { + if (XSERVER_TIME_IS_BEFORE (timestamp, display->last_focus_time)) + return; + XSetInputFocus (display->xdisplay, display->no_focus_window, RevertToPointerRoot, timestamp); display->expected_focus_window = NULL; + display->last_focus_time = timestamp; + meta_display_remove_autoraise_callback (display); } diff --git a/src/display.h b/src/display.h index ff40938de..a3452d5fe 100644 --- a/src/display.h +++ b/src/display.h @@ -192,10 +192,18 @@ struct _MetaDisplay */ MetaWindow *previously_focused_window; - /* window we are expecting a FocusIn event for + /* window we are expecting a FocusIn event for or the current focus + * window if we are not expecting any FocusIn/FocusOut events; not + * perfect because applications can call XSetInputFocus directly. + * (It could also be messed up if a timestamp later than current + * time is sent to meta_display_set_input_focus_window, though that + * would be a programming error). See bug 154598 for more info. */ MetaWindow *expected_focus_window; + /* last timestamp that a window was focused */ + Time last_focus_time; + guint static_gravity_works : 1; /*< private-ish >*/ @@ -350,6 +358,18 @@ struct _MetaDisplay #endif }; +/* Xserver time can wraparound, thus comparing two timestamps needs to take + * this into account. Here's a little macro to help out. If no wraparound + * has occurred, this is equivalent to + * time1 < time2 + * Of course, the rest of the ugliness of this macro comes from accounting + * for the fact that wraparound can occur. + */ +#define XSERVER_TIME_IS_BEFORE(time1, time2) \ + ( (( time1 < time2 ) && ( time2 - time1 < ((guint32)-1)/2 )) || \ + (( time1 > time2 ) && ( time1 - time2 > ((guint32)-1)/2 )) \ + ) + gboolean meta_display_open (const char *name); void meta_display_close (MetaDisplay *display); MetaScreen* meta_display_screen_for_root (MetaDisplay *display, @@ -486,8 +506,25 @@ void meta_display_increment_focus_sentinel (MetaDisplay *display); void meta_display_decrement_focus_sentinel (MetaDisplay *display); gboolean meta_display_focus_sentinel_clear (MetaDisplay *display); +/* meta_display_set_input_focus_window is like XSetInputFocus, except + * that (a) it can't detect timestamps later than the current time, + * since Metacity isn't part of the XServer, and thus gives erroneous + * behavior in this circumstance (so don't do it), and (b) it uses + * display->last_focus_time and display->expected_focus_window since + * we don't have access to the true Xserver ones. + */ +void meta_display_set_input_focus_window (MetaDisplay *display, + MetaWindow *window, + gboolean focus_frame, + Time timestamp); + +/* meta_display_focus_the_no_focus_window is called when the + * designated no_focus_window should be focused, but is otherwise the + * same as meta_display_set_input_focus_window + */ void meta_display_focus_the_no_focus_window (MetaDisplay *display, Time timestamp); + void meta_display_queue_autoraise_callback (MetaDisplay *display, MetaWindow *window); void meta_display_remove_autoraise_callback (MetaDisplay *display); diff --git a/src/window.c b/src/window.c index 97a13ee05..ccc2a5539 100644 --- a/src/window.c +++ b/src/window.c @@ -47,14 +47,6 @@ #include #endif -/* Xserver time can wraparound, thus comparing two timestamps needs to take - * this into account. Here's a little macro to help out. - */ -#define XSERVER_TIME_IS_LATER(time1, time2) \ - ( ((time1 >= time2) && (time1 - time2 < G_MAXULONG / 2)) || \ - ((time1 < time2) && (time2 - time1 > G_MAXULONG / 2)) \ - ) - typedef enum { META_IS_CONFIGURE_REQUEST = 1 << 0, @@ -1633,7 +1625,7 @@ window_takes_focus_on_map (MetaWindow *window) compare = window->net_wm_user_time_set ? window->net_wm_user_time : compare; if ((window->display->focus_window == NULL) || - (XSERVER_TIME_IS_LATER (compare, window->display->focus_window->net_wm_user_time))) + XSERVER_TIME_IS_BEFORE (window->display->focus_window->net_wm_user_time, compare)) { meta_topic (META_DEBUG_STARTUP, "new window %s with no intervening events\n", @@ -3284,11 +3276,10 @@ meta_window_focus (MetaWindow *window, { meta_topic (META_DEBUG_FOCUS, "Focusing frame of %s\n", window->desc); - XSetInputFocus (window->display->xdisplay, - window->frame->xwindow, - RevertToPointerRoot, - timestamp); - window->display->expected_focus_window = window; + meta_display_set_input_focus_window (window->display, + window, + TRUE, + timestamp); } } else @@ -3298,13 +3289,12 @@ meta_window_focus (MetaWindow *window, if (window->input) { meta_topic (META_DEBUG_FOCUS, - "Calling XSetInputFocus() on client window %s since input = true\n", + "Setting input focus on %s since input = true\n", window->desc); - XSetInputFocus (window->display->xdisplay, - window->xwindow, - RevertToPointerRoot, - timestamp); - window->display->expected_focus_window = window; + meta_display_set_input_focus_window (window->display, + window, + FALSE, + timestamp); } if (window->take_focus) @@ -3326,10 +3316,6 @@ meta_window_focus (MetaWindow *window, window->wm_state_demands_attention = FALSE; set_net_wm_state (window); } - - /* Check if there's an autoraise timeout for a different window */ - if (window != window->display->autoraise_window) - meta_display_remove_autoraise_callback (window->display); } static void