From 28a54c6bb4f5e4157cec8e6a69736ceef07c3099 Mon Sep 17 00:00:00 2001 From: Elijah Newren Date: Thu, 24 Jun 2004 15:47:05 +0000 Subject: [PATCH] Add support for _NET_WM_USER_TIME 2004-06-17 Elijah Newren Add support for _NET_WM_USER_TIME * src/display.c: (meta_display_open): Add _NET_WM_USER_TIME to atom_names[], (event_callback): Manually set _NET_WM_USER_TIME upon KeyPress (doesn't work since keyboard isn't grabbed) and ButtonPress (does work), this is just a fallback for applications that don't update this themselves. * src/display.h: (struct _MetaDisplay): Add atom_net_wm_user_time field * src/screen.c: (meta_screen_apply_startup_properties): Check for TIMESTAMP provided from startup sequence as well. * src/stack.c: s/meta_window_set_stack_position/meta_window_set_stack_position_no_sync/, (meta_window_set_stack_position): New function which calls the meta_window_set_stack_position_no_sync function followed immediately by calling meta_stack_sync_to_server. * src/window-props.c: (init_net_wm_user_time), (reload_net_wm_user_time): new functions, (reload_wm_hints): also load atom_net_wm_user_time * src/window.c: new XSERVER_TIME_IS_LATER macro (accounts for timestamp wraparound), (meta_window_new_with_attrs): add timestamp attributes, (window_takes_focus_on_map): use TIMESTAMP from startup notification and _NET_WM_USER_TIME to decide whether to focus new windows, (meta_window_show): if app doesn't take focus on map, place it just below the focused window in the stack (process_property_notify): check for changes to _NET_WM_USRE_TIME, (meta_window_stack_just_below): new function * src/window.h: (_MetaWindow struct): new fields for initial_timestamp, initial_timestamp_set, net_wm_user_time_set, and net_wm_user_time, (meta_window_stack_just_below): new function --- ChangeLog | 42 ++++++++++++ src/display.c | 14 +++- src/display.h | 1 + src/screen.c | 12 ++++ src/stack.c | 26 +++++--- src/stack.h | 4 -- src/window-props.c | 32 ++++++++- src/window.c | 157 +++++++++++++++++++++++++++++++++++---------- src/window.h | 20 ++++-- 9 files changed, 257 insertions(+), 51 deletions(-) diff --git a/ChangeLog b/ChangeLog index 17346fee1..cf0ddafc1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,45 @@ +2004-06-17 Elijah Newren + + Add support for _NET_WM_USER_TIME + + * src/display.c: + (meta_display_open): Add _NET_WM_USER_TIME to atom_names[], + (event_callback): Manually set _NET_WM_USER_TIME upon KeyPress + (doesn't work since keyboard isn't grabbed) and ButtonPress (does + work), this is just a fallback for applications that don't update + this themselves. + + * src/display.h: (struct _MetaDisplay): Add atom_net_wm_user_time field + + * src/screen.c: (meta_screen_apply_startup_properties): Check for + TIMESTAMP provided from startup sequence as well. + + * src/stack.c: + s/meta_window_set_stack_position/meta_window_set_stack_position_no_sync/, + (meta_window_set_stack_position): New function which calls the + meta_window_set_stack_position_no_sync function followed immediately + by calling meta_stack_sync_to_server. + + * src/window-props.c: + (init_net_wm_user_time), (reload_net_wm_user_time): new functions, + (reload_wm_hints): also load atom_net_wm_user_time + + * src/window.c: + new XSERVER_TIME_IS_LATER macro (accounts for timestamp wraparound), + (meta_window_new_with_attrs): add timestamp attributes, + (window_takes_focus_on_map): use TIMESTAMP from startup + notification and _NET_WM_USER_TIME to decide whether to focus new + windows, + (meta_window_show): if app doesn't take focus on map, place it + just below the focused window in the stack + (process_property_notify): check for changes to _NET_WM_USRE_TIME, + (meta_window_stack_just_below): new function + + * src/window.h: + (_MetaWindow struct): new fields for initial_timestamp, + initial_timestamp_set, net_wm_user_time_set, and net_wm_user_time, + (meta_window_stack_just_below): new function + 2004-06-21 Anders Carlsson * src/common.h: diff --git a/src/display.c b/src/display.c index 50ff95834..fa574bfeb 100644 --- a/src/display.c +++ b/src/display.c @@ -281,6 +281,7 @@ meta_display_open (const char *name) "_NET_WM_ACTION_MINIMIZE", "_NET_FRAME_EXTENTS", "_NET_REQUEST_FRAME_EXTENTS", + "_NET_WM_USER_TIME", }; Atom atoms[G_N_ELEMENTS(atom_names)]; @@ -427,6 +428,7 @@ meta_display_open (const char *name) display->atom_net_wm_action_minimize = atoms[82]; display->atom_net_frame_extents = atoms[83]; display->atom_net_request_frame_extents = atoms[84]; + display->atom_net_wm_user_time = atoms[85]; display->prop_hooks = NULL; meta_display_init_window_prop_hooks (display); @@ -1177,7 +1179,7 @@ event_callback (XEvent *event, Window modified; gboolean frame_was_receiver; gboolean filter_out_event; - + display = data; if (dump_events) @@ -1326,6 +1328,16 @@ event_callback (XEvent *event, } } #endif /* HAVE_SHAPE */ + + if (window && ((event->type == KeyPress) || (event->type == ButtonPress))) + { + g_assert (CurrentTime != display->current_time); + meta_topic (META_DEBUG_WINDOW_STATE, + "Metacity set %s's net_wm_user_time to %d.\n", + window->desc, display->current_time); + window->net_wm_user_time_set = TRUE; + window->net_wm_user_time = display->current_time; + } switch (event->type) { diff --git a/src/display.h b/src/display.h index b49dcc1fd..08255fe1d 100644 --- a/src/display.h +++ b/src/display.h @@ -175,6 +175,7 @@ struct _MetaDisplay Atom atom_net_wm_strut_partial; Atom atom_net_frame_extents; Atom atom_net_request_frame_extents; + Atom atom_net_wm_user_time; /* This is the actual window from focus events, * not the one we last set diff --git a/src/screen.c b/src/screen.c index 293734f52..bfc86895f 100644 --- a/src/screen.c +++ b/src/screen.c @@ -2481,6 +2481,7 @@ meta_screen_apply_startup_properties (MetaScreen *screen, if (sequence != NULL) { int space; + Time timestamp; meta_topic (META_DEBUG_STARTUP, "Found startup sequence for window %s ID \"%s\"\n", @@ -2500,6 +2501,17 @@ meta_screen_apply_startup_properties (MetaScreen *screen, } } + if (!window->initial_timestamp_set) + { + timestamp = sn_startup_sequence_get_timestamp (sequence); + meta_topic (META_DEBUG_STARTUP, + "Setting initial window timestamp to %lu based on startup info\n", + timestamp); + + window->initial_timestamp_set = TRUE; + window->initial_timestamp = timestamp; + } + return; } else diff --git a/src/stack.c b/src/stack.c index 50f37c344..eacc9ae08 100644 --- a/src/stack.c +++ b/src/stack.c @@ -47,6 +47,8 @@ #define WINDOW_IN_STACK(w) (w->stack_position >= 0) static void meta_stack_sync_to_server (MetaStack *stack); +static void meta_window_set_stack_position_no_sync (MetaWindow *window, + int position); MetaStack* meta_stack_new (MetaScreen *screen) @@ -122,8 +124,8 @@ meta_stack_remove (MetaStack *stack, /* Set window to top position, so removing it will not leave gaps * in the set of positions */ - meta_window_set_stack_position (window, - stack->n_positions - 1); + meta_window_set_stack_position_no_sync (window, + stack->n_positions - 1); window->stack_position = -1; stack->n_positions -= 1; @@ -162,8 +164,8 @@ void meta_stack_raise (MetaStack *stack, MetaWindow *window) { - meta_window_set_stack_position (window, - stack->n_positions - 1); + meta_window_set_stack_position_no_sync (window, + stack->n_positions - 1); meta_stack_sync_to_server (stack); } @@ -172,7 +174,7 @@ void meta_stack_lower (MetaStack *stack, MetaWindow *window) { - meta_window_set_stack_position (window, 0); + meta_window_set_stack_position_no_sync (window, 0); meta_stack_sync_to_server (stack); } @@ -669,7 +671,7 @@ ensure_above (MetaWindow *above, if (above->stack_position < below->stack_position) { /* move above to below->stack_position bumping below down the stack */ - meta_window_set_stack_position (above, below->stack_position); + meta_window_set_stack_position_no_sync (above, below->stack_position); g_assert (below->stack_position + 1 == above->stack_position); } meta_topic (META_DEBUG_STACK, "%s above at %d > %s below at %d\n", @@ -1465,8 +1467,8 @@ meta_stack_windows_cmp (MetaStack *stack, } void -meta_window_set_stack_position (MetaWindow *window, - int position) +meta_window_set_stack_position_no_sync (MetaWindow *window, + int position) { int low, high, delta; GList *tmp; @@ -1517,3 +1519,11 @@ meta_window_set_stack_position (MetaWindow *window, "Window %s had stack_position set to %d\n", window->desc, window->stack_position); } + +void +meta_window_set_stack_position (MetaWindow *window, + int position) +{ + meta_window_set_stack_position_no_sync (window, position); + meta_stack_sync_to_server (window->screen->stack); +} diff --git a/src/stack.h b/src/stack.h index b0c21e564..0b8d05e2c 100644 --- a/src/stack.h +++ b/src/stack.h @@ -141,7 +141,3 @@ void meta_window_set_stack_position (MetaWindow *window, int position); #endif - - - - diff --git a/src/window-props.c b/src/window-props.c index 6f9b4b675..2eed8b2e8 100644 --- a/src/window-props.c +++ b/src/window-props.c @@ -170,6 +170,31 @@ reload_net_wm_pid (MetaWindow *window, } } +static void +init_net_wm_user_time (MetaDisplay *display, + Atom property, + MetaPropValue *value) +{ + value->type = META_PROP_VALUE_CARDINAL; + value->atom = display->atom_net_wm_user_time; +} + +static void +reload_net_wm_user_time (MetaWindow *window, + MetaPropValue *value) +{ + if (value->type != META_PROP_VALUE_INVALID) + { + gulong cardinal = value->v.cardinal; + + window->net_wm_user_time_set = TRUE; + window->net_wm_user_time = cardinal; + meta_topic (META_DEBUG_STARTUP, + "Window %s has _NET_WM_USER_TIME of %lu\n", + window->desc, window->net_wm_user_time); + } +} + static void set_window_title (MetaWindow *window, const char *title) @@ -814,7 +839,7 @@ reload_wm_hints (MetaWindow *window, -#define N_HOOKS 23 +#define N_HOOKS 24 void meta_display_init_window_prop_hooks (MetaDisplay *display) @@ -844,6 +869,11 @@ meta_display_init_window_prop_hooks (MetaDisplay *display) hooks[i].reload_func = reload_net_wm_pid; ++i; + hooks[i].property = display->atom_net_wm_user_time; + hooks[i].init_func = init_net_wm_user_time; + hooks[i].reload_func = reload_net_wm_user_time; + ++i; + hooks[i].property = display->atom_net_wm_name; hooks[i].init_func = init_net_wm_name; hooks[i].reload_func = reload_net_wm_name; diff --git a/src/window.c b/src/window.c index addfd2fa5..aa3c4d0e7 100644 --- a/src/window.c +++ b/src/window.c @@ -47,6 +47,14 @@ #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, @@ -216,7 +224,7 @@ meta_window_new_with_attrs (MetaDisplay *display, MetaWorkspace *space; gulong existing_wm_state; gulong event_mask; -#define N_INITIAL_PROPS 12 +#define N_INITIAL_PROPS 13 Atom initial_props[N_INITIAL_PROPS]; int i; gboolean has_shape; @@ -446,6 +454,8 @@ meta_window_new_with_attrs (MetaDisplay *display, window->all_keys_grabbed = FALSE; window->withdrawn = FALSE; window->initial_workspace_set = FALSE; + window->initial_timestamp_set = FALSE; + window->net_wm_user_time_set = FALSE; window->calc_placement = FALSE; window->shaken_loose = FALSE; window->have_focus_click_grab = FALSE; @@ -509,6 +519,7 @@ meta_window_new_with_attrs (MetaDisplay *display, window->layer = META_LAYER_LAST; /* invalid value */ window->stack_position = -1; window->initial_workspace = 0; /* not used */ + window->initial_timestamp = 0; /* not used */ meta_display_register_x_window (display, &window->xwindow, window); @@ -536,6 +547,7 @@ meta_window_new_with_attrs (MetaDisplay *display, initial_props[i++] = XA_WM_NORMAL_HINTS; initial_props[i++] = display->atom_wm_protocols; initial_props[i++] = XA_WM_HINTS; + initial_props[i++] = display->atom_net_wm_user_time; g_assert (N_INITIAL_PROPS == i); meta_window_reload_properties (window, initial_props, N_INITIAL_PROPS); @@ -1555,6 +1567,8 @@ meta_window_queue_calc_showing (MetaWindow *window) static gboolean window_takes_focus_on_map (MetaWindow *window) { + Time compare; + /* don't initially focus windows that are intended to not accept * focus */ @@ -1572,41 +1586,80 @@ window_takes_focus_on_map (MetaWindow *window) /* don't focus these */ break; case META_WINDOW_NORMAL: - - /* Always focus new windows */ - return TRUE; - - /* Old Windows-XP style rule for reference */ - /* Focus only if the current focus is on a desktop element - * or nonexistent. - * - * (using display->focus_window is a bit of a race condition, - * but I have no idea how to avoid it) - */ - if (window->display->focus_window == NULL || - (window->display->focus_window && - (window->display->focus_window->type == META_WINDOW_DOCK || - window->display->focus_window->type == META_WINDOW_DESKTOP))) - return TRUE; - break; case META_WINDOW_DIALOG: case META_WINDOW_MODAL_DIALOG: - /* Always focus */ - return TRUE; + meta_topic (META_DEBUG_STARTUP, + "COMPARISON:\n" + " net_wm_user_time_set : %d\n" + " net_wm_user_time : %lu\n" + " initial_timestamp_set: %d\n" + " initial_timestamp : %lu\n", + window->net_wm_user_time_set, + window->net_wm_user_time, + window->initial_timestamp_set, + window->initial_timestamp); + if (window->display->focus_window != NULL) { + meta_topic (META_DEBUG_STARTUP, + "COMPARISON (continued):\n" + " focus_window : %s\n" + " fw->net_wm_user_time : %lu\n", + window->display->focus_window->desc, + window->display->focus_window->net_wm_user_time); + } - /* Old Windows-XP style rule for reference */ - /* Focus only if the transient parent has focus */ - /* (using display->focus_window is a bit of a race condition, - * but I have no idea how to avoid it) + /* We expect the most common case for not focusing a new window + * to be when a hint to not focus it has been set. Since we can + * deal with that case rapidly, we use special case it--this is + * merely a preliminary optimization. :) */ - if (window->display->focus_window == NULL || - (window->display->focus_window && - meta_window_is_ancestor_of_transient (window->display->focus_window, - window)) || - (window->display->focus_window->type == META_WINDOW_DOCK || - window->display->focus_window->type == META_WINDOW_DESKTOP)) - return TRUE; + if ( ((window->net_wm_user_time_set == TRUE) && + (window->net_wm_user_time == 0)) + || + ((window->initial_timestamp_set == TRUE) && + (window->initial_timestamp == 0))) + { + meta_topic (META_DEBUG_STARTUP, + "window %s explicitly requested no focus\n", + window->desc); + return FALSE; + } + + if (!(window->net_wm_user_time_set) && !(window->initial_timestamp_set)) + { + meta_topic (META_DEBUG_STARTUP, + "no information about window %s found\n", + window->desc); + return TRUE; + } + + /* To determine the "launch" time of an application, + * startup-notification can set the TIMESTAMP and the + * application (usually via its toolkit such as gtk or qt) can + * set the _NET_WM_USER_TIME. If both are set, then it means + * the user has interacted with the application since it + * launched, and _NET_WM_USER_TIME is the value that should be + * used in the comparison. + */ + compare = window->initial_timestamp_set ? window->initial_timestamp : 0; + 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))) + { + meta_topic (META_DEBUG_STARTUP, + "new window %s with no intervening events\n", + window->desc); + return TRUE; + } + else + { + meta_topic (META_DEBUG_STARTUP, + "window %s focus prevented by other activity; %lu is before %lu\n", + window->desc, compare, window->display->focus_window->net_wm_user_time); + return FALSE; + } + break; } @@ -1618,13 +1671,20 @@ meta_window_show (MetaWindow *window) { gboolean did_placement; gboolean did_show; - + gboolean takes_focus_on_map; + meta_topic (META_DEBUG_WINDOW_STATE, "Showing window %s, shaded: %d iconic: %d placed: %d\n", window->desc, window->shaded, window->iconic, window->placed); did_show = FALSE; did_placement = FALSE; + takes_focus_on_map = window_takes_focus_on_map (window); + + if ( (!takes_focus_on_map) && (window->display->focus_window != NULL) ) + meta_window_stack_just_below (window, + window->display->focus_window); + if (!window->placed) { /* We have to recalc the placement here since other windows may @@ -1721,7 +1781,7 @@ meta_window_show (MetaWindow *window) } } - if (window_takes_focus_on_map (window)) + if (takes_focus_on_map) { meta_window_focus (window, meta_display_get_current_time (window->display)); @@ -4418,6 +4478,13 @@ process_property_notify (MetaWindow *window, meta_window_reload_property (window, window->display->atom_net_wm_sync_request_counter); } + else if (event->atom == window->display->atom_net_wm_user_time) + { + meta_verbose ("Property notify on %s for _NET_WM_USER_TIME\n", window->desc); + + meta_window_reload_property (window, + window->display->atom_net_wm_user_time); + } return TRUE; } @@ -7056,3 +7123,27 @@ meta_window_update_layer (MetaWindow *window) meta_stack_update_layer (window->screen->stack, window); meta_stack_thaw (window->screen->stack); } + +void +meta_window_stack_just_below (MetaWindow *window, + MetaWindow *below_this_one) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (below_this_one != NULL); + + if (window->stack_position > below_this_one->stack_position) + { + meta_topic (META_DEBUG_STACK, + "Setting stack position of window %s to %d (making it below window %s).\n", + window->desc, + below_this_one->stack_position - 1, + below_this_one->desc); + meta_window_set_stack_position (window, below_this_one->stack_position - 1); + } + else + { + meta_topic (META_DEBUG_STACK, + "Window %s was already below window %s.\n", + window->desc, below_this_one->desc); + } +} diff --git a/src/window.h b/src/window.h index 534b2524c..ca7948416 100644 --- a/src/window.h +++ b/src/window.h @@ -103,6 +103,9 @@ struct _MetaWindow /* Initial workspace property */ int initial_workspace; + /* Initial timestamp property */ + Time initial_timestamp; + /* Whether we're maximized */ guint maximized : 1; guint maximize_after_placement : 1; @@ -139,6 +142,12 @@ struct _MetaWindow /* whether an initial workspace was explicitly set */ guint initial_workspace_set : 1; + /* whether an initial timestamp was explicitly set */ + guint initial_timestamp_set : 1; + + /* whether net_wm_user_time has been set yet */ + guint net_wm_user_time_set : 1; + /* These are the flags from WM_PROTOCOLS */ guint take_focus : 1; guint delete_window : 1; @@ -259,6 +268,10 @@ struct _MetaWindow * is withdrawing the window. */ int unmaps_pending; + + /* set to the most recent user-interaction event timestamp that we + know about for this window */ + Time net_wm_user_time; /* The size we set the window to last (i.e. what we believe * to be its actual size on the server). The x, y are @@ -501,8 +514,7 @@ void meta_window_recalc_features (MetaWindow *window); void meta_window_queue_update_icon (MetaWindow *window); +void meta_window_stack_just_below (MetaWindow *window, + MetaWindow *below_this_one); + #endif - - - -