Add support for _NET_WM_USER_TIME

2004-06-17  Elijah Newren  <newren@math.utah.edu>

	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
This commit is contained in:
Elijah Newren 2004-06-24 15:47:05 +00:00 committed by Elijah Newren
parent ef1ecc8128
commit 28a54c6bb4
9 changed files with 257 additions and 51 deletions

View File

@ -1,3 +1,45 @@
2004-06-17 Elijah Newren <newren@math.utah.edu>
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 <andersca@gnome.org> 2004-06-21 Anders Carlsson <andersca@gnome.org>
* src/common.h: * src/common.h:

View File

@ -281,6 +281,7 @@ meta_display_open (const char *name)
"_NET_WM_ACTION_MINIMIZE", "_NET_WM_ACTION_MINIMIZE",
"_NET_FRAME_EXTENTS", "_NET_FRAME_EXTENTS",
"_NET_REQUEST_FRAME_EXTENTS", "_NET_REQUEST_FRAME_EXTENTS",
"_NET_WM_USER_TIME",
}; };
Atom atoms[G_N_ELEMENTS(atom_names)]; 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_wm_action_minimize = atoms[82];
display->atom_net_frame_extents = atoms[83]; display->atom_net_frame_extents = atoms[83];
display->atom_net_request_frame_extents = atoms[84]; display->atom_net_request_frame_extents = atoms[84];
display->atom_net_wm_user_time = atoms[85];
display->prop_hooks = NULL; display->prop_hooks = NULL;
meta_display_init_window_prop_hooks (display); meta_display_init_window_prop_hooks (display);
@ -1177,7 +1179,7 @@ event_callback (XEvent *event,
Window modified; Window modified;
gboolean frame_was_receiver; gboolean frame_was_receiver;
gboolean filter_out_event; gboolean filter_out_event;
display = data; display = data;
if (dump_events) if (dump_events)
@ -1326,6 +1328,16 @@ event_callback (XEvent *event,
} }
} }
#endif /* HAVE_SHAPE */ #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) switch (event->type)
{ {

View File

@ -175,6 +175,7 @@ struct _MetaDisplay
Atom atom_net_wm_strut_partial; Atom atom_net_wm_strut_partial;
Atom atom_net_frame_extents; Atom atom_net_frame_extents;
Atom atom_net_request_frame_extents; Atom atom_net_request_frame_extents;
Atom atom_net_wm_user_time;
/* This is the actual window from focus events, /* This is the actual window from focus events,
* not the one we last set * not the one we last set

View File

@ -2481,6 +2481,7 @@ meta_screen_apply_startup_properties (MetaScreen *screen,
if (sequence != NULL) if (sequence != NULL)
{ {
int space; int space;
Time timestamp;
meta_topic (META_DEBUG_STARTUP, meta_topic (META_DEBUG_STARTUP,
"Found startup sequence for window %s ID \"%s\"\n", "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; return;
} }
else else

View File

@ -47,6 +47,8 @@
#define WINDOW_IN_STACK(w) (w->stack_position >= 0) #define WINDOW_IN_STACK(w) (w->stack_position >= 0)
static void meta_stack_sync_to_server (MetaStack *stack); static void meta_stack_sync_to_server (MetaStack *stack);
static void meta_window_set_stack_position_no_sync (MetaWindow *window,
int position);
MetaStack* MetaStack*
meta_stack_new (MetaScreen *screen) 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 /* Set window to top position, so removing it will not leave gaps
* in the set of positions * in the set of positions
*/ */
meta_window_set_stack_position (window, meta_window_set_stack_position_no_sync (window,
stack->n_positions - 1); stack->n_positions - 1);
window->stack_position = -1; window->stack_position = -1;
stack->n_positions -= 1; stack->n_positions -= 1;
@ -162,8 +164,8 @@ void
meta_stack_raise (MetaStack *stack, meta_stack_raise (MetaStack *stack,
MetaWindow *window) MetaWindow *window)
{ {
meta_window_set_stack_position (window, meta_window_set_stack_position_no_sync (window,
stack->n_positions - 1); stack->n_positions - 1);
meta_stack_sync_to_server (stack); meta_stack_sync_to_server (stack);
} }
@ -172,7 +174,7 @@ void
meta_stack_lower (MetaStack *stack, meta_stack_lower (MetaStack *stack,
MetaWindow *window) MetaWindow *window)
{ {
meta_window_set_stack_position (window, 0); meta_window_set_stack_position_no_sync (window, 0);
meta_stack_sync_to_server (stack); meta_stack_sync_to_server (stack);
} }
@ -669,7 +671,7 @@ ensure_above (MetaWindow *above,
if (above->stack_position < below->stack_position) if (above->stack_position < below->stack_position)
{ {
/* move above to below->stack_position bumping below down the stack */ /* 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); g_assert (below->stack_position + 1 == above->stack_position);
} }
meta_topic (META_DEBUG_STACK, "%s above at %d > %s below at %d\n", 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 void
meta_window_set_stack_position (MetaWindow *window, meta_window_set_stack_position_no_sync (MetaWindow *window,
int position) int position)
{ {
int low, high, delta; int low, high, delta;
GList *tmp; GList *tmp;
@ -1517,3 +1519,11 @@ meta_window_set_stack_position (MetaWindow *window,
"Window %s had stack_position set to %d\n", "Window %s had stack_position set to %d\n",
window->desc, window->stack_position); 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);
}

View File

@ -141,7 +141,3 @@ void meta_window_set_stack_position (MetaWindow *window,
int position); int position);
#endif #endif

View File

@ -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 static void
set_window_title (MetaWindow *window, set_window_title (MetaWindow *window,
const char *title) const char *title)
@ -814,7 +839,7 @@ reload_wm_hints (MetaWindow *window,
#define N_HOOKS 23 #define N_HOOKS 24
void void
meta_display_init_window_prop_hooks (MetaDisplay *display) 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; hooks[i].reload_func = reload_net_wm_pid;
++i; ++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].property = display->atom_net_wm_name;
hooks[i].init_func = init_net_wm_name; hooks[i].init_func = init_net_wm_name;
hooks[i].reload_func = reload_net_wm_name; hooks[i].reload_func = reload_net_wm_name;

View File

@ -47,6 +47,14 @@
#include <X11/extensions/shape.h> #include <X11/extensions/shape.h>
#endif #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 typedef enum
{ {
META_IS_CONFIGURE_REQUEST = 1 << 0, META_IS_CONFIGURE_REQUEST = 1 << 0,
@ -216,7 +224,7 @@ meta_window_new_with_attrs (MetaDisplay *display,
MetaWorkspace *space; MetaWorkspace *space;
gulong existing_wm_state; gulong existing_wm_state;
gulong event_mask; gulong event_mask;
#define N_INITIAL_PROPS 12 #define N_INITIAL_PROPS 13
Atom initial_props[N_INITIAL_PROPS]; Atom initial_props[N_INITIAL_PROPS];
int i; int i;
gboolean has_shape; gboolean has_shape;
@ -446,6 +454,8 @@ meta_window_new_with_attrs (MetaDisplay *display,
window->all_keys_grabbed = FALSE; window->all_keys_grabbed = FALSE;
window->withdrawn = FALSE; window->withdrawn = FALSE;
window->initial_workspace_set = FALSE; window->initial_workspace_set = FALSE;
window->initial_timestamp_set = FALSE;
window->net_wm_user_time_set = FALSE;
window->calc_placement = FALSE; window->calc_placement = FALSE;
window->shaken_loose = FALSE; window->shaken_loose = FALSE;
window->have_focus_click_grab = 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->layer = META_LAYER_LAST; /* invalid value */
window->stack_position = -1; window->stack_position = -1;
window->initial_workspace = 0; /* not used */ window->initial_workspace = 0; /* not used */
window->initial_timestamp = 0; /* not used */
meta_display_register_x_window (display, &window->xwindow, window); 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++] = XA_WM_NORMAL_HINTS;
initial_props[i++] = display->atom_wm_protocols; initial_props[i++] = display->atom_wm_protocols;
initial_props[i++] = XA_WM_HINTS; initial_props[i++] = XA_WM_HINTS;
initial_props[i++] = display->atom_net_wm_user_time;
g_assert (N_INITIAL_PROPS == i); g_assert (N_INITIAL_PROPS == i);
meta_window_reload_properties (window, initial_props, N_INITIAL_PROPS); meta_window_reload_properties (window, initial_props, N_INITIAL_PROPS);
@ -1555,6 +1567,8 @@ meta_window_queue_calc_showing (MetaWindow *window)
static gboolean static gboolean
window_takes_focus_on_map (MetaWindow *window) window_takes_focus_on_map (MetaWindow *window)
{ {
Time compare;
/* don't initially focus windows that are intended to not accept /* don't initially focus windows that are intended to not accept
* focus * focus
*/ */
@ -1572,41 +1586,80 @@ window_takes_focus_on_map (MetaWindow *window)
/* don't focus these */ /* don't focus these */
break; break;
case META_WINDOW_NORMAL: 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_DIALOG:
case META_WINDOW_MODAL_DIALOG: case META_WINDOW_MODAL_DIALOG:
/* Always focus */ meta_topic (META_DEBUG_STARTUP,
return TRUE; "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 */ /* We expect the most common case for not focusing a new window
/* Focus only if the transient parent has focus */ * to be when a hint to not focus it has been set. Since we can
/* (using display->focus_window is a bit of a race condition, * deal with that case rapidly, we use special case it--this is
* but I have no idea how to avoid it) * merely a preliminary optimization. :)
*/ */
if (window->display->focus_window == NULL || if ( ((window->net_wm_user_time_set == TRUE) &&
(window->display->focus_window && (window->net_wm_user_time == 0))
meta_window_is_ancestor_of_transient (window->display->focus_window, ||
window)) || ((window->initial_timestamp_set == TRUE) &&
(window->display->focus_window->type == META_WINDOW_DOCK || (window->initial_timestamp == 0)))
window->display->focus_window->type == META_WINDOW_DESKTOP)) {
return TRUE; 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; break;
} }
@ -1618,13 +1671,20 @@ meta_window_show (MetaWindow *window)
{ {
gboolean did_placement; gboolean did_placement;
gboolean did_show; gboolean did_show;
gboolean takes_focus_on_map;
meta_topic (META_DEBUG_WINDOW_STATE, meta_topic (META_DEBUG_WINDOW_STATE,
"Showing window %s, shaded: %d iconic: %d placed: %d\n", "Showing window %s, shaded: %d iconic: %d placed: %d\n",
window->desc, window->shaded, window->iconic, window->placed); window->desc, window->shaded, window->iconic, window->placed);
did_show = FALSE; did_show = FALSE;
did_placement = 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) if (!window->placed)
{ {
/* We have to recalc the placement here since other windows may /* 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_window_focus (window,
meta_display_get_current_time (window->display)); meta_display_get_current_time (window->display));
@ -4418,6 +4478,13 @@ process_property_notify (MetaWindow *window,
meta_window_reload_property (window, meta_window_reload_property (window,
window->display->atom_net_wm_sync_request_counter); 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; return TRUE;
} }
@ -7056,3 +7123,27 @@ meta_window_update_layer (MetaWindow *window)
meta_stack_update_layer (window->screen->stack, window); meta_stack_update_layer (window->screen->stack, window);
meta_stack_thaw (window->screen->stack); 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);
}
}

View File

@ -103,6 +103,9 @@ struct _MetaWindow
/* Initial workspace property */ /* Initial workspace property */
int initial_workspace; int initial_workspace;
/* Initial timestamp property */
Time initial_timestamp;
/* Whether we're maximized */ /* Whether we're maximized */
guint maximized : 1; guint maximized : 1;
guint maximize_after_placement : 1; guint maximize_after_placement : 1;
@ -139,6 +142,12 @@ struct _MetaWindow
/* whether an initial workspace was explicitly set */ /* whether an initial workspace was explicitly set */
guint initial_workspace_set : 1; 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 */ /* These are the flags from WM_PROTOCOLS */
guint take_focus : 1; guint take_focus : 1;
guint delete_window : 1; guint delete_window : 1;
@ -259,6 +268,10 @@ struct _MetaWindow
* is withdrawing the window. * is withdrawing the window.
*/ */
int unmaps_pending; 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 /* 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 * 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_queue_update_icon (MetaWindow *window);
void meta_window_stack_just_below (MetaWindow *window,
MetaWindow *below_this_one);
#endif #endif