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>
* src/common.h:

View File

@ -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);
@ -1327,6 +1329,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)
{
case KeyPress:

View File

@ -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

View File

@ -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

View File

@ -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,7 +124,7 @@ 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,
meta_window_set_stack_position_no_sync (window,
stack->n_positions - 1);
window->stack_position = -1;
stack->n_positions -= 1;
@ -162,7 +164,7 @@ void
meta_stack_raise (MetaStack *stack,
MetaWindow *window)
{
meta_window_set_stack_position (window,
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,7 +1467,7 @@ meta_stack_windows_cmp (MetaStack *stack,
}
void
meta_window_set_stack_position (MetaWindow *window,
meta_window_set_stack_position_no_sync (MetaWindow *window,
int position)
{
int low, high, delta;
@ -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);
}

View File

@ -141,7 +141,3 @@ void meta_window_set_stack_position (MetaWindow *window,
int position);
#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
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;

View File

@ -47,6 +47,14 @@
#include <X11/extensions/shape.h>
#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))
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,6 +1671,7 @@ 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",
@ -1625,6 +1679,12 @@ meta_window_show (MetaWindow *window)
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);
}
}

View File

@ -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;
@ -260,6 +269,10 @@ struct _MetaWindow
*/
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
* the actual server-side x,y so are relative to the frame
@ -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