From 14931ed3917899049583defbe0225a857741347a Mon Sep 17 00:00:00 2001 From: Giovanni Campagna Date: Fri, 26 Jul 2013 16:19:44 +0200 Subject: [PATCH] MetaWayland: don't destroy the output list, emit the right events instead Destroying the output list causes toolkits to believe that all monitors were unplugged, which then causes assertions in code that have outside knowledge of this not having happened (like in code using GnomeRR DBus API). --- src/wayland/meta-wayland-private.h | 2 +- src/wayland/meta-wayland.c | 136 ++++++++++++++++++++++++++--- 2 files changed, 124 insertions(+), 14 deletions(-) diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h index 65674ffe7..9f7dade5d 100644 --- a/src/wayland/meta-wayland-private.h +++ b/src/wayland/meta-wayland-private.h @@ -130,7 +130,7 @@ typedef struct struct _MetaWaylandCompositor { - GList *outputs; + GHashTable *outputs; struct wl_display *wayland_display; struct wl_event_loop *wayland_loop; diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c index d631084b3..52451e2fe 100644 --- a/src/wayland/meta-wayland.c +++ b/src/wayland/meta-wayland.c @@ -630,16 +630,40 @@ meta_wayland_compositor_create_region (struct wl_client *wayland_client, region->region = cairo_region_create (); } +typedef struct { + MetaOutput *output; + struct wl_global *global; + int x, y; + enum wl_output_transform transform; + + GList *resources; +} MetaWaylandOutput; + +static void +output_resource_destroy (struct wl_resource *res) +{ + MetaWaylandOutput *wayland_output; + + wayland_output = wl_resource_get_user_data (res); + wayland_output->resources = g_list_remove (wayland_output->resources, res); +} + static void bind_output (struct wl_client *client, void *data, guint32 version, guint32 id) { - MetaOutput *output = data; + MetaWaylandOutput *wayland_output = data; + MetaOutput *output = wayland_output->output; struct wl_resource *resource; + guint mode_flags; resource = wl_resource_create (client, &wl_output_interface, version, id); + wayland_output->resources = g_list_prepend (wayland_output->resources, resource); + + wl_resource_set_user_data (resource, wayland_output); + wl_resource_set_destructor (resource, output_resource_destroy); meta_verbose ("Binding output %p/%s (%u, %u, %u, %u) x %f\n", output, output->name, @@ -660,9 +684,13 @@ bind_output (struct wl_client *client, output->product, output->crtc->transform); + mode_flags = WL_OUTPUT_MODE_CURRENT; + if (output->crtc->current_mode == output->preferred_mode) + mode_flags |= WL_OUTPUT_MODE_PREFERRED; + wl_resource_post_event (resource, WL_OUTPUT_MODE, - WL_OUTPUT_MODE_PREFERRED | WL_OUTPUT_MODE_CURRENT, + mode_flags, (int)output->crtc->rect.width, (int)output->crtc->rect.height, (int)output->crtc->current_mode->refresh_rate); @@ -673,29 +701,111 @@ bind_output (struct wl_client *client, } static void -meta_wayland_compositor_create_outputs (MetaWaylandCompositor *compositor, +wayland_output_destroy_notify (gpointer data) +{ + MetaWaylandOutput *wayland_output = data; + GList *resources; + + /* Make sure the destructors don't mess with the list */ + resources = wayland_output->resources; + wayland_output->resources = NULL; + + wl_global_destroy (wayland_output->global); + g_list_free (resources); + + g_slice_free (MetaWaylandOutput, wayland_output); +} + +static void +wayland_output_update_for_output (MetaWaylandOutput *wayland_output, + MetaOutput *output) +{ + GList *iter; + guint mode_flags; + + mode_flags = WL_OUTPUT_MODE_CURRENT; + if (output->crtc->current_mode == output->preferred_mode) + mode_flags |= WL_OUTPUT_MODE_PREFERRED; + + for (iter = wayland_output->resources; iter; iter = iter->next) + { + struct wl_resource *resource = iter->data; + + if (wayland_output->x != output->crtc->rect.x || + wayland_output->y != output->crtc->rect.y || + wayland_output->transform != output->crtc->transform) + { + wl_resource_post_event (resource, + WL_OUTPUT_GEOMETRY, + (int)output->crtc->rect.x, + (int)output->crtc->rect.y, + output->width_mm, + output->height_mm, + output->subpixel_order, + output->vendor, + output->product, + output->crtc->transform); + } + + wl_resource_post_event (resource, + WL_OUTPUT_MODE, + mode_flags, + (int)output->crtc->rect.width, + (int)output->crtc->rect.height, + (int)output->crtc->current_mode->refresh_rate); + } + + /* It's very important that we change the output pointer here, as + the old structure is about to be freed by MetaMonitorManager */ + wayland_output->output = output; + wayland_output->x = output->crtc->rect.x; + wayland_output->y = output->crtc->rect.y; + wayland_output->transform = output->crtc->transform; +} + +static GHashTable * +meta_wayland_compositor_update_outputs (MetaWaylandCompositor *compositor, MetaMonitorManager *monitors) { MetaOutput *outputs; unsigned int i, n_outputs; + GHashTable *new_table; outputs = meta_monitor_manager_get_outputs (monitors, &n_outputs); + new_table = g_hash_table_new_full (NULL, NULL, NULL, wayland_output_destroy_notify); for (i = 0; i < n_outputs; i++) { MetaOutput *output = &outputs[i]; - struct wl_global *global; + MetaWaylandOutput *wayland_output; /* wayland does not expose disabled outputs */ if (output->crtc == NULL) - continue; + { + g_hash_table_remove (compositor->outputs, GSIZE_TO_POINTER (output->output_id)); + continue; + } - global = wl_global_create (compositor->wayland_display, - &wl_output_interface, 2, - output, bind_output); - compositor->outputs = g_list_prepend (compositor->outputs, global); + wayland_output = g_hash_table_lookup (compositor->outputs, GSIZE_TO_POINTER (output->output_id)); + + if (wayland_output) + { + g_hash_table_steal (compositor->outputs, GSIZE_TO_POINTER (output->output_id)); + } + else + { + wayland_output = g_slice_new0 (MetaWaylandOutput); + wayland_output->global = wl_global_create (compositor->wayland_display, + &wl_output_interface, 2, + wayland_output, bind_output); + } + + wayland_output_update_for_output (wayland_output, output); + g_hash_table_insert (new_table, GSIZE_TO_POINTER (output->output_id), wayland_output); } + g_hash_table_destroy (compositor->outputs); + return new_table; } const static struct wl_compositor_interface meta_wayland_compositor_interface = { @@ -1438,9 +1548,7 @@ static void on_monitors_changed (MetaMonitorManager *monitors, MetaWaylandCompositor *compositor) { - g_list_free_full (compositor->outputs, (GDestroyNotify) wl_global_destroy); - compositor->outputs = NULL; - meta_wayland_compositor_create_outputs (compositor, monitors); + compositor->outputs = meta_wayland_compositor_update_outputs (compositor, monitors); } void @@ -1553,7 +1661,9 @@ meta_wayland_init (void) monitors = meta_monitor_manager_get (); g_signal_connect (monitors, "monitors-changed", G_CALLBACK (on_monitors_changed), compositor); - meta_wayland_compositor_create_outputs (compositor, monitors); + + compositor->outputs = g_hash_table_new_full (NULL, NULL, NULL, wayland_output_destroy_notify); + compositor->outputs = meta_wayland_compositor_update_outputs (compositor, monitors); compositor->stage = meta_wayland_stage_new ();