Compare commits

...

28 Commits

Author SHA1 Message Date
Marco Trevisan (Treviño)
079a625eaa monitor-config-manager: Fallback to closed laptop lid configuration
When closing the lid of a laptop, we reconfigure all the monitors in order
to update the CRTCs and (if enabled) the global UI scaling factor.

To do this, we try first to reuse the current configuration for the usable
monitors, but if we have only monitor enabled and this one is on the laptop
lid we just end up creating a new configuration where the primary monitor is
the laptop one (as per find_primary_monitor() in MetaMonitorConfigManager),
but ignoring the user parameters.

In case the user selected a different resolution / scaling compared to the
default one, while the laptop lid is closed we might change the monitors
layout, causing applications to rescale or reposition.

To avoid this, when creating the monitors configuration from the current
current state, in case we have only one monitor available and that one is
the laptop panel, let's just reuse this configuration.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1200

(cherry-picked from commit e48516679c)
2020-05-05 22:19:27 +02:00
Florian Müllner
f3420b0341 window: Really propagate effective on-all-workspaces setting to transients
Commit cda9579034 fixed a corner case when setting the initial workspace
state of transient windows, but it still missed a case:

should_be_on_all_workspaces() returns whether the window should be on all
workspaces according to its properties/placement, but it doesn't take
transient relations into account.

That means in case of nested transients, we can still fail the assert:

 1. on-all-workspaces toplevel
 2. should_be_on_all_workspaces() is TRUE for the first transient's parent,
    as the window from (1) has on_all_workspaces_requested == TRUE
 3. should_be_on_all_workspaces() is FALSE for the second transient's
    parent, as the window from (2) is only on-all-workspace because
    of its parent

We can fix this by either using the state from the root ancestor
instead of the direct transient parent, or by using the parent's
on_all_workspaces_state.

The latter is simpler, so go with that.

https://gitlab.gnome.org/GNOME/mutter/issues/1083
2020-05-05 22:19:27 +02:00
Carlos Garnacho
dde0906639 core: propagate the effective on-all-workspaces setting to transient window
Commit 09bab98b1e tried to avoid several workspace changes while in
window construction, but it missed a case:

If we have a window on a secondary monitor with no workspaces enabled
(so it implicitly gets on_all_workspaces = TRUE without requesting it)
and trigger the creation of a second window that has the first as
transient-for, it would first try to set the first workspace than the
transient-for window and then fallback to all/current workspace.

After that commit we only try to set the same workspace than the
transient-for window, but it gets none as neither is on a single workspace,
nor did really request to be on all workspaces.

Fixes crashes when opening transient X11 dialogs in the secondary monitor.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/714
2020-05-05 22:19:27 +02:00
Carlos Garnacho
a611dadc0b core: Avoid consecutive workspace changes in window construction
We first set the workspace to the transient-for parent's, and then
try to set on the current workspace. If both happen, we double the
work on adding/removing it from the workspace, and everything that
happens in result.

Should reduce some activity while typing on the Epiphany address
bar, as the animation results in a number of xdg_popup being created
and destroyed to handle the animation.

https://gitlab.gnome.org/GNOME/mutter/issues/556
2020-05-05 22:19:27 +02:00
Jonas Ådahl
88e855bf0a x11: Limit touch replay pointer events to when replaying
When a touch sequence was rejected, the emulated pointer events would be
replayed with old timestamps. This caused issues with grabs as they
would be ignored due to being too old. This was mitigated by making sure
device event timestamps never travelled back in time by tampering with
any event that had a timestamp seemingly in the past.

This failed when the most recent timestamp that had been received were
much older than the timestamp of the new event. This could for example
happen when a session was left not interacted with for 40+ days or so;
when interacted with again, as any new timestamp would according to
XSERVER_TIME_IS_BEFORE() still be in the past compared to the "most
recent" one. The effect is that we'd always use the `latest_evtime` for
all new device events without ever updating it.

The end result of this was that passive grabs would become active when
interacted with, but would then newer be released, as the timestamps to
XIAllowEvents() would out of date, resulting in the desktop effectively
freezing, as the Shell would have an active pointer grab.

To avoid the situation where we get stuck with an old `latest_evtime`
timestamp, limit the tampering with device event timestamp to 1) only
pointer events, and 2) only during the replay sequence. The second part
is implemented by sending an asynchronous message via the X server after
rejecting a touch sequence, only potentially tampering with the device
event timestamps until the reply. This should avoid the stuck timestamp
as in those situations, we'll always have a relatively up to date
`latest_evtime` meaning XSERVER_TIME_IS_BEFORE() will not get confused.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/886
2020-04-24 21:14:40 +02:00
Jonas Ådahl
f205dabc5e display: Move finishing of touch sequence to the backend
We need to manipulate an X11 grab when a touch sequence ends; move that
logic to where it belongs - in the X11 backend.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/886
2020-04-24 21:14:40 +02:00
Marco Trevisan (Treviño)
74e3126b77 window-x11: Focus a window in the active workspace as take-focus fallback
Starting with commit 2db94e2e we try to focus a fallback default focus window
if no take-focus window candidate gets the input focus when we request it and
we limit the focus candidates to the current window's workspace.

However, if the window is unmanaging, the workspace might be unset, and we could
end up in deferencing a NULL pointer causing a crash.

So, in case the window's workspace is unset, just use the currently active
workspace for the display.

Closes https://gitlab.gnome.org/GNOME/mutter/issues/687

https://gitlab.gnome.org/GNOME/mutter/merge_requests/688

(cherry picked from commit 5ca0ef078d)
2019-07-18 11:04:50 +02:00
Jonas Ådahl
4f36e82f68 monitor-manager: Don't use switch-config when ensuring configuration
Switch-configs are only to be used in certain circumstances (see
meta_monitor_manager_can_switch_config()) so when ensuring
configuration and attempting to create a linear configuration, use the
linear configuration constructor function directly without going via the
switch config method, otherwise we might incorrectly fall back to the
fallback configuration (only enable primary monitor).

This is a regression introduced by 6267732bec.

Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/342


(cherry picked from commit 149e4d6934)
2019-07-08 17:38:10 +00:00
Daniel Drake
b7bc8e56b7 monitor-manager: use MonitorsConfig to track switch_config
When constructing MetaMonitorsConfig objects, store which type
of switch_config they are for (or UNKNOWN if it is not such
type of config).

Stop unconditionally setting current_switch_config to UNKNOWN when
handling monitors changed events. Instead, set it to the switch_config
type stored in the MonitorsConfig in the codepath that updates logical
state. In addition to being called in the hotplug case along the same
code flow that generates monitors changed events, this is also called
in the coldplug case where a secondary monitor was connected before
mutter was started.

When creating the default linear display config, create it as a
switch_config so that internal state gets updated to represent
linear mode when this config is used.

The previous behaviour of unconditionally resetting current_switch_config
to UNKNOWN was breaking the internal state machine for display config
switching, causing misbehaviour in gnome-shell's switchMonitor UI when
using display switch hotkeys. The lack of internal tracking when the
displays are already in the default "Join Displays" linear mode was
then causing the first display switch hotkey press to do nothing
(it would attempt to select "Join Displays" mode, but that was already
active).

Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/281
https://gitlab.gnome.org/GNOME/mutter/merge_requests/213


