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).
This commit is contained in:
Giovanni Campagna 2013-07-26 16:19:44 +02:00
parent c06762518e
commit 14931ed391
2 changed files with 124 additions and 14 deletions

View File

@ -130,7 +130,7 @@ typedef struct
struct _MetaWaylandCompositor
{
GList *outputs;
GHashTable *outputs;
struct wl_display *wayland_display;
struct wl_event_loop *wayland_loop;

View File

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