seat/impl: Wait for pointer constraining when updating viewports

It is generally assumed here and there that the pointer at all point in
time is within some logical monitor, if there is any logical monitor to
be within.

With the input thread, this was for a short amount of time not reliable,
resulting in crashes in combination with hotplugging or suspend/resume,
where monitors come and go quickly.

What happens is that the pointer at first is within a logical monitor,
but when that logical monitor is removed, while the new monitor
viewports are handed to the input thread, the constraining happens
asynchronously, meaning there is a time between between the new
viewports are sent, and before clutter_seat_query_state() starts
reporting the constrained position.

If a new client mapped a maximized window during this short time frame,
we'd crash with

    #0 meta_window_place at ../src/core/place.c:883
    #1 place_window_if_needed at ../src/core/constraints.c:562
    #2 meta_window_constrain at ../src/core/constraints.c:310
    #3 meta_window_move_resize_internal at ../src/core/window.c:3869
    #4 meta_window_force_placement at ../src/core/window.c:2120
    #5 xdg_toplevel_set_maximized at ../src/wayland/meta-wayland-xdg-shell.c:429
    #6 ffi_call_unix64 at ../src/x86/unix64.S:105
    #7 ffi_call_int at ../src/x86/ffi64.c:672
    #8 wl_closure_invoke at ../src/connection.c:1025
    #9 wl_client_connection_data at ../src/wayland-server.c:437

The fix for this is to make sure that the viewports are updated and
pointers constrained synchronously, i.e. the main thread will wait until
after the input thread is done constraining before continuing.

Related: https://bugzilla.redhat.com/show_bug.cgi?id=2147502
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2711>
This commit is contained in:
Jonas Ådahl 2022-11-24 11:19:28 +01:00
parent eb7abdacd8
commit 1645171d4b

View File

@ -3623,17 +3623,31 @@ ensure_pointer_onscreen (MetaSeatImpl *seat_impl)
coords.x, coords.y, NULL);
}
typedef struct
{
MetaViewportInfo *viewports;
GMutex mutex;
GCond cond;
gboolean constrained;
} SetViewportsData;
static gboolean
set_viewports (GTask *task)
{
MetaSeatImpl *seat_impl = g_task_get_source_object (task);
MetaViewportInfo *viewports = g_task_get_task_data (task);
SetViewportsData *data = g_task_get_task_data (task);
MetaViewportInfo *viewports = data->viewports;
g_set_object (&seat_impl->viewports, viewports);
g_task_return_boolean (task, TRUE);
ensure_pointer_onscreen (seat_impl);
g_mutex_lock (&data->mutex);
data->constrained = TRUE;
g_cond_signal (&data->cond);
g_mutex_unlock (&data->mutex);
return G_SOURCE_REMOVE;
}
@ -3641,15 +3655,28 @@ void
meta_seat_impl_set_viewports (MetaSeatImpl *seat_impl,
MetaViewportInfo *viewports)
{
SetViewportsData data = {};
GTask *task;
g_return_if_fail (META_IS_SEAT_IMPL (seat_impl));
data.viewports = viewports;
g_mutex_init (&data.mutex);
g_cond_init (&data.cond);
task = g_task_new (seat_impl, NULL, NULL, NULL);
g_task_set_task_data (task, g_object_ref (viewports), g_object_unref);
g_task_set_task_data (task, &data, NULL);
meta_seat_impl_run_input_task (seat_impl, task,
(GSourceFunc) set_viewports);
g_object_unref (task);
g_mutex_lock (&data.mutex);
while (!data.constrained)
g_cond_wait (&data.cond, &data.mutex);
g_mutex_unlock (&data.mutex);
g_mutex_clear (&data.mutex);
g_cond_clear (&data.cond);
}
MetaSeatImpl *