(cherry picked from commit 6267732bec)
2019-07-08 17:37:10 +00:00
Marco Trevisan (Treviño)
d3f75f31c8 screen: Unset the active workspace and remove workspaces on free
Related to https://gitlab.gnome.org/GNOME/mutter/issues/652
2019-07-08 16:34:38 +00:00
Marco Trevisan (Treviño)
0ca9d88926 screen: Destroy window actors after windows while unmanaging compositor
After commit 2e64457f4 we destroy window actors as part of compositor
destruction, however this might emit 'actor-removed' signals on window groups
that the shell could use to access to resources that are already free'd at this
point (like the destroyed screen, in gnome-shell's Panel._updateSolidStyle()).

So, move the actor destructions under in a new function and destroy the window
actors as part of compositor unmanage step, just after that all the windows are
unmanaged (as they need to have an actor around).

Fixes https://gitlab.gnome.org/GNOME/mutter/issues/652
2019-07-08 16:34:38 +00:00
Marco Trevisan (Treviño)
0276630671 window-x11: Use any focusable window as fallback delayed focus window
As per commit f71151a5 we focus an input window if no take-focus-window accepts
it. This might lead to an infinite loop if there are various focusable but
non-input windows in the stack.

When the current focus window is unmanaging and we're trying to focus a
WM_TAKE_FOCUS window, we intent to give the focus to the first focusable input
window in the stack.

However, if an application (such as the Java ones) only uses non-input
WM_TAKE_FOCUS windows, are not requesting these ones to get the focus. This
might lead to a state where no window is focused, or a wrong one is.

So, instead of only focus the first eventually input window available, try to
request to all the take-focus windows that are in the stack between the
destroyed one and the first input one to acquire the input focus.
Use a queue to keep track of those windows, that is passed around stealing
ownership, while we protect for unmanaged queued windows.

Also, reduce the default timeout value, as the previous one might lead to an
excessive long wait.

Added metatests verifying these situations.

Closes: https://gitlab.gnome.org/GNOME/mutter/issues/660
https://gitlab.gnome.org/GNOME/mutter/merge_requests/669

(cherry picked from commit 6d8293a422)
2019-07-08 18:31:05 +02:00
Marco Trevisan (Treviño)
7d54c5621c stack: Add a function to get a sorted list of focus candidates
Use a static function if a window can be the default focus window, and use such
function to return a filtered list of the stack.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/669

(cherry picked from commit 2439255f32)
2019-07-08 17:42:32 +02:00
Marco Trevisan (Treviño)
7951ddab40 window-x11: Accept any focusable window as fallback focus
As per commit f71151a5 we were ignoring WM_TAKE_FOCUS-only windows as focus
targets, however this might end-up in an infinite loop if there are multiple
non-input windows stacked.

So, accept any focusable window as fallback focus target even if it's a
take-focus one (that might not reply to the request).

Added a stacking test to verify this.

Closes: https://gitlab.gnome.org/GNOME/mutter/issues/660
https://gitlab.gnome.org/GNOME/mutter/merge_requests/669

(cherry picked from commit c327b2df95)
2019-07-08 17:42:30 +02:00
Marco Trevisan (Treviño)
a6df771747 window-x11: Don't double-check for unmanaging windows
When looking for the best fallback focus window, we ignore it if it is in the
unmanaging state, but meta_stack_get_default_focus_window() does this is check
for us already.

So, ignore the redundant test.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/669


(cherry picked from commit 9aee47daa9)
2019-07-08 17:41:23 +02:00
Marco Trevisan (Treviño)
90d8fef5f5 window: Emit an error and return when trying to activate an unmanaged
If something (i.e. gnome-shell or an extension) tries to activate an unmanaged
window, we should warn about this and avoid to perform further actions as this
could lead to a crash of mutter, since the window has not valid flags (like
workspace) set anymore at this stage.

Fixes https://gitlab.gnome.org/GNOME/mutter/issues/580

https://gitlab.gnome.org/GNOME/mutter/merge_requests/564


(cherry picked from commit a6fc656e91)
2019-06-28 12:13:51 +00:00
Marco Trevisan (Treviño)
b462e519e8 window-x11: Focus the default window with delay while waiting for take-focus
When requesting to a take-focus window to acquire the input, the client may or
may not respond with a SetInputFocus (this doesn't happen for no-input gtk
windows in fact [to be fixed there too]), in such case we were unsetting the
focus while waiting the reply.

In case the client won't respond, we wait for a small delay (set to 250 ms) for
the take-focus window to grab the input focus before setting it to the default
window.

Added a test for this behavior and for the case in which a window takes the
focus meanwhile we're waiting to focus the default window.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/307
(cherry picked from commit f71151a5dd)
2019-06-24 16:14:00 +02:00
Marco Trevisan (Treviño)
0060ddc5bf test-runner: Add 'dispatch' command
This will only wait for events to be dispatched and processed by the server
without waiting for client processing.

Reuse the code for the wait command too.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/307
(cherry picked from commit 6022b23923)
2019-06-24 16:14:00 +02:00
Marco Trevisan (Treviño)
8dd564adff test-runner: Add 'sleep' command
This allows to sleep for a given timeout in milliseconds.

Rename test_case_before_redraw to test_case_loop_quit since it's a generic
function and use it for the timeout too.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/307
(cherry picked from commit d08763c18c)
2019-06-24 16:14:00 +02:00
Marco Trevisan (Treviño)
556d36baa8 tests: Verify focused window in closed-transient tests
Ensure that we have a focused window when closing transient windows with
no-focus or no-take-focus atoms

https://gitlab.gnome.org/GNOME/mutter/merge_requests/307
(cherry picked from commit fcb408ad5d)
2019-06-24 16:14:00 +02:00
Marco Trevisan (Treviño)
1dd0799c77 test-runner: Add 'assert_focused' command
This allows to verify which window should have the focus, which might not
be the same as the top of the stack.

It's possible to assert the case where there's no focused window using
"NONE" as parameter.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/307
(cherry picked from commit 51f9e04ef1)
2019-06-24 16:14:00 +02:00
Marco Trevisan (Treviño)
1ebf32187a tests, stacking: Add tests with no-input and no-take-focus windows
When a window with no frame, that doesn't accept focus and that has no
take-focus atom set is destroyed, we ended up in not changing the current_focus
window, causing a crash.

Added test cases that verify this situation.

Related to https://gitlab.gnome.org/GNOME/mutter/issues/308
https://gitlab.gnome.org/GNOME/mutter/merge_requests/307
(cherry picked from commit 2fc7760cee)
2019-06-24 16:14:00 +02:00
Marco Trevisan (Treviño)
ac1503a559 tests: Add 'can_take_focus' command to runner and client
Allow to set/unset WM_TAKE_FOCUS from client window.
This is added by default by gtk, but this might not happen in other toolkits,
so add an ability to (un)set this.

So fetch the protocols with XGetWMProtocols and unset the atom.

test-client now needs to depend on Xlib directly in meson build.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/307
(cherry picked from commit f2d2d473b7)
2019-06-24 15:49:16 +02:00
Marco Trevisan (Treviño)
93f268a4a8 tests: Add 'accept_focus' command to runner and client
Under the hood, calls gtk_window_set_accept_focus in the client

https://gitlab.gnome.org/GNOME/mutter/merge_requests/307
(cherry picked from commit e1f839f48f)
2019-06-24 15:49:16 +02:00
Marco Trevisan (Treviño)
68691cb1b9 workspace: Focus only ancestors that are focusable
When destroying a window that has a parent, we initially try to focus one of
its ancestors. However if no ancestor can be focused, then we should instead
focus the default focus window instead of trying to request focus for a window
that can't get focus anyways.

Fixes https://gitlab.gnome.org/GNOME/mutter/issues/308
(cherry picked from commit eccc791f3b)
2019-06-24 15:49:16 +02:00
Marco Trevisan (Treviño)
8d936a410c cogl/pipeline: Don't try to access to free'd pointer data
When free'ing a pipeline we destroy the BigState first and then the fragment and
vertex snippets lists using the big state pointer which is now invalid.
This causes a crash  when G_SLICE=always-malloc is set and using MALLOC_CHECK_.

So, invert the operations by free'ing the snippet lists first, and the big state
afterwards.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/581


(cherry picked from commit 7e0d185120)
2019-05-27 22:40:53 +00:00
Marco Trevisan (Treviño)
fc74c7def9 wayland-seat: Use g_free to cleanup MetaWaylandSeat
MetaWaylandSeat is allocated using g_new0(), and thus we should use g_free() to
destroy it.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/581


(cherry picked from commit 0405786573)
2019-05-27 22:38:46 +00:00
Marco Trevisan (Treviño)
9b8a3fbaed cursor-renderer-native: Free MetaCursorNativePrivate struct
Fix a small leak in native renderer.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/581


(cherry picked from commit b016ff29f6)
2019-05-27 22:35:52 +00:00
29 changed files with 912 additions and 114 deletions

View File

@@ -492,9 +492,6 @@ _cogl_pipeline_free (CoglPipeline *pipeline)
_cogl_bitmask_destroy (&uniforms_state->changed_mask);
}
if (pipeline->differences & COGL_PIPELINE_STATE_NEEDS_BIG_STATE)
g_slice_free (CoglPipelineBigState, pipeline->big_state);
if (pipeline->differences & COGL_PIPELINE_STATE_LAYERS)
{
g_list_foreach (pipeline->layer_differences,
@@ -508,6 +505,9 @@ _cogl_pipeline_free (CoglPipeline *pipeline)
if (pipeline->differences & COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS)
_cogl_pipeline_snippet_list_free (&pipeline->big_state->fragment_snippets);
if (pipeline->differences & COGL_PIPELINE_STATE_NEEDS_BIG_STATE)
g_slice_free (CoglPipelineBigState, pipeline->big_state);
g_list_free (pipeline->deprecated_get_layers_list);
recursively_free_layer_caches (pipeline);

View File

@@ -15,6 +15,12 @@ dist_stacking_DATA = \
tests/stacking/basic-x11.metatest \
tests/stacking/basic-wayland.metatest \
tests/stacking/closed-transient.metatest \
tests/stacking/closed-transient-no-input-no-take-focus-parent.metatest \
tests/stacking/closed-transient-no-input-no-take-focus-parents.metatest \
tests/stacking/closed-transient-no-input-parent.metatest \
tests/stacking/closed-transient-no-input-parent-delayed-focus-default-cancelled.metatest \
tests/stacking/closed-transient-no-input-parents.metatest \
tests/stacking/closed-transient-no-input-parents-queued-default-focus-destroyed.metatest \
tests/stacking/minimized.metatest \
tests/stacking/mixed-windows.metatest \
tests/stacking/set-parent.metatest \

View File

@@ -51,6 +51,14 @@
#define META_TYPE_BACKEND (meta_backend_get_type ())
G_DECLARE_DERIVABLE_TYPE (MetaBackend, meta_backend, META, BACKEND, GObject)
typedef enum _MetaSequenceState
{
META_SEQUENCE_NONE,
META_SEQUENCE_ACCEPTED,
META_SEQUENCE_REJECTED,
META_SEQUENCE_PENDING_END
} MetaSequenceState;
struct _MetaBackendClass
{
GObjectClass parent_class;
@@ -73,6 +81,10 @@ struct _MetaBackendClass
int device_id,
uint32_t timestamp);
void (* finish_touch_sequence) (MetaBackend *backend,
ClutterEventSequence *sequence,
MetaSequenceState state);
void (* warp_pointer) (MetaBackend *backend,
int x,
int y);
@@ -134,6 +146,10 @@ gboolean meta_backend_ungrab_device (MetaBackend *backend,
int device_id,
uint32_t timestamp);
void meta_backend_finish_touch_sequence (MetaBackend *backend,
ClutterEventSequence *sequence,
MetaSequenceState state);
void meta_backend_warp_pointer (MetaBackend *backend,
int x,
int y);

View File

@@ -846,6 +846,20 @@ meta_backend_ungrab_device (MetaBackend *backend,
return META_BACKEND_GET_CLASS (backend)->ungrab_device (backend, device_id, timestamp);
}
/**
* meta_backend_finish_touch_sequence: (skip)
*/
void
meta_backend_finish_touch_sequence (MetaBackend *backend,
ClutterEventSequence *sequence,
MetaSequenceState state)
{
if (META_BACKEND_GET_CLASS (backend)->finish_touch_sequence)
META_BACKEND_GET_CLASS (backend)->finish_touch_sequence (backend,
sequence,
state);
}
/**
* meta_backend_warp_pointer: (skip)
*/

View File

@@ -330,23 +330,35 @@ MetaMonitorsConfigKey *
meta_create_monitors_config_key_for_current_state (MetaMonitorManager *monitor_manager)
{
MetaMonitorsConfigKey *config_key;
MetaMonitorSpec *laptop_monitor_spec;
GList *l;
GList *monitor_specs;
laptop_monitor_spec = NULL;
monitor_specs = NULL;
for (l = monitor_manager->monitors; l; l = l->next)
{
MetaMonitor *monitor = l->data;
MetaMonitorSpec *monitor_spec;
if (meta_monitor_is_laptop_panel (monitor) &&
meta_monitor_manager_is_lid_closed (monitor_manager))
continue;
if (meta_monitor_is_laptop_panel (monitor))
{
laptop_monitor_spec = meta_monitor_get_spec (monitor);
if (meta_monitor_manager_is_lid_closed (monitor_manager))
continue;
}
monitor_spec = meta_monitor_spec_clone (meta_monitor_get_spec (monitor));
monitor_specs = g_list_prepend (monitor_specs, monitor_spec);
}
if (!monitor_specs && laptop_monitor_spec)
{
monitor_specs =
g_list_prepend (NULL, meta_monitor_spec_clone (laptop_monitor_spec));
}
if (!monitor_specs)
return NULL;
@@ -1007,6 +1019,7 @@ meta_monitor_config_manager_create_for_switch_config (MetaMonitorConfigManager
MetaMonitorSwitchConfigType config_type)
{
MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
MetaMonitorsConfig *config;
if (!meta_monitor_manager_can_switch_config (monitor_manager))
return NULL;
@@ -1014,18 +1027,27 @@ meta_monitor_config_manager_create_for_switch_config (MetaMonitorConfigManager
switch (config_type)
{
case META_MONITOR_SWITCH_CONFIG_ALL_MIRROR:
return create_for_switch_config_all_mirror (config_manager);
case META_MONITOR_SWITCH_CONFIG_ALL_LINEAR:
return meta_monitor_config_manager_create_linear (config_manager);
case META_MONITOR_SWITCH_CONFIG_EXTERNAL:
return create_for_switch_config_external (config_manager);
case META_MONITOR_SWITCH_CONFIG_BUILTIN:
return create_for_switch_config_builtin (config_manager);
case META_MONITOR_SWITCH_CONFIG_UNKNOWN:
g_warn_if_reached ();
config = create_for_switch_config_all_mirror (config_manager);
break;
case META_MONITOR_SWITCH_CONFIG_ALL_LINEAR:
config = meta_monitor_config_manager_create_linear (config_manager);
break;
case META_MONITOR_SWITCH_CONFIG_EXTERNAL:
config = create_for_switch_config_external (config_manager);
break;
case META_MONITOR_SWITCH_CONFIG_BUILTIN:
config = create_for_switch_config_builtin (config_manager);
break;
case META_MONITOR_SWITCH_CONFIG_UNKNOWN:
default:
g_warn_if_reached ();
return NULL;
}
return NULL;
if (config)
meta_monitors_config_set_switch_config (config, config_type);
return config;
}
void
@@ -1217,6 +1239,19 @@ meta_monitors_config_key_equal (gconstpointer data_a,
return TRUE;
}
MetaMonitorSwitchConfigType
meta_monitors_config_get_switch_config (MetaMonitorsConfig *config)
{
return config->switch_config;
}
void
meta_monitors_config_set_switch_config (MetaMonitorsConfig *config,
MetaMonitorSwitchConfigType switch_config)
{
config->switch_config = switch_config;
}
MetaMonitorsConfig *
meta_monitors_config_new_full (GList *logical_monitor_configs,
GList *disabled_monitor_specs,
@@ -1232,6 +1267,7 @@ meta_monitors_config_new_full (GList *logical_monitor_con
disabled_monitor_specs);
config->layout_mode = layout_mode;
config->flags = flags;
config->switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN;
return config;
}

View File

@@ -69,6 +69,8 @@ struct _MetaMonitorsConfig
MetaMonitorsConfigFlag flags;
MetaLogicalMonitorLayoutMode layout_mode;
MetaMonitorSwitchConfigType switch_config;
};
#define META_TYPE_MONITORS_CONFIG (meta_monitors_config_get_type ())
@@ -124,6 +126,11 @@ MetaMonitorsConfig * meta_monitors_config_new (MetaMonitorManager *mon
MetaLogicalMonitorLayoutMode layout_mode,
MetaMonitorsConfigFlag flags);
MetaMonitorSwitchConfigType meta_monitors_config_get_switch_config (MetaMonitorsConfig *config);
void meta_monitors_config_set_switch_config (MetaMonitorsConfig *config,
MetaMonitorSwitchConfigType switch_config);
unsigned int meta_monitors_config_key_hash (gconstpointer config_key);
gboolean meta_monitors_config_key_equal (gconstpointer config_key_a,

View File

@@ -2652,8 +2652,6 @@ meta_monitor_manager_read_current_state (MetaMonitorManager *manager)
static void
meta_monitor_manager_notify_monitors_changed (MetaMonitorManager *manager)
{
manager->current_switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN;
meta_backend_monitors_changed (manager->backend);
g_signal_emit (manager, signals[MONITORS_CHANGED_INTERNAL], 0);
@@ -2710,10 +2708,17 @@ meta_monitor_manager_update_logical_state (MetaMonitorManager *manager,
MetaMonitorsConfig *config)
{
if (config)
manager->layout_mode = config->layout_mode;
{
manager->layout_mode = config->layout_mode;
manager->current_switch_config =
meta_monitors_config_get_switch_config (config);
}
else
manager->layout_mode =
meta_monitor_manager_get_default_layout_mode (manager);
{
manager->layout_mode =
meta_monitor_manager_get_default_layout_mode (manager);
manager->current_switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN;
}
meta_monitor_manager_rebuild_logical_monitors (manager, config);
}
@@ -2755,6 +2760,12 @@ void
meta_monitor_manager_update_logical_state_derived (MetaMonitorManager *manager,
MetaMonitorsConfig *config)
{
if (config)
manager->current_switch_config =
meta_monitors_config_get_switch_config (config);
else
manager->current_switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN;
manager->layout_mode = META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL;
meta_monitor_manager_rebuild_logical_monitors_derived (manager, config);

View File

@@ -710,6 +710,7 @@ static void
cursor_priv_free (MetaCursorNativePrivate *cursor_priv)
{
g_hash_table_destroy (cursor_priv->gpu_states);
g_free (cursor_priv);
}
static MetaCursorNativePrivate *

View File

@@ -60,6 +60,10 @@ struct _MetaBackendX11Private
XSyncAlarm user_active_alarm;
XSyncCounter counter;
int current_touch_replay_sync_serial;
int pending_touch_replay_sync_serial;
Atom touch_replay_sync_atom;
int xinput_opcode;
int xinput_event_base;
int xinput_error_base;
@@ -168,6 +172,26 @@ meta_backend_x11_translate_device_event (MetaBackendX11 *x11,
backend_x11_class->translate_device_event (x11, device_event);
}
static void
maybe_translate_touch_replay_pointer_event (MetaBackendX11 *x11,
XIDeviceEvent *device_event)
{
MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11);
if (!device_event->send_event &&
device_event->time != CurrentTime &&
priv->current_touch_replay_sync_serial !=
priv->pending_touch_replay_sync_serial &&
XSERVER_TIME_IS_BEFORE (device_event->time, priv->latest_evtime))
{
/* Emulated pointer events received after XIRejectTouch is received
* on a passive touch grab will contain older timestamps, update those
* so we dont get InvalidTime at grabs.
*/
device_event->time = priv->latest_evtime;
}
}
static void
translate_device_event (MetaBackendX11 *x11,
XIDeviceEvent *device_event)
@@ -177,19 +201,7 @@ translate_device_event (MetaBackendX11 *x11,
meta_backend_x11_translate_device_event (x11, device_event);
if (!device_event->send_event && device_event->time != CurrentTime)
{
if (XSERVER_TIME_IS_BEFORE (device_event->time, priv->latest_evtime))
{
/* Emulated pointer events received after XIRejectTouch is received
* on a passive touch grab will contain older timestamps, update those
* so we dont get InvalidTime at grabs.
*/
device_event->time = priv->latest_evtime;
}
/* Update the internal latest evtime, for any possible later use */
priv->latest_evtime = device_event->time;
}
priv->latest_evtime = device_event->time;
}
static void
@@ -254,6 +266,9 @@ maybe_spoof_event_as_stage_event (MetaBackendX11 *x11,
case XI_Motion:
case XI_ButtonPress:
case XI_ButtonRelease:
maybe_translate_touch_replay_pointer_event (x11,
(XIDeviceEvent *) input_event);
/* Intentional fall-through */
case XI_KeyPress:
case XI_KeyRelease:
case XI_TouchBegin:
@@ -321,6 +336,17 @@ handle_host_xevent (MetaBackend *backend,
MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11);
gboolean bypass_clutter = FALSE;
switch (event->type)
{
case ClientMessage:
if (event->xclient.window == meta_backend_x11_get_xwindow (x11) &&
event->xclient.message_type == priv->touch_replay_sync_atom)
priv->current_touch_replay_sync_serial = event->xclient.data.l[0];
break;
default:
break;
}
XGetEventData (priv->xdisplay, &event->xcookie);
{
@@ -528,6 +554,10 @@ meta_backend_x11_post_init (MetaBackend *backend)
monitor_manager = meta_backend_get_monitor_manager (backend);
g_signal_connect (monitor_manager, "monitors-changed-internal",
G_CALLBACK (on_monitors_changed), backend);
priv->touch_replay_sync_atom = XInternAtom (priv->xdisplay,
"_MUTTER_TOUCH_SEQUENCE_SYNC",
False);
}
static ClutterBackend *
@@ -584,6 +614,43 @@ meta_backend_x11_ungrab_device (MetaBackend *backend,
return (ret == Success);
}
static void
meta_backend_x11_finish_touch_sequence (MetaBackend *backend,
ClutterEventSequence *sequence,
MetaSequenceState state)
{
MetaBackendX11 *x11 = META_BACKEND_X11 (backend);
MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11);
int event_mode;
if (state == META_SEQUENCE_ACCEPTED)
event_mode = XIAcceptTouch;
else if (state == META_SEQUENCE_REJECTED)
event_mode = XIRejectTouch;
else
g_return_if_reached ();
XIAllowTouchEvents (priv->xdisplay,
META_VIRTUAL_CORE_POINTER_ID,
clutter_x11_event_sequence_get_touch_detail (sequence),
DefaultRootWindow (priv->xdisplay), event_mode);
if (state == META_SEQUENCE_REJECTED)
{
XClientMessageEvent ev;
ev = (XClientMessageEvent) {
.type = ClientMessage,
.window = meta_backend_x11_get_xwindow (x11),
.message_type = priv->touch_replay_sync_atom,
.format = 32,
.data.l[0] = ++priv->pending_touch_replay_sync_serial,
};
XSendEvent (priv->xdisplay, meta_backend_x11_get_xwindow (x11),
False, 0, (XEvent *) &ev);
}
}
static void
meta_backend_x11_warp_pointer (MetaBackend *backend,
int x,
@@ -769,6 +836,7 @@ meta_backend_x11_class_init (MetaBackendX11Class *klass)
backend_class->post_init = meta_backend_x11_post_init;
backend_class->grab_device = meta_backend_x11_grab_device;
backend_class->ungrab_device = meta_backend_x11_ungrab_device;
backend_class->finish_touch_sequence = meta_backend_x11_finish_touch_sequence;
backend_class->warp_pointer = meta_backend_x11_warp_pointer;
backend_class->get_current_logical_monitor = meta_backend_x11_get_current_logical_monitor;
backend_class->get_keymap = meta_backend_x11_get_keymap;

View File

@@ -73,4 +73,6 @@ MetaCloseDialog * meta_compositor_create_close_dialog (MetaCompositor *composito
MetaInhibitShortcutsDialog * meta_compositor_create_inhibit_shortcuts_dialog (MetaCompositor *compositor,
MetaWindow *window);
void meta_compositor_unmanage_window_actors (MetaCompositor *compositor);
#endif /* META_COMPOSITOR_PRIVATE_H */

View File

@@ -145,19 +145,6 @@ meta_compositor_destroy (MetaCompositor *compositor)
clutter_threads_remove_repaint_func (compositor->pre_paint_func_id);
clutter_threads_remove_repaint_func (compositor->post_paint_func_id);
if (compositor->top_window_actor)
{
g_signal_handlers_disconnect_by_func (compositor->top_window_actor,
on_top_window_actor_destroyed,
compositor);
compositor->top_window_actor = NULL;
}
g_clear_pointer (&compositor->window_group, clutter_actor_destroy);
g_clear_pointer (&compositor->top_window_group, clutter_actor_destroy);
g_clear_pointer (&compositor->feedback_group, clutter_actor_destroy);
g_clear_pointer (&compositor->windows, g_list_free);
if (compositor->have_x11_sync_object)
meta_sync_ring_destroy ();
}
@@ -610,6 +597,23 @@ meta_compositor_unmanage (MetaCompositor *compositor)
}
}
void
meta_compositor_unmanage_window_actors (MetaCompositor *compositor)
{
if (compositor->top_window_actor)
{
g_signal_handlers_disconnect_by_func (compositor->top_window_actor,
on_top_window_actor_destroyed,
compositor);
compositor->top_window_actor = NULL;
}
g_clear_pointer (&compositor->window_group, clutter_actor_destroy);
g_clear_pointer (&compositor->top_window_group, clutter_actor_destroy);
g_clear_pointer (&compositor->feedback_group, clutter_actor_destroy);
g_clear_pointer (&compositor->windows, g_list_free);
}
/**
* meta_shape_cow_for_window:
* @compositor: A #MetaCompositor

View File

@@ -55,6 +55,7 @@
#include "backends/x11/meta-backend-x11.h"
#include "backends/meta-stage-private.h"
#include "backends/meta-input-settings-private.h"
#include "backends/meta-backend-private.h"
#include <clutter/x11/clutter-x11.h>
#ifdef HAVE_RANDR
@@ -533,27 +534,23 @@ gesture_tracker_state_changed (MetaGestureTracker *tracker,
MetaSequenceState state,
MetaDisplay *display)
{
if (meta_is_wayland_compositor ())
switch (state)
{
if (state == META_SEQUENCE_ACCEPTED)
meta_display_cancel_touch (display);
}
else
{
MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ());
int event_mode;
case META_SEQUENCE_NONE:
case META_SEQUENCE_PENDING_END:
return;
case META_SEQUENCE_ACCEPTED:
meta_display_cancel_touch (display);
if (state == META_SEQUENCE_ACCEPTED)
event_mode = XIAcceptTouch;
else if (state == META_SEQUENCE_REJECTED)
event_mode = XIRejectTouch;
else
return;
/* Intentional fall-through */
case META_SEQUENCE_REJECTED:
{
MetaBackend *backend;
XIAllowTouchEvents (meta_backend_x11_get_xdisplay (backend),
META_VIRTUAL_CORE_POINTER_ID,
clutter_x11_event_sequence_get_touch_detail (sequence),
DefaultRootWindow (display->xdisplay), event_mode);
backend = meta_get_backend ();
meta_backend_finish_touch_sequence (backend, sequence, state);
break;
}
}
}

View File

@@ -38,12 +38,7 @@
typedef struct _MetaGestureTracker MetaGestureTracker;
typedef struct _MetaGestureTrackerClass MetaGestureTrackerClass;
typedef enum {
META_SEQUENCE_NONE,
META_SEQUENCE_ACCEPTED,
META_SEQUENCE_REJECTED,
META_SEQUENCE_PENDING_END
} MetaSequenceState;
typedef enum _MetaSequenceState MetaSequenceState;
struct _MetaGestureTracker
{

View File

@@ -31,6 +31,7 @@
#include "config.h"
#include "meta-gesture-tracker-private.h"
#include "meta-surface-actor.h"
#include "meta-backend-private.h"
#define DISTANCE_THRESHOLD 30

View File

@@ -41,6 +41,7 @@
#include "stack.h"
#include <meta/compositor.h>
#include <meta/meta-enum-types.h>
#include "compositor-private.h"
#include "core.h"
#include "meta-cursor-tracker-private.h"
#include "boxes-private.h"
@@ -835,6 +836,7 @@ meta_screen_free (MetaScreen *screen,
guint32 timestamp)
{
MetaDisplay *display;
GList *l;
display = screen->display;
@@ -844,6 +846,8 @@ meta_screen_free (MetaScreen *screen,
meta_display_unmanage_windows_for_screen (display, screen, timestamp);
meta_compositor_unmanage_window_actors (display->compositor);
meta_prefs_remove_listener (prefs_changed_callback, screen);
meta_screen_ungrab_keys (screen);
@@ -874,6 +878,16 @@ meta_screen_free (MetaScreen *screen,
g_free (screen->screen_name);
screen->active_workspace = NULL;
for (l = screen->workspaces; l;)
{
MetaWorkspace *workspace = l->data;
l = l->next;
meta_workspace_remove (workspace);
}
g_object_unref (screen);
}

View File

@@ -1176,6 +1176,27 @@ window_contains_point (MetaWindow *window,
return POINT_IN_RECT (root_x, root_y, rect);
}
static gboolean
window_can_get_default_focus (MetaWindow *window)
{
if (window->unmaps_pending > 0)
return FALSE;
if (window->unmanaging)
return FALSE;
if (!(window->input || window->take_focus))
return FALSE;
if (!meta_window_should_be_showing (window))
return FALSE;
if (window->type == META_WINDOW_DOCK)
return FALSE;
return TRUE;
}
static MetaWindow*
get_default_focus_window (MetaStack *stack,
MetaWorkspace *workspace,
@@ -1203,24 +1224,12 @@ get_default_focus_window (MetaStack *stack,
if (window == not_this_one)
continue;
if (window->unmaps_pending > 0)
continue;
if (window->unmanaging)
continue;
if (!(window->input || window->take_focus))
continue;
if (!meta_window_should_be_showing (window))
if (!window_can_get_default_focus (window))
continue;
if (must_be_at_point && !window_contains_point (window, root_x, root_y))
continue;
if (window->type == META_WINDOW_DOCK)
continue;
return window;
}
@@ -1275,6 +1284,26 @@ meta_stack_list_windows (MetaStack *stack,
return workspace_windows;
}
GList *
meta_stack_get_default_focus_candidates (MetaStack *stack,
MetaWorkspace *workspace)
{
GList *windows = meta_stack_list_windows (stack, workspace);
GList *l;
for (l = windows; l;)
{
GList *next = l->next;
if (!window_can_get_default_focus (l->data))
windows = g_list_delete_link (windows, l);
l = next;
}
return windows;
}
int
meta_stack_windows_cmp (MetaStack *stack,
MetaWindow *window_a,

View File

@@ -337,6 +337,21 @@ MetaWindow* meta_stack_get_default_focus_window_at_point (MetaStack *stack,
int root_x,
int root_y);
/**
* meta_stack_get_default_focus_candidates:
* @stack: The stack to examine.
* @workspace: If not %NULL, only windows on this workspace will be
* returned; otherwise all windows in the stack will be
* returned.
*
* Returns all the focus candidate windows in the stack, in order.
*
* Returns: (transfer container) (element-type Meta.Window):
* A #GList of #MetaWindow, in stacking order, honouring layers.
*/
GList * meta_stack_get_default_focus_candidates (MetaStack *stack,
MetaWorkspace *workspace);
/**
* meta_stack_list_windows:
* @stack: The stack to examine.

View File

@@ -1272,11 +1272,10 @@ _meta_window_shared_new (MetaDisplay *display,
window->desc, window->transient_for->desc);
set_workspace_state (window,
window->transient_for->on_all_workspaces_requested,
window->transient_for->on_all_workspaces,
window->transient_for->workspace);
}
if (window->on_all_workspaces)
else if (window->on_all_workspaces)
{
meta_topic (META_DEBUG_PLACEMENT,
"Putting window %s on all workspaces\n",
@@ -3622,6 +3621,13 @@ meta_window_activate_full (MetaWindow *window,
MetaWorkspace *workspace)
{
gboolean allow_workspace_switch;
if (window->unmanaging)
{
g_warning ("Trying to activate unmanaged window '%s'", window->desc);
return;
}
meta_topic (META_DEBUG_FOCUS,
"_NET_ACTIVE_WINDOW message sent for %s at time %u "
"by client type %u.\n",

View File

@@ -85,6 +85,12 @@ typedef struct _MetaWorkspaceLogicalMonitorData
MetaRectangle logical_monitor_work_area;
} MetaWorkspaceLogicalMonitorData;
typedef struct _MetaWorkspaceFocusableAncestorData
{
MetaWorkspace *workspace;
MetaWindow *out_window;
} MetaWorkspaceFocusableAncestorData;
static MetaWorkspaceLogicalMonitorData *
meta_workspace_get_logical_monitor_data (MetaWorkspace *workspace,
MetaLogicalMonitor *logical_monitor)
@@ -1319,13 +1325,20 @@ meta_workspace_focus_default_window (MetaWorkspace *workspace,
}
static gboolean
record_ancestor (MetaWindow *window,
void *data)
find_focusable_ancestor (MetaWindow *window,
gpointer user_data)
{
MetaWindow **result = data;
MetaWorkspaceFocusableAncestorData *data = user_data;
*result = window;
return FALSE; /* quit with the first ancestor we find */
if (!window->unmanaging && (window->input || window->take_focus) &&
meta_window_located_on_workspace (window, data->workspace) &&
meta_window_showing_on_its_workspace (window))
{
data->out_window = window;
return FALSE;
}
return TRUE;
}
/* Focus ancestor of not_this_one if there is one */
@@ -1347,11 +1360,15 @@ focus_ancestor_or_top_window (MetaWorkspace *workspace,
if (not_this_one)
{
MetaWindow *ancestor;
ancestor = NULL;
meta_window_foreach_ancestor (not_this_one, record_ancestor, &ancestor);
if (ancestor != NULL &&
meta_window_located_on_workspace (ancestor, workspace) &&
meta_window_showing_on_its_workspace (ancestor))
MetaWorkspaceFocusableAncestorData data;
data = (MetaWorkspaceFocusableAncestorData) {
.workspace = workspace,
};
meta_window_foreach_ancestor (not_this_one, find_focusable_ancestor, &data);
ancestor = data.out_window;
if (ancestor)
{
meta_topic (META_DEBUG_FOCUS,
"Focusing %s, ancestor of %s\n",

View File

@@ -0,0 +1,23 @@
new_client 1 x11
create 1/1
show 1/1
create 1/2 csd
set_parent 1/2 1
can_take_focus 1/2 false
accept_focus 1/2 false
show 1/2
create 1/3 csd
set_parent 1/3 2
show 1/3
wait
assert_focused 1/3
assert_stacking 1/1 1/2 1/3
destroy 1/3
wait
assert_focused 1/1
assert_stacking 1/1 1/2

View File

@@ -0,0 +1,30 @@
new_client 2 x11
create 2/1
show 2/1
wait
new_client 1 x11
create 1/1
accept_focus 1/1 false
can_take_focus 1/1 false
show 1/1
create 1/2 csd
set_parent 1/2 1
can_take_focus 1/2 false
accept_focus 1/2 false
show 1/2
create 1/3 csd
set_parent 1/3 2
show 1/3
wait
assert_focused 1/3
assert_stacking 2/1 1/1 1/2 1/3
destroy 1/3
wait
assert_stacking 1/1 1/2 2/1
assert_focused 2/1

View File

@@ -0,0 +1,36 @@
new_client 2 x11
create 2/1
show 2/1
new_client 1 x11
create 1/1
show 1/1
create 1/2 csd
set_parent 1/2 1
accept_focus 1/2 false
show 1/2
create 1/3 csd
set_parent 1/3 2
show 1/3
wait
assert_focused 1/3
assert_stacking 2/1 1/1 1/2 1/3
destroy 1/3
sleep 10
assert_focused none
assert_stacking 2/1 1/1 1/2
activate 2/1
wait
assert_focused 2/1
assert_stacking 1/1 1/2 2/1
sleep 250
assert_focused 2/1
assert_stacking 1/1 1/2 2/1

View File

@@ -0,0 +1,30 @@
new_client 2 x11
create 2/1
show 2/1
new_client 1 x11
create 1/1
show 1/1
create 1/2 csd
set_parent 1/2 1
accept_focus 1/2 false
show 1/2
create 1/3 csd
set_parent 1/3 2
show 1/3
wait
assert_focused 1/3
assert_stacking 2/1 1/1 1/2 1/3
destroy 1/3
dispatch
assert_focused none
assert_stacking 2/1 1/1 1/2
sleep 150
assert_focused 1/1
assert_stacking 2/1 1/1 1/2

View File

@@ -0,0 +1,43 @@
new_client 0 x11
create 0/1
show 0/1
new_client 1 x11
create 1/1
show 1/1
create 1/2 csd
set_parent 1/2 1
accept_focus 1/2 false
show 1/2
create 1/3 csd
set_parent 1/3 2
accept_focus 1/3 false
show 1/3
create 1/4 csd
set_parent 1/4 3
accept_focus 1/4 false
show 1/4
create 1/5 csd
set_parent 1/5 3
show 1/5
wait
assert_focused 1/5
assert_stacking 0/1 1/1 1/2 1/3 1/4 1/5
destroy 1/5
dispatch
assert_focused none
assert_stacking 0/1 1/1 1/2 1/3 1/4
destroy 1/2
dispatch
sleep 450
assert_focused 1/1
assert_stacking 0/1 1/1 1/3 1/4

View File

@@ -0,0 +1,46 @@
new_client 0 x11
create 0/1
show 0/1
new_client 1 x11
create 1/1
show 1/1
create 1/2 csd
set_parent 1/2 1
accept_focus 1/2 false
show 1/2
create 1/3 csd
set_parent 1/3 2
accept_focus 1/3 false
show 1/3
create 1/4 csd
set_parent 1/4 3
accept_focus 1/4 false
show 1/4
create 1/5 csd
set_parent 1/5 3
show 1/5
wait
assert_focused 1/5
assert_stacking 0/1 1/1 1/2 1/3 1/4 1/5
destroy 1/5
dispatch
assert_focused none
assert_stacking 0/1 1/1 1/2 1/3 1/4
sleep 600
assert_focused 1/1
assert_stacking 0/1 1/1 1/2 1/3 1/4
destroy 1/3
wait
assert_focused 1/1
assert_stacking 0/1 1/1 1/2 1/4

View File

@@ -161,6 +161,74 @@ process_line (const char *line)
gtk_window_set_transient_for (GTK_WINDOW (window),
GTK_WINDOW (parent_window));
}
else if (strcmp (argv[0], "accept_focus") == 0)
{
if (argc != 3)
{
g_print ("usage: %s <window-id> [true|false]", argv[0]);
goto out;
}
GtkWidget *window = lookup_window (argv[1]);
if (!window)
{
g_print ("unknown window %s", argv[1]);
goto out;
}
gboolean enabled = g_ascii_strcasecmp (argv[2], "true") == 0;
gtk_window_set_accept_focus (GTK_WINDOW (window), enabled);
}
else if (strcmp (argv[0], "can_take_focus") == 0)
{
if (argc != 3)
{
g_print ("usage: %s <window-id> [true|false]", argv[0]);
goto out;
}
GtkWidget *window = lookup_window (argv[1]);
if (!window)
{
g_print ("unknown window %s", argv[1]);
goto out;
}
if (wayland)
{
g_print ("%s not supported under wayland", argv[0]);
goto out;
}
GdkDisplay *display = gdk_display_get_default ();
GdkWindow *gdkwindow = gtk_widget_get_window (window);
Display *xdisplay = gdk_x11_display_get_xdisplay (display);
Window xwindow = GDK_WINDOW_XID (gdkwindow);
Atom wm_take_focus = gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS");
gboolean add = g_ascii_strcasecmp(argv[2], "true") == 0;
Atom *protocols = NULL;
Atom *new_protocols;
int n_protocols = 0;
int i, n = 0;
gdk_display_sync (display);
XGetWMProtocols (xdisplay, xwindow, &protocols, &n_protocols);
new_protocols = g_new0 (Atom, n_protocols + (add ? 1 : 0));
for (i = 0; i < n_protocols; ++i)
{
if (protocols[i] != wm_take_focus)
new_protocols[n++] = protocols[i];
}
if (add)
new_protocols[n++] = wm_take_focus;
XSetWMProtocols (xdisplay, xwindow, new_protocols, n);
XFree (new_protocols);
XFree (protocols);
}
else if (strcmp (argv[0], "show") == 0)
{
if (argc != 2)

View File

@@ -113,7 +113,7 @@ test_case_new (void)
}
static gboolean
test_case_before_redraw (gpointer data)
test_case_loop_quit (gpointer data)
{
TestCase *test = data;
@@ -122,6 +122,24 @@ test_case_before_redraw (gpointer data)
return FALSE;
}
static gboolean
test_case_dispatch (TestCase *test,
GError **error)
{
/* Wait until we've done any outstanding queued up work.
* Though we add this as BEFORE_REDRAW, the iteration that runs the
* BEFORE_REDRAW idles will proceed on and do the redraw, so we're
* waiting until after *all* frame processing.
*/
meta_later_add (META_LATER_BEFORE_REDRAW,
test_case_loop_quit,
test,
NULL);
g_main_loop_run (test->loop);
return TRUE;
}
static gboolean
test_case_wait (TestCase *test,
GError **error)
@@ -138,16 +156,8 @@ test_case_wait (TestCase *test,
if (!test_client_wait (value, error))
return FALSE;
/* Then wait until we've done any outstanding queued up work.
* Though we add this as BEFORE_REDRAW, the iteration that runs the
* BEFORE_REDRAW idles will proceed on and do the redraw, so we're
* waiting until after *all* frame processing.
*/
meta_later_add (META_LATER_BEFORE_REDRAW,
test_case_before_redraw,
test,
NULL);
g_main_loop_run (test->loop);
/* Then wait until we've done any outstanding queued up work. */
test_case_dispatch (test, error);
/* Then set an XSync counter ourselves and and wait until
* we receive the resulting event - this makes sure that we've
@@ -157,6 +167,17 @@ test_case_wait (TestCase *test,
return TRUE;
}
static gboolean
test_case_sleep (TestCase *test,
guint32 interval,
GError **error)
{
g_timeout_add_full (G_PRIORITY_LOW, interval, test_case_loop_quit, test, NULL);
g_main_loop_run (test->loop);
return TRUE;
}
#define BAD_COMMAND(...) \
G_STMT_START { \
g_set_error (error, \
@@ -273,6 +294,37 @@ test_case_assert_stacking (TestCase *test,
return *error == NULL;
}
static gboolean
test_case_assert_focused (TestCase *test,
const char *expected_window,
GError **error)
{
MetaDisplay *display = meta_get_display ();
if (!display->focus_window)
{
if (g_strcmp0 (expected_window, "none") != 0)
{
g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_ASSERTION_FAILED,
"focus: expected='%s', actual='none'", expected_window);
}
}
else
{
const char *focused = display->focus_window->title;
if (g_str_has_prefix (focused, "test/"))
focused += 5;
if (g_strcmp0 (focused, expected_window) != 0)
g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_ASSERTION_FAILED,
"focus: expected='%s', actual='%s'",
expected_window, focused);
}
return *error == NULL;
}
static gboolean
test_case_check_xserver_stacking (TestCase *test,
GError **error)
@@ -406,6 +458,44 @@ test_case_do (TestCase *test,
NULL))
return FALSE;
}
else if (strcmp (argv[0], "accept_focus") == 0)
{
if (argc != 3 ||
(g_ascii_strcasecmp (argv[2], "true") != 0 &&
g_ascii_strcasecmp (argv[2], "false") != 0))
BAD_COMMAND("usage: %s <client-id>/<window-id> [true|false]",
argv[0]);
TestClient *client;
const char *window_id;
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
return FALSE;
if (!test_client_do (client, error,
argv[0], window_id,
argv[2],
NULL))
return FALSE;
}
else if (strcmp (argv[0], "can_take_focus") == 0)
{
if (argc != 3 ||
(g_ascii_strcasecmp (argv[2], "true") != 0 &&
g_ascii_strcasecmp (argv[2], "false") != 0))
BAD_COMMAND("usage: %s <client-id>/<window-id> [true|false]",
argv[0]);
TestClient *client;
const char *window_id;
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
return FALSE;
if (!test_client_do (client, error,
argv[0], window_id,
argv[2],
NULL))
return FALSE;
}
else if (strcmp (argv[0], "show") == 0 ||
strcmp (argv[0], "hide") == 0 ||
strcmp (argv[0], "activate") == 0 ||
@@ -450,6 +540,28 @@ test_case_do (TestCase *test,
if (!test_case_wait (test, error))
return FALSE;
}
else if (strcmp (argv[0], "dispatch") == 0)
{
if (argc != 1)
BAD_COMMAND("usage: %s", argv[0]);
if (!test_case_dispatch (test, error))
return FALSE;
}
else if (strcmp (argv[0], "sleep") == 0)
{
guint64 interval;
if (argc != 2)
BAD_COMMAND("usage: %s <milliseconds>", argv[0]);
if (!g_ascii_string_to_unsigned (argv[1], 10, 0, G_MAXUINT32,
&interval, error))
return FALSE;
if (!test_case_sleep (test, (guint32) interval, error))
return FALSE;
}
else if (strcmp (argv[0], "assert_stacking") == 0)
{
if (!test_case_assert_stacking (test, argv + 1, argc - 1, error))
@@ -458,6 +570,11 @@ test_case_do (TestCase *test,
if (!test_case_check_xserver_stacking (test, error))
return FALSE;
}
else if (strcmp (argv[0], "assert_focused") == 0)
{
if (!test_case_assert_focused (test, argv[1], error))
return FALSE;
}
else
{
BAD_COMMAND("Unknown command %s", argv[0]);

View File

@@ -264,7 +264,7 @@ meta_wayland_seat_free (MetaWaylandSeat *seat)
g_object_unref (seat->touch);
meta_wayland_text_input_destroy (seat->text_input);
g_slice_free (MetaWaylandSeat, seat);
g_free (seat);
}
static gboolean

View File

@@ -53,8 +53,15 @@
#include "backends/meta-logical-monitor.h"
#include "backends/x11/meta-backend-x11.h"
#define TAKE_FOCUS_FALLBACK_DELAY_MS 150
G_DEFINE_TYPE_WITH_PRIVATE (MetaWindowX11, meta_window_x11, META_TYPE_WINDOW)
static void
meta_window_x11_maybe_focus_delayed (MetaWindow *window,
GQueue *other_focus_candidates,
guint32 timestamp);
static void
meta_window_x11_init (MetaWindowX11 *window_x11)
{
@@ -722,6 +729,160 @@ request_take_focus (MetaWindow *window,
send_icccm_message (window, display->atom_WM_TAKE_FOCUS, timestamp);
}
typedef struct
{
MetaWindow *window;
GQueue *pending_focus_candidates;
guint32 timestamp;
guint timeout_id;
gulong unmanaged_id;
gulong focused_changed_id;
} MetaWindowX11DelayedFocusData;
static void
disconnect_pending_focus_window_signals (MetaWindow *window,
GQueue *focus_candidates)
{
g_signal_handlers_disconnect_by_func (window, g_queue_remove,
focus_candidates);
}
static void
meta_window_x11_delayed_focus_data_free (MetaWindowX11DelayedFocusData *data)
{
g_signal_handler_disconnect (data->window, data->unmanaged_id);
g_signal_handler_disconnect (data->window->display, data->focused_changed_id);
if (data->pending_focus_candidates)
{
g_queue_foreach (data->pending_focus_candidates,
(GFunc) disconnect_pending_focus_window_signals,
data->pending_focus_candidates);
g_queue_free (data->pending_focus_candidates);
}
if (data->timeout_id)
g_source_remove (data->timeout_id);
g_free (data);
}
static void
focus_candidates_maybe_take_and_focus_next (GQueue **focus_candidates_ptr,
guint32 timestamp)
{
MetaWindow *focus_window;
GQueue *focus_candidates;
g_assert (*focus_candidates_ptr);
if (g_queue_is_empty (*focus_candidates_ptr))
return;
focus_candidates = g_steal_pointer (focus_candidates_ptr);
focus_window = g_queue_pop_head (focus_candidates);
disconnect_pending_focus_window_signals (focus_window, focus_candidates);
meta_window_x11_maybe_focus_delayed (focus_window, focus_candidates, timestamp);
}
static gboolean
focus_window_delayed_timeout (gpointer user_data)
{
MetaWindowX11DelayedFocusData *data = user_data;
MetaWindow *window = data->window;
guint32 timestamp = data->timestamp;
focus_candidates_maybe_take_and_focus_next (&data->pending_focus_candidates,
timestamp);
data->timeout_id = 0;
meta_window_x11_delayed_focus_data_free (data);
meta_window_focus (window, timestamp);
return G_SOURCE_REMOVE;
}
static void
meta_window_x11_maybe_focus_delayed (MetaWindow *window,
GQueue *other_focus_candidates,
guint32 timestamp)
{
MetaWindowX11DelayedFocusData *data;
data = g_new0 (MetaWindowX11DelayedFocusData, 1);
data->window = window;
data->timestamp = timestamp;
data->pending_focus_candidates = other_focus_candidates;
meta_topic (META_DEBUG_FOCUS,
"Requesting delayed focus to %s\n", window->desc);
data->unmanaged_id =
g_signal_connect_swapped (window, "unmanaged",
G_CALLBACK (meta_window_x11_delayed_focus_data_free),
data);
data->focused_changed_id =
g_signal_connect_swapped (window->display, "notify::focus-window",
G_CALLBACK (meta_window_x11_delayed_focus_data_free),
data);
data->timeout_id = g_timeout_add (TAKE_FOCUS_FALLBACK_DELAY_MS,
focus_window_delayed_timeout, data);
}
static void
maybe_focus_default_window (MetaScreen *screen,
MetaWindow *not_this_one,
guint32 timestamp)
{
MetaWorkspace *workspace;
MetaStack *stack = screen->stack;
g_autoptr (GList) focusable_windows = NULL;
g_autoptr (GQueue) focus_candidates = NULL;
GList *l;
if (not_this_one && not_this_one->workspace)
workspace = not_this_one->workspace;
else
workspace = screen->active_workspace;
/* Go through all the focusable windows and try to focus them
* in order, waiting for a delay. The first one that replies to
* the request (in case of take focus windows) changing the display
* focused window, will stop the chained requests.
*/
focusable_windows =
meta_stack_get_default_focus_candidates (stack, workspace);
focus_candidates = g_queue_new ();
for (l = g_list_last (focusable_windows); l; l = l->prev)
{
MetaWindow *focus_window = l->data;
if (focus_window == not_this_one)
continue;
g_queue_push_tail (focus_candidates, focus_window);
g_signal_connect_swapped (focus_window, "unmanaged",
G_CALLBACK (g_queue_remove),
focus_candidates);
if (!META_IS_WINDOW_X11 (focus_window))
break;
if (focus_window->input)
break;
if (focus_window->shaded && focus_window->frame)
break;
}
focus_candidates_maybe_take_and_focus_next (&focus_candidates, timestamp);
}
static void
meta_window_x11_focus (MetaWindow *window,
guint32 timestamp)
@@ -771,14 +932,19 @@ meta_window_x11_focus (MetaWindow *window,
* Normally, we want to just leave the focus undisturbed until
* the window responds to WM_TAKE_FOCUS, but if we're unmanaging
* the current focus window we *need* to move the focus away, so
* we focus the no_focus_window now (and set
* display->focus_window to that) before sending WM_TAKE_FOCUS.
* we focus the no focus window before sending WM_TAKE_FOCUS,
* and eventually the default focus windwo excluding this one,
* if meanwhile we don't get any focus request.
*/
if (window->display->focus_window != NULL &&
window->display->focus_window->unmanaging)
meta_display_focus_the_no_focus_window (window->display,
window->screen,
timestamp);
{
meta_display_focus_the_no_focus_window (window->display,
window->screen,
timestamp);
maybe_focus_default_window (window->screen, window,
timestamp);
}
}
request_take_focus (window, timestamp);