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
This commit is contained in:
Jonas Dreßler 2020-03-09 12:33:17 +01:00 committed by verdre
parent 38db4a5a65
commit 696b534570
2 changed files with 40 additions and 3 deletions

View File

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

View File

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