From 696b5345704cb4ee6394637ed76d2373fa9f8530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Mon, 9 Mar 2020 12:33:17 +0100 Subject: [PATCH] wayland/surface: Send enter event when a client binds to wl_output late When hotplugging a new monitor, we recreate all the MetaWaylandOutputs and need to emit leave events to the surfaces for the old wl_outputs and enter events for the newly created ones. There's a race condition though: We might update the monitors a surface is on (and thus emit enter/leave events for the wl_outputs) before the Wayland client is registered with the new wl_output (ie. the bind_output() callback of MetaWaylandOutput was called), which means we don't send an enter event to the client in surface_entered_output(). Since MetaWaylandSurface now has the MetaWaylandOutput in its outputs hashtable, it thinks the client has been notified and won't send any more enter events. To fix that, make MetaWaylandOutput emit a new signal "output-bound" when a client bound to the output and make all surfaces which are on that output listen to the signal. In the signal handler compare the newly added client to the client the surface belongs to, and if it's the same one, send an enter event to that client. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1230 --- src/wayland/meta-wayland-outputs.c | 10 +++++++++ src/wayland/meta-wayland-surface.c | 33 +++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/wayland/meta-wayland-outputs.c b/src/wayland/meta-wayland-outputs.c index aed66f30c..75c29ab00 100644 --- a/src/wayland/meta-wayland-outputs.c +++ b/src/wayland/meta-wayland-outputs.c @@ -41,6 +41,7 @@ enum { OUTPUT_DESTROYED, + OUTPUT_BOUND, LAST_SIGNAL }; @@ -339,6 +340,7 @@ bind_output (struct wl_client *client, send_output_events (resource, wayland_output, logical_monitor, TRUE, NULL); + g_signal_emit (wayland_output, signals[OUTPUT_BOUND], 0, resource); } static void @@ -558,6 +560,14 @@ meta_wayland_output_class_init (MetaWaylandOutputClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + + signals[OUTPUT_BOUND] = g_signal_new ("output-bound", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_POINTER); } static void diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c index fa84944bd..5ea64b12e 100644 --- a/src/wayland/meta-wayland-surface.c +++ b/src/wayland/meta-wayland-surface.c @@ -1162,6 +1162,16 @@ static const struct wl_surface_interface meta_wayland_wl_surface_interface = { wl_surface_damage_buffer, }; +static void +handle_output_bound (MetaWaylandOutput *wayland_output, + struct wl_resource *output_resource, + MetaWaylandSurface *surface) +{ + if (wl_resource_get_client (output_resource) == + wl_resource_get_client (surface->resource)) + wl_surface_send_enter (surface->resource, output_resource); +} + static void surface_entered_output (MetaWaylandSurface *surface, MetaWaylandOutput *wayland_output) @@ -1179,6 +1189,10 @@ surface_entered_output (MetaWaylandSurface *surface, wl_surface_send_enter (surface->resource, resource); } + + g_signal_connect (wayland_output, "output-bound", + G_CALLBACK (handle_output_bound), + surface); } static void @@ -1188,6 +1202,10 @@ surface_left_output (MetaWaylandSurface *surface, GList *iter; struct wl_resource *resource; + g_signal_handlers_disconnect_by_func (wayland_output, + G_CALLBACK (handle_output_bound), + surface); + for (iter = wayland_output->resources; iter != NULL; iter = iter->next) { resource = iter->data; @@ -1265,9 +1283,18 @@ update_surface_output_state (gpointer key, gpointer value, gpointer user_data) } static void -surface_output_disconnect_signal (gpointer key, gpointer value, gpointer user_data) +surface_output_disconnect_signals (gpointer key, + gpointer value, + gpointer user_data) { - g_signal_handler_disconnect (key, (gulong) GPOINTER_TO_SIZE (value)); + MetaWaylandOutput *wayland_output = key; + MetaWaylandSurface *surface = user_data; + + g_signal_handler_disconnect (wayland_output, + (gulong) GPOINTER_TO_SIZE (value)); + g_signal_handlers_disconnect_by_func (wayland_output, + G_CALLBACK (handle_output_bound), + surface); } void @@ -1338,7 +1365,7 @@ wl_surface_destructor (struct wl_resource *resource) meta_wayland_compositor_destroy_frame_callbacks (compositor, surface); g_hash_table_foreach (surface->outputs_to_destroy_notify_id, - surface_output_disconnect_signal, + surface_output_disconnect_signals, surface); g_hash_table_unref (surface->outputs_to_destroy_notify_id);