[mutter] output: Move output info to MetaOutputInfo struct



commit 1406348be4c7cdbbc1f0481c120cad976ecad2a3
Author: Jonas Ã…dahl <jadahl gmail com>
Date:   Wed Feb 26 09:45:07 2020 +0100

    output: Move output info to MetaOutputInfo struct
    
    The output info is established during construction and will stay the
    same for the lifetime of the MetaOutput object. Moving it out of the
    main struct enables us to eventually clean up the MetaOutput type
    inheritence to use proper GObject types.
    
    https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1287

 src/backends/meta-gpu.c                    |   3 +-
 src/backends/meta-monitor-config-manager.c |  11 +-
 src/backends/meta-monitor-manager-dummy.c  | 111 ++++++-----
 src/backends/meta-monitor-manager.c        | 143 ++++++++------
 src/backends/meta-monitor.c                | 231 +++++++++++++----------
 src/backends/meta-output.c                 |  99 ++++++++--
 src/backends/meta-output.h                 |  39 +++-
 src/backends/native/meta-gpu-kms.c         |  16 +-
 src/backends/native/meta-output-kms.c      | 136 ++++++-------
 src/backends/x11/meta-gpu-xrandr.c         |  14 +-
 src/backends/x11/meta-output-xrandr.c      | 294 ++++++++++++++++-------------
 src/tests/headless-start-test.c            |  26 ++-
 src/tests/monitor-test-utils.c             |  70 +++----
 13 files changed, 705 insertions(+), 488 deletions(-)
---
diff --git a/src/backends/meta-gpu.c b/src/backends/meta-gpu.c
index cc67fa718a..ce4353bf01 100644
--- a/src/backends/meta-gpu.c
+++ b/src/backends/meta-gpu.c
@@ -57,8 +57,9 @@ meta_gpu_has_hotplug_mode_update (MetaGpu *gpu)
   for (l = priv->outputs; l; l = l->next)
     {
       MetaOutput *output = l->data;
+      const MetaOutputInfo *output_info = meta_output_get_info (output);
 
-      if (output->hotplug_mode_update)
+      if (output_info->hotplug_mode_update)
         return TRUE;
     }
 
diff --git a/src/backends/meta-monitor-config-manager.c b/src/backends/meta-monitor-config-manager.c
index cb11eba71a..55053276b7 100644
--- a/src/backends/meta-monitor-config-manager.c
+++ b/src/backends/meta-monitor-config-manager.c
@@ -118,16 +118,19 @@ find_unassigned_crtc (MetaOutput *output,
                       GArray     *reserved_crtcs)
 {
   MetaCrtc *crtc;
+  const MetaOutputInfo *output_info;
   unsigned int i;
 
   crtc = meta_output_get_assigned_crtc (output);
   if (crtc && !is_crtc_assigned (crtc, crtc_assignments))
     return crtc;
 
+  output_info = meta_output_get_info (output);
+
   /* then try to assign a CRTC that wasn't used */
-  for (i = 0; i < output->n_possible_crtcs; i++)
+  for (i = 0; i < output_info->n_possible_crtcs; i++)
     {
-      crtc = output->possible_crtcs[i];
+      crtc = output_info->possible_crtcs[i];
 
       if (is_crtc_assigned (crtc, crtc_assignments))
         continue;
@@ -139,9 +142,9 @@ find_unassigned_crtc (MetaOutput *output,
     }
 
   /* finally just give a CRTC that we haven't assigned */
-  for (i = 0; i < output->n_possible_crtcs; i++)
+  for (i = 0; i < output_info->n_possible_crtcs; i++)
     {
-      crtc = output->possible_crtcs[i];
+      crtc = output_info->possible_crtcs[i];
 
       if (is_crtc_assigned (crtc, crtc_assignments))
         continue;
diff --git a/src/backends/meta-monitor-manager-dummy.c b/src/backends/meta-monitor-manager-dummy.c
index d7f07b012f..6547f9dcdf 100644
--- a/src/backends/meta-monitor-manager-dummy.c
+++ b/src/backends/meta-monitor-manager-dummy.c
@@ -142,6 +142,7 @@ append_monitor (MetaMonitorManager *manager,
   MetaOutput *output;
   unsigned int i;
   unsigned int number;
+  g_autoptr (MetaOutputInfo) output_info = NULL;
   const char *mode_specs_str;
   GList *l;
 
@@ -207,44 +208,46 @@ append_monitor (MetaMonitorManager *manager,
 
   number = g_list_length (*outputs) + 1;
 
+  output_info = meta_output_info_new ();
+  output_info->name = g_strdup_printf ("LVDS%d", number);
+  output_info->vendor = g_strdup ("MetaProducts Inc.");
+  output_info->product = g_strdup ("MetaMonitor");
+  output_info->serial = g_strdup_printf ("0xC0FFEE-%d", number);
+  output_info->suggested_x = -1;
+  output_info->suggested_y = -1;
+  output_info->width_mm = 222;
+  output_info->height_mm = 125;
+  output_info->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
+  output_info->preferred_mode = g_list_last (*modes)->data;
+  output_info->n_possible_clones = 0;
+  output_info->connector_type = META_CONNECTOR_TYPE_LVDS;
+
+  output_info->modes = g_new0 (MetaCrtcMode *, n_mode_specs);
+  for (l = new_modes, i = 0; l; l = l->next, i++)
+    {
+      MetaCrtcMode *mode = l->data;
+
+      output_info->modes[i] = mode;
+    }
+  output_info->n_modes = n_mode_specs;
+  output_info->possible_crtcs = g_new0 (MetaCrtc *, 1);
+  output_info->possible_crtcs[0] = g_list_last (*crtcs)->data;
+  output_info->n_possible_crtcs = 1;
+
   output = g_object_new (META_TYPE_OUTPUT,
                          "id", number,
                          "gpu", gpu,
+                         "info", output_info,
                          NULL);
 
   output_dummy = g_new0 (MetaOutputDummy, 1);
   *output_dummy = (MetaOutputDummy) {
     .scale = scale
   };
-
-  output->name = g_strdup_printf ("LVDS%d", number);
-  output->vendor = g_strdup ("MetaProducts Inc.");
-  output->product = g_strdup ("MetaMonitor");
-  output->serial = g_strdup_printf ("0xC0FFEE-%d", number);
-  output->suggested_x = -1;
-  output->suggested_y = -1;
-  output->width_mm = 222;
-  output->height_mm = 125;
-  output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
-  output->preferred_mode = g_list_last (*modes)->data;
-  output->n_possible_clones = 0;
-  output->connector_type = META_CONNECTOR_TYPE_LVDS;
   output->driver_private = output_dummy;
   output->driver_notify =
     (GDestroyNotify) meta_output_dummy_notify_destroy;
 
-  output->modes = g_new0 (MetaCrtcMode *, n_mode_specs);
-  for (l = new_modes, i = 0; l; l = l->next, i++)
-    {
-      MetaCrtcMode *mode = l->data;
-
-      output->modes[i] = mode;
-    }
-  output->n_modes = n_mode_specs;
-  output->possible_crtcs = g_new0 (MetaCrtc *, 1);
-  output->possible_crtcs[0] = g_list_last (*crtcs)->data;
-  output->n_possible_crtcs = 1;
-
   *outputs = g_list_append (*outputs, output);
 }
 
@@ -307,6 +310,7 @@ append_tiled_monitor (MetaMonitorManager *manager,
       MetaCrtcMode *preferred_mode;
       unsigned int j;
       unsigned int number;
+      g_autoptr (MetaOutputInfo) output_info = NULL;
       GList *l;
 
       output_dummy = g_new0 (MetaOutputDummy, 1);
@@ -319,24 +323,21 @@ append_tiled_monitor (MetaMonitorManager *manager,
 
       preferred_mode = g_list_last (*modes)->data;
 
-      output = g_object_new (META_TYPE_OUTPUT,
-                             "id", number,
-                             "gpu", gpu,
-                             NULL);
-
-      output->name = g_strdup_printf ("LVDS%d", number);
-      output->vendor = g_strdup ("MetaProducts Inc.");
-      output->product = g_strdup ("MetaMonitor");
-      output->serial = g_strdup_printf ("0xC0FFEE-%d", number);
-      output->suggested_x = -1;
-      output->suggested_y = -1;
-      output->width_mm = 222;
-      output->height_mm = 125;
-      output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
-      output->preferred_mode = preferred_mode;
-      output->n_possible_clones = 0;
-      output->connector_type = META_CONNECTOR_TYPE_LVDS;
-      output->tile_info = (MetaTileInfo) {
+      output_info = meta_output_info_new ();
+
+      output_info->name = g_strdup_printf ("LVDS%d", number);
+      output_info->vendor = g_strdup ("MetaProducts Inc.");
+      output_info->product = g_strdup ("MetaMonitor");
+      output_info->serial = g_strdup_printf ("0xC0FFEE-%d", number);
+      output_info->suggested_x = -1;
+      output_info->suggested_y = -1;
+      output_info->width_mm = 222;
+      output_info->height_mm = 125;
+      output_info->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
+      output_info->preferred_mode = preferred_mode;
+      output_info->n_possible_clones = 0;
+      output_info->connector_type = META_CONNECTOR_TYPE_LVDS;
+      output_info->tile_info = (MetaTileInfo) {
         .group_id = tile_group_id,
         .max_h_tiles = n_tiles,
         .max_v_tiles = 1,
@@ -345,27 +346,33 @@ append_tiled_monitor (MetaMonitorManager *manager,
         .tile_w = preferred_mode->width,
         .tile_h = preferred_mode->height
       },
-      output->driver_private = output_dummy;
-      output->driver_notify =
-        (GDestroyNotify) meta_output_dummy_notify_destroy;
 
-      output->modes = g_new0 (MetaCrtcMode *, G_N_ELEMENTS (mode_specs));
+      output_info->modes = g_new0 (MetaCrtcMode *, G_N_ELEMENTS (mode_specs));
       for (l = new_modes, j = 0; l; l = l->next, j++)
         {
           MetaCrtcMode *mode = l->data;
 
-          output->modes[j] = mode;
+          output_info->modes[j] = mode;
         }
-      output->n_modes = G_N_ELEMENTS (mode_specs);
+      output_info->n_modes = G_N_ELEMENTS (mode_specs);
 
-      output->possible_crtcs = g_new0 (MetaCrtc *, n_tiles);
+      output_info->possible_crtcs = g_new0 (MetaCrtc *, n_tiles);
       for (l = new_crtcs, j = 0; l; l = l->next, j++)
         {
           MetaCrtc *crtc = l->data;
 
-          output->possible_crtcs[j] = crtc;
+          output_info->possible_crtcs[j] = crtc;
         }
-      output->n_possible_crtcs = n_tiles;
+      output_info->n_possible_crtcs = n_tiles;
+
+      output = g_object_new (META_TYPE_OUTPUT,
+                             "id", number,
+                             "gpu", gpu,
+                             "info", output_info,
+                             NULL);
+      output->driver_private = output_dummy;
+      output->driver_notify =
+        (GDestroyNotify) meta_output_dummy_notify_destroy;
 
       *outputs = g_list_append (*outputs, output);
     }
diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c
index aba071f259..d76e4348b2 100644
--- a/src/backends/meta-monitor-manager.c
+++ b/src/backends/meta-monitor-manager.c
@@ -140,7 +140,10 @@ meta_monitor_manager_set_primary_logical_monitor (MetaMonitorManager *manager,
 static gboolean
 is_main_tiled_monitor_output (MetaOutput *output)
 {
-  return output->tile_info.loc_h_tile == 0 && output->tile_info.loc_v_tile == 0;
+  const MetaOutputInfo *output_info = meta_output_get_info (output);
+
+  return (output_info->tile_info.loc_h_tile == 0 &&
+          output_info->tile_info.loc_v_tile == 0);
 }
 
 static MetaLogicalMonitor *
@@ -1059,15 +1062,23 @@ meta_monitor_manager_handle_get_resources (MetaDBusDisplayConfig *skeleton,
   for (l = combined_outputs, i = 0; l; l = l->next, i++)
     {
       MetaOutput *output = l->data;
+      const MetaOutputInfo *output_info = meta_output_get_info (output);
       GVariantBuilder crtcs, modes, clones, properties;
       GBytes *edid;
       MetaCrtc *crtc;
       int crtc_index;
+      int backlight;
+      int min_backlight_step;
+      gboolean is_primary;
+      gboolean is_presentation;
+      const char * connector_type_name;
+      gboolean is_underscanning;
+      gboolean supports_underscanning;
 
       g_variant_builder_init (&crtcs, G_VARIANT_TYPE ("au"));
-      for (j = 0; j < output->n_possible_crtcs; j++)
+      for (j = 0; j < output_info->n_possible_crtcs; j++)
         {
-          MetaCrtc *possible_crtc = output->possible_crtcs[j];
+          MetaCrtc *possible_crtc = output_info->possible_crtcs[j];
           unsigned possible_crtc_index;
 
           possible_crtc_index = g_list_index (combined_crtcs, possible_crtc);
@@ -1075,52 +1086,62 @@ meta_monitor_manager_handle_get_resources (MetaDBusDisplayConfig *skeleton,
         }
 
       g_variant_builder_init (&modes, G_VARIANT_TYPE ("au"));
-      for (j = 0; j < output->n_modes; j++)
+      for (j = 0; j < output_info->n_modes; j++)
         {
           unsigned mode_index;
 
-          mode_index = g_list_index (combined_modes, output->modes[j]);
+          mode_index = g_list_index (combined_modes, output_info->modes[j]);
           g_variant_builder_add (&modes, "u", mode_index);
         }
 
       g_variant_builder_init (&clones, G_VARIANT_TYPE ("au"));
-      for (j = 0; j < output->n_possible_clones; j++)
+      for (j = 0; j < output_info->n_possible_clones; j++)
         {
           unsigned int possible_clone_index;
 
           possible_clone_index = g_list_index (combined_outputs,
-                                               output->possible_clones[j]);
+                                               output_info->possible_clones[j]);
           g_variant_builder_add (&clones, "u", possible_clone_index);
         }
 
+      backlight = meta_output_get_backlight (output);
+      min_backlight_step =
+        output_info->backlight_max - output_info->backlight_min
+        ? 100 / (output_info->backlight_max - output_info->backlight_min)
+        : -1;
+      is_primary = meta_output_is_primary (output);
+      is_presentation = meta_output_is_presentation (output);
+      is_underscanning = meta_output_is_underscanning (output);
+      connector_type_name = get_connector_type_name (output_info->connector_type);
+      supports_underscanning = output_info->supports_underscanning;
+
       g_variant_builder_init (&properties, G_VARIANT_TYPE ("a{sv}"));
       g_variant_builder_add (&properties, "{sv}", "vendor",
-                             g_variant_new_string (output->vendor));
+                             g_variant_new_string (output_info->vendor));
       g_variant_builder_add (&properties, "{sv}", "product",
-                             g_variant_new_string (output->product));
+                             g_variant_new_string (output_info->product));
       g_variant_builder_add (&properties, "{sv}", "serial",
-                             g_variant_new_string (output->serial));
+                             g_variant_new_string (output_info->serial));
       g_variant_builder_add (&properties, "{sv}", "width-mm",
-                             g_variant_new_int32 (output->width_mm));
+                             g_variant_new_int32 (output_info->width_mm));
       g_variant_builder_add (&properties, "{sv}", "height-mm",
-                             g_variant_new_int32 (output->height_mm));
+                             g_variant_new_int32 (output_info->height_mm));
       g_variant_builder_add (&properties, "{sv}", "display-name",
-                             g_variant_new_string (output->name));
+                             g_variant_new_string (output_info->name));
       g_variant_builder_add (&properties, "{sv}", "backlight",
-                             g_variant_new_int32 (meta_output_get_backlight (output)));
+                             g_variant_new_int32 (backlight));
       g_variant_builder_add (&properties, "{sv}", "min-backlight-step",
-                             g_variant_new_int32 ((output->backlight_max - output->backlight_min) ?
-                                                  100 / (output->backlight_max - output->backlight_min) : 
-1));
+                             g_variant_new_int32 (min_backlight_step));
       g_variant_builder_add (&properties, "{sv}", "primary",
-                             g_variant_new_boolean (meta_output_is_primary (output)));
+                             g_variant_new_boolean (is_primary));
       g_variant_builder_add (&properties, "{sv}", "presentation",
-                             g_variant_new_boolean (meta_output_is_presentation (output)));
+                             g_variant_new_boolean (is_presentation));
       g_variant_builder_add (&properties, "{sv}", "connector-type",
-                             g_variant_new_string (get_connector_type_name (output->connector_type)));
+                             g_variant_new_string (connector_type_name));
       g_variant_builder_add (&properties, "{sv}", "underscanning",
-                             g_variant_new_boolean (meta_output_is_underscanning (output)));
+                             g_variant_new_boolean (is_underscanning));
       g_variant_builder_add (&properties, "{sv}", "supports-underscanning",
-                             g_variant_new_boolean (output->supports_underscanning));
+                             g_variant_new_boolean (supports_underscanning));
 
       edid = manager_class->read_edid (manager, output);
       if (edid)
@@ -1131,18 +1152,20 @@ meta_monitor_manager_handle_get_resources (MetaDBusDisplayConfig *skeleton,
           g_bytes_unref (edid);
         }
 
-      if (output->tile_info.group_id)
+      if (output_info->tile_info.group_id)
         {
-          g_variant_builder_add (&properties, "{sv}", "tile",
-                                 g_variant_new ("(uuuuuuuu)",
-                                                output->tile_info.group_id,
-                                                output->tile_info.flags,
-                                                output->tile_info.max_h_tiles,
-                                                output->tile_info.max_v_tiles,
-                                                output->tile_info.loc_h_tile,
-                                                output->tile_info.loc_v_tile,
-                                                output->tile_info.tile_w,
-                                                output->tile_info.tile_h));
+          GVariant *tile_variant;
+
+          tile_variant = g_variant_new ("(uuuuuuuu)",
+                                        output_info->tile_info.group_id,
+                                        output_info->tile_info.flags,
+                                        output_info->tile_info.max_h_tiles,
+                                        output_info->tile_info.max_v_tiles,
+                                        output_info->tile_info.loc_h_tile,
+                                        output_info->tile_info.loc_v_tile,
+                                        output_info->tile_info.tile_w,
+                                        output_info->tile_info.tile_h);
+          g_variant_builder_add (&properties, "{sv}", "tile", tile_variant);
         }
 
       crtc = meta_output_get_assigned_crtc (output);
@@ -1152,7 +1175,7 @@ meta_monitor_manager_handle_get_resources (MetaDBusDisplayConfig *skeleton,
                              meta_output_get_id (output),
                              crtc_index,
                              &crtcs,
-                             output->name,
+                             meta_output_get_name (output),
                              &modes,
                              &clones,
                              &properties);
@@ -2103,6 +2126,7 @@ meta_monitor_manager_handle_change_backlight  (MetaDBusDisplayConfig *skeleton,
 {
   GList *combined_outputs;
   MetaOutput *output;
+  const MetaOutputInfo *output_info;
   int new_backlight;
 
   if (serial != manager->serial)
@@ -2134,8 +2158,10 @@ meta_monitor_manager_handle_change_backlight  (MetaDBusDisplayConfig *skeleton,
       return TRUE;
     }
 
+  output_info = meta_output_get_info (output);
   if (meta_output_get_backlight (output) == -1 ||
-      (output->backlight_min == 0 && output->backlight_max == 0))
+      (output_info->backlight_min == 0 &&
+       output_info->backlight_max == 0))
     {
       g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
                                              G_DBUS_ERROR_INVALID_ARGS,
@@ -2653,8 +2679,9 @@ rebuild_monitors (MetaMonitorManager *manager)
       for (k = meta_gpu_get_outputs (gpu); k; k = k->next)
         {
           MetaOutput *output = k->data;
+          const MetaOutputInfo *output_info = meta_output_get_info (output);
 
-          if (output->tile_info.group_id)
+          if (output_info->tile_info.group_id)
             {
               if (is_main_tiled_monitor_output (output))
                 {
@@ -2886,8 +2913,8 @@ meta_monitor_manager_rebuild_derived (MetaMonitorManager *manager,
 }
 
 void
-meta_output_parse_edid (MetaOutput *output,
-                        GBytes     *edid)
+meta_output_info_parse_edid (MetaOutputInfo *output_info,
+                             GBytes         *edid)
 {
   MonitorInfo *parsed_edid;
   gsize len;
@@ -2899,42 +2926,44 @@ meta_output_parse_edid (MetaOutput *output,
 
   if (parsed_edid)
     {
-      output->vendor = g_strndup (parsed_edid->manufacturer_code, 4);
-      if (!g_utf8_validate (output->vendor, -1, NULL))
-        g_clear_pointer (&output->vendor, g_free);
+      output_info->vendor = g_strndup (parsed_edid->manufacturer_code, 4);
+      if (!g_utf8_validate (output_info->vendor, -1, NULL))
+        g_clear_pointer (&output_info->vendor, g_free);
 
-      output->product = g_strndup (parsed_edid->dsc_product_name, 14);
-      if (!g_utf8_validate (output->product, -1, NULL) ||
-          output->product[0] == '\0')
+      output_info->product = g_strndup (parsed_edid->dsc_product_name, 14);
+      if (!g_utf8_validate (output_info->product, -1, NULL) ||
+          output_info->product[0] == '\0')
         {
-          g_clear_pointer (&output->product, g_free);
-          output->product = g_strdup_printf ("0x%04x", (unsigned) parsed_edid->product_code);
+          g_clear_pointer (&output_info->product, g_free);
+          output_info->product = g_strdup_printf ("0x%04x", (unsigned) parsed_edid->product_code);
         }
 
-      output->serial = g_strndup (parsed_edid->dsc_serial_number, 14);
-      if (!g_utf8_validate (output->serial, -1, NULL) ||
-          output->serial[0] == '\0')
+      output_info->serial = g_strndup (parsed_edid->dsc_serial_number, 14);
+      if (!g_utf8_validate (output_info->serial, -1, NULL) ||
+          output_info->serial[0] == '\0')
         {
-          g_clear_pointer (&output->serial, g_free);
-          output->serial = g_strdup_printf ("0x%08x", parsed_edid->serial_number);
+          g_clear_pointer (&output_info->serial, g_free);
+          output_info->serial = g_strdup_printf ("0x%08x", parsed_edid->serial_number);
         }
 
       g_free (parsed_edid);
     }
 
  out:
-  if (!output->vendor)
-    output->vendor = g_strdup ("unknown");
-  if (!output->product)
-    output->product = g_strdup ("unknown");
-  if (!output->serial)
-    output->serial = g_strdup ("unknown");
+  if (!output_info->vendor)
+    output_info->vendor = g_strdup ("unknown");
+  if (!output_info->product)
+    output_info->product = g_strdup ("unknown");
+  if (!output_info->serial)
+    output_info->serial = g_strdup ("unknown");
 }
 
 gboolean
 meta_output_is_laptop (MetaOutput *output)
 {
-  switch (output->connector_type)
+  const MetaOutputInfo *output_info = meta_output_get_info (output);
+
+  switch (output_info->connector_type)
     {
     case META_CONNECTOR_TYPE_eDP:
     case META_CONNECTOR_TYPE_LVDS:
diff --git a/src/backends/meta-monitor.c b/src/backends/meta-monitor.c
index c6817d4708..5887b6e167 100644
--- a/src/backends/meta-monitor.c
+++ b/src/backends/meta-monitor.c
@@ -168,19 +168,28 @@ meta_monitor_spec_free (MetaMonitorSpec *monitor_spec)
   g_free (monitor_spec);
 }
 
+static const MetaOutputInfo *
+meta_monitor_get_main_output_info (MetaMonitor *monitor)
+{
+  MetaOutput *output = meta_monitor_get_main_output (monitor);
+
+  return meta_output_get_info (output);
+}
+
 static void
 meta_monitor_generate_spec (MetaMonitor *monitor)
 {
   MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
-  MetaOutput *output = meta_monitor_get_main_output (monitor);
+  const MetaOutputInfo *output_info =
+    meta_monitor_get_main_output_info (monitor);
   MetaMonitorSpec *monitor_spec;
 
   monitor_spec = g_new0 (MetaMonitorSpec, 1);
   *monitor_spec = (MetaMonitorSpec) {
-    .connector = g_strdup (output->name),
-    .vendor = g_strdup (output->vendor),
-    .product = g_strdup (output->product),
-    .serial = g_strdup (output->serial),
+    .connector = g_strdup (output_info->name),
+    .vendor = g_strdup (output_info->vendor),
+    .product = g_strdup (output_info->product),
+    .serial = g_strdup (output_info->serial),
   };
 
   priv->spec = monitor_spec;
@@ -322,11 +331,10 @@ meta_monitor_is_primary (MetaMonitor *monitor)
 gboolean
 meta_monitor_supports_underscanning (MetaMonitor *monitor)
 {
-  MetaOutput *output;
-
-  output = meta_monitor_get_main_output (monitor);
+  const MetaOutputInfo *output_info =
+    meta_monitor_get_main_output_info (monitor);
 
-  return output->supports_underscanning;
+  return output_info->supports_underscanning;
 }
 
 gboolean
@@ -342,11 +350,10 @@ meta_monitor_is_underscanning (MetaMonitor *monitor)
 gboolean
 meta_monitor_is_laptop_panel (MetaMonitor *monitor)
 {
-  MetaOutput *output;
-
-  output = meta_monitor_get_main_output (monitor);
+  const MetaOutputInfo *output_info =
+    meta_monitor_get_main_output_info (monitor);
 
-  switch (output->connector_type)
+  switch (output_info->connector_type)
     {
     case META_CONNECTOR_TYPE_eDP:
     case META_CONNECTOR_TYPE_LVDS:
@@ -392,65 +399,65 @@ meta_monitor_get_physical_dimensions (MetaMonitor *monitor,
                                       int         *width_mm,
                                       int         *height_mm)
 {
-  MetaOutput *output;
+  const MetaOutputInfo *output_info =
+    meta_monitor_get_main_output_info (monitor);
 
-  output = meta_monitor_get_main_output (monitor);
-  *width_mm = output->width_mm;
-  *height_mm = output->height_mm;
+  *width_mm = output_info->width_mm;
+  *height_mm = output_info->height_mm;
 }
 
 CoglSubpixelOrder
 meta_monitor_get_subpixel_order (MetaMonitor *monitor)
 {
-  MetaOutput *output;
+  const MetaOutputInfo *output_info =
+    meta_monitor_get_main_output_info (monitor);
 
-  output = meta_monitor_get_main_output (monitor);
-  return output->subpixel_order;
+  return output_info->subpixel_order;
 }
 
 const char *
 meta_monitor_get_connector (MetaMonitor *monitor)
 {
-  MetaOutput *output;
+  const MetaOutputInfo *output_info =
+    meta_monitor_get_main_output_info (monitor);
 
-  output = meta_monitor_get_main_output (monitor);
-  return output->name;
+  return output_info->name;
 }
 
 const char *
 meta_monitor_get_vendor (MetaMonitor *monitor)
 {
-  MetaOutput *output;
+  const MetaOutputInfo *output_info =
+    meta_monitor_get_main_output_info (monitor);
 
-  output = meta_monitor_get_main_output (monitor);
-  return output->vendor;
+  return output_info->vendor;
 }
 
 const char *
 meta_monitor_get_product (MetaMonitor *monitor)
 {
-  MetaOutput *output;
+  const MetaOutputInfo *output_info =
+    meta_monitor_get_main_output_info (monitor);
 
-  output = meta_monitor_get_main_output (monitor);
-  return output->product;
+  return output_info->product;
 }
 
 const char *
 meta_monitor_get_serial (MetaMonitor *monitor)
 {
-  MetaOutput *output;
+  const MetaOutputInfo *output_info =
+    meta_monitor_get_main_output_info (monitor);
 
-  output = meta_monitor_get_main_output (monitor);
-  return output->serial;
+  return output_info->serial;
 }
 
 MetaConnectorType
 meta_monitor_get_connector_type (MetaMonitor *monitor)
 {
-  MetaOutput *output;
+  const MetaOutputInfo *output_info =
+    meta_monitor_get_main_output_info (monitor);
 
-  output = meta_monitor_get_main_output (monitor);
-  return output->connector_type;
+  return output_info->connector_type;
 }
 
 MetaMonitorTransform
@@ -562,9 +569,10 @@ meta_monitor_create_spec (MetaMonitor  *monitor,
                           int           height,
                           MetaCrtcMode *crtc_mode)
 {
-  MetaOutput *output = meta_monitor_get_main_output (monitor);
+  const MetaOutputInfo *output_info =
+    meta_monitor_get_main_output_info (monitor);
 
-  if (meta_monitor_transform_is_rotated (output->panel_orientation_transform))
+  if (meta_monitor_transform_is_rotated (output_info->panel_orientation_transform))
     {
       int temp = width;
       width = height;
@@ -586,15 +594,17 @@ meta_monitor_normal_generate_modes (MetaMonitorNormal *monitor_normal)
   MetaMonitorPrivate *monitor_priv =
     meta_monitor_get_instance_private (monitor);
   MetaOutput *output;
+  const MetaOutputInfo *output_info;
   MetaCrtcModeFlag preferred_mode_flags;
   unsigned int i;
 
   output = meta_monitor_get_main_output (monitor);
-  preferred_mode_flags = output->preferred_mode->flags;
+  output_info = meta_output_get_info (output);
+  preferred_mode_flags = output_info->preferred_mode->flags;
 
-  for (i = 0; i < output->n_modes; i++)
+  for (i = 0; i < output_info->n_modes; i++)
     {
-      MetaCrtcMode *crtc_mode = output->modes[i];
+      MetaCrtcMode *crtc_mode = output_info->modes[i];
       MetaCrtc *crtc;
       MetaMonitorMode *mode;
       gboolean replace;
@@ -623,12 +633,12 @@ meta_monitor_normal_generate_modes (MetaMonitorNormal *monitor_normal)
 
       if (!meta_monitor_add_mode (monitor, mode, replace))
         {
-          g_assert (crtc_mode != output->preferred_mode);
+          g_assert (crtc_mode != output_info->preferred_mode);
           meta_monitor_mode_free (mode);
           continue;
         }
 
-      if (crtc_mode == output->preferred_mode)
+      if (crtc_mode == output_info->preferred_mode)
         monitor_priv->preferred_mode = mode;
 
       crtc = meta_output_get_assigned_crtc (output);
@@ -697,14 +707,14 @@ meta_monitor_normal_get_suggested_position (MetaMonitor *monitor,
                                             int         *x,
                                             int         *y)
 {
-  MetaOutput *output;
+  const MetaOutputInfo *output_info =
+    meta_monitor_get_main_output_info (monitor);
 
-  output = meta_monitor_get_main_output (monitor);
-  if (output->suggested_x < 0 && output->suggested_y < 0)
+  if (output_info->suggested_x < 0 && output_info->suggested_y < 0)
     return FALSE;
 
-  *x = output->suggested_x;
-  *y = output->suggested_y;
+  *x = output_info->suggested_x;
+  *y = output_info->suggested_y;
 
   return TRUE;
 }
@@ -765,12 +775,15 @@ add_tiled_monitor_outputs (MetaGpu          *gpu,
   for (l = outputs; l; l = l->next)
     {
       MetaOutput *output = l->data;
+      const MetaOutputInfo *output_info = meta_output_get_info (output);
+      const MetaOutputInfo *origin_output_info;
 
-      if (output->tile_info.group_id != monitor_tiled->tile_group_id)
+      if (output_info->tile_info.group_id != monitor_tiled->tile_group_id)
         continue;
 
-      g_warn_if_fail (output->subpixel_order ==
-                      monitor_tiled->origin_output->subpixel_order);
+      origin_output_info = meta_output_get_info (monitor_tiled->origin_output);
+      g_warn_if_fail (output_info->subpixel_order ==
+                      origin_output_info->subpixel_order);
 
       monitor_priv->outputs = g_list_append (monitor_priv->outputs,
                                              g_object_ref (output));
@@ -786,51 +799,68 @@ calculate_tile_coordinate (MetaMonitor         *monitor,
 {
   MetaMonitorPrivate *monitor_priv =
     meta_monitor_get_instance_private (monitor);
+  const MetaOutputInfo *output_info = meta_output_get_info (output);
   GList *l;
   int x = 0;
   int y = 0;
 
   for (l = monitor_priv->outputs; l; l = l->next)
     {
-      MetaOutput *other_output = l->data;
+      const MetaOutputInfo *other_output_info = meta_output_get_info (l->data);
 
       switch (crtc_transform)
         {
         case META_MONITOR_TRANSFORM_NORMAL:
         case META_MONITOR_TRANSFORM_FLIPPED:
-          if (other_output->tile_info.loc_v_tile == output->tile_info.loc_v_tile &&
-              other_output->tile_info.loc_h_tile < output->tile_info.loc_h_tile)
-            x += other_output->tile_info.tile_w;
-          if (other_output->tile_info.loc_h_tile == output->tile_info.loc_h_tile &&
-              other_output->tile_info.loc_v_tile < output->tile_info.loc_v_tile)
-            y += other_output->tile_info.tile_h;
+          if ((other_output_info->tile_info.loc_v_tile ==
+               output_info->tile_info.loc_v_tile) &&
+              (other_output_info->tile_info.loc_h_tile <
+               output_info->tile_info.loc_h_tile))
+            x += other_output_info->tile_info.tile_w;
+          if ((other_output_info->tile_info.loc_h_tile ==
+               output_info->tile_info.loc_h_tile) &&
+              (other_output_info->tile_info.loc_v_tile <
+               output_info->tile_info.loc_v_tile))
+            y += other_output_info->tile_info.tile_h;
           break;
         case META_MONITOR_TRANSFORM_180:
         case META_MONITOR_TRANSFORM_FLIPPED_180:
-          if (other_output->tile_info.loc_v_tile == output->tile_info.loc_v_tile &&
-              other_output->tile_info.loc_h_tile > output->tile_info.loc_h_tile)
-            x += other_output->tile_info.tile_w;
-          if (other_output->tile_info.loc_h_tile == output->tile_info.loc_h_tile &&
-              other_output->tile_info.loc_v_tile > output->tile_info.loc_v_tile)
-            y += other_output->tile_info.tile_h;
+          if ((other_output_info->tile_info.loc_v_tile ==
+               output_info->tile_info.loc_v_tile) &&
+              (other_output_info->tile_info.loc_h_tile >
+               output_info->tile_info.loc_h_tile))
+            x += other_output_info->tile_info.tile_w;
+          if ((other_output_info->tile_info.loc_h_tile ==
+               output_info->tile_info.loc_h_tile) &&
+              (other_output_info->tile_info.loc_v_tile >
+               output_info->tile_info.loc_v_tile))
+            y += other_output_info->tile_info.tile_h;
           break;
         case META_MONITOR_TRANSFORM_270:
         case META_MONITOR_TRANSFORM_FLIPPED_270:
-          if (other_output->tile_info.loc_v_tile == output->tile_info.loc_v_tile &&
-              other_output->tile_info.loc_h_tile > output->tile_info.loc_h_tile)
-            y += other_output->tile_info.tile_w;
-          if (other_output->tile_info.loc_h_tile == output->tile_info.loc_h_tile &&
-              other_output->tile_info.loc_v_tile > output->tile_info.loc_v_tile)
-            x += other_output->tile_info.tile_h;
+          if ((other_output_info->tile_info.loc_v_tile ==
+               output_info->tile_info.loc_v_tile) &&
+              (other_output_info->tile_info.loc_h_tile >
+               output_info->tile_info.loc_h_tile))
+            y += other_output_info->tile_info.tile_w;
+          if ((other_output_info->tile_info.loc_h_tile ==
+               output_info->tile_info.loc_h_tile) &&
+              (other_output_info->tile_info.loc_v_tile >
+               output_info->tile_info.loc_v_tile))
+            x += other_output_info->tile_info.tile_h;
           break;
         case META_MONITOR_TRANSFORM_90:
         case META_MONITOR_TRANSFORM_FLIPPED_90:
-          if (other_output->tile_info.loc_v_tile == output->tile_info.loc_v_tile &&
-              other_output->tile_info.loc_h_tile < output->tile_info.loc_h_tile)
-            y += other_output->tile_info.tile_w;
-          if (other_output->tile_info.loc_h_tile == output->tile_info.loc_h_tile &&
-              other_output->tile_info.loc_v_tile < output->tile_info.loc_v_tile)
-            x += other_output->tile_info.tile_h;
+          if ((other_output_info->tile_info.loc_v_tile ==
+               output_info->tile_info.loc_v_tile) &&
+              (other_output_info->tile_info.loc_h_tile <
+               output_info->tile_info.loc_h_tile))
+            y += other_output_info->tile_info.tile_w;
+          if ((other_output_info->tile_info.loc_h_tile ==
+               output_info->tile_info.loc_h_tile) &&
+              (other_output_info->tile_info.loc_v_tile <
+               output_info->tile_info.loc_v_tile))
+            x += other_output_info->tile_info.tile_h;
           break;
         }
     }
@@ -854,13 +884,13 @@ meta_monitor_tiled_calculate_tiled_size (MetaMonitor *monitor,
   height = 0;
   for (l = monitor_priv->outputs; l; l = l->next)
     {
-      MetaOutput *output = l->data;
+      const MetaOutputInfo *output_info = meta_output_get_info (l->data);
 
-      if (output->tile_info.loc_v_tile == 0)
-        width += output->tile_info.tile_w;
+      if (output_info->tile_info.loc_v_tile == 0)
+        width += output_info->tile_info.tile_w;
 
-      if (output->tile_info.loc_h_tile == 0)
-        height += output->tile_info.tile_h;
+      if (output_info->tile_info.loc_h_tile == 0)
+        height += output_info->tile_info.tile_h;
     }
 
   *out_width = width;
@@ -897,24 +927,27 @@ static gboolean
 is_crtc_mode_tiled (MetaOutput   *output,
                     MetaCrtcMode *crtc_mode)
 {
-  return (crtc_mode->width == (int) output->tile_info.tile_w &&
-          crtc_mode->height == (int) output->tile_info.tile_h);
+  const MetaOutputInfo *output_info = meta_output_get_info (output);
+
+  return (crtc_mode->width == (int) output_info->tile_info.tile_w &&
+          crtc_mode->height == (int) output_info->tile_info.tile_h);
 }
 
 static MetaCrtcMode *
 find_tiled_crtc_mode (MetaOutput   *output,
                       MetaCrtcMode *reference_crtc_mode)
 {
+  const MetaOutputInfo *output_info = meta_output_get_info (output);
   MetaCrtcMode *crtc_mode;
   unsigned int i;
 
-  crtc_mode = output->preferred_mode;
+  crtc_mode = output_info->preferred_mode;
   if (is_crtc_mode_tiled (output, crtc_mode))
     return crtc_mode;
 
-  for (i = 0; i < output->n_modes; i++)
+  for (i = 0; i < output_info->n_modes; i++)
     {
-      crtc_mode = output->modes[i];
+      crtc_mode = output_info->modes[i];
 
       if (!is_crtc_mode_tiled (output, crtc_mode))
         continue;
@@ -958,12 +991,13 @@ create_tiled_monitor_mode (MetaMonitorTiled *monitor_tiled,
   for (l = monitor_priv->outputs, i = 0; l; l = l->next, i++)
     {
       MetaOutput *output = l->data;
+      const MetaOutputInfo *output_info = meta_output_get_info (output);
       MetaCrtcMode *tiled_crtc_mode;
 
       tiled_crtc_mode = find_tiled_crtc_mode (output, reference_crtc_mode);
       if (!tiled_crtc_mode)
         {
-          g_warning ("No tiled mode found on %s", output->name);
+          g_warning ("No tiled mode found on %s", meta_output_get_name (output));
           meta_monitor_mode_free ((MetaMonitorMode *) mode);
           return NULL;
         }
@@ -973,7 +1007,8 @@ create_tiled_monitor_mode (MetaMonitorTiled *monitor_tiled,
         .crtc_mode = tiled_crtc_mode
       };
 
-      is_preferred = is_preferred && tiled_crtc_mode == output->preferred_mode;
+      is_preferred = (is_preferred &&
+                      tiled_crtc_mode == output_info->preferred_mode);
     }
 
   *out_is_preferred = is_preferred;
@@ -988,16 +1023,18 @@ generate_tiled_monitor_modes (MetaMonitorTiled *monitor_tiled)
   MetaMonitorPrivate *monitor_priv =
     meta_monitor_get_instance_private (monitor);
   MetaOutput *main_output;
+  const MetaOutputInfo *main_output_info;
   GList *tiled_modes = NULL;
   unsigned int i;
   MetaMonitorMode *best_mode = NULL;
   GList *l;
 
   main_output = meta_monitor_get_main_output (META_MONITOR (monitor_tiled));
+  main_output_info = meta_output_get_info (main_output);
 
-  for (i = 0; i < main_output->n_modes; i++)
+  for (i = 0; i < main_output_info->n_modes; i++)
     {
-      MetaCrtcMode *reference_crtc_mode = main_output->modes[i];
+      MetaCrtcMode *reference_crtc_mode = main_output_info->modes[i];
       MetaMonitorMode *mode;
       gboolean is_preferred;
 
@@ -1094,13 +1131,14 @@ create_untiled_monitor_mode (MetaMonitorTiled *monitor_tiled,
 static int
 count_untiled_crtc_modes (MetaOutput *output)
 {
+  const MetaOutputInfo *output_info = meta_output_get_info (output);
   int count;
   unsigned int i;
 
   count = 0;
-  for (i = 0; i < output->n_modes; i++)
+  for (i = 0; i < output_info->n_modes; i++)
     {
-      MetaCrtcMode *crtc_mode = output->modes[i];
+      MetaCrtcMode *crtc_mode = output_info->modes[i];
 
       if (!is_crtc_mode_tiled (output, crtc_mode))
         count++;
@@ -1149,13 +1187,15 @@ generate_untiled_monitor_modes (MetaMonitorTiled *monitor_tiled)
   MetaMonitorPrivate *monitor_priv =
     meta_monitor_get_instance_private (monitor);
   MetaOutput *main_output;
+  const MetaOutputInfo *main_output_info;
   unsigned int i;
 
   main_output = meta_monitor_get_main_output (monitor);
+  main_output_info = meta_output_get_info (main_output);
 
-  for (i = 0; i < main_output->n_modes; i++)
+  for (i = 0; i < main_output_info->n_modes; i++)
     {
-      MetaCrtcMode *crtc_mode = main_output->modes[i];
+      MetaCrtcMode *crtc_mode = main_output_info->modes[i];
       MetaMonitorMode *mode;
 
       mode = create_untiled_monitor_mode (monitor_tiled,
@@ -1177,7 +1217,7 @@ generate_untiled_monitor_modes (MetaMonitorTiled *monitor_tiled)
         }
 
       if (!monitor_priv->preferred_mode &&
-          crtc_mode == main_output->preferred_mode)
+          crtc_mode == main_output_info->preferred_mode)
         monitor_priv->preferred_mode = mode;
     }
 }
@@ -1281,6 +1321,7 @@ meta_monitor_tiled_new (MetaGpu            *gpu,
                         MetaMonitorManager *monitor_manager,
                         MetaOutput         *output)
 {
+  const MetaOutputInfo *output_info = meta_output_get_info (output);
   MetaMonitorTiled *monitor_tiled;
   MetaMonitor *monitor;
   MetaMonitorPrivate *monitor_priv;
@@ -1291,7 +1332,7 @@ meta_monitor_tiled_new (MetaGpu            *gpu,
 
   monitor_priv->gpu = gpu;
 
-  monitor_tiled->tile_group_id = output->tile_info.group_id;
+  monitor_tiled->tile_group_id = output_info->tile_info.group_id;
   monitor_priv->winsys_id = meta_output_get_id (output);
 
   monitor_tiled->origin_output = output;
diff --git a/src/backends/meta-output.c b/src/backends/meta-output.c
index 4e6e1f1973..55ac61c769 100644
--- a/src/backends/meta-output.c
+++ b/src/backends/meta-output.c
@@ -27,6 +27,7 @@ enum
 
   PROP_ID,
   PROP_GPU,
+  PROP_INFO,
 
   N_PROPS
 };
@@ -39,6 +40,8 @@ typedef struct _MetaOutputPrivate
 
   MetaGpu *gpu;
 
+  MetaOutputInfo *info;
+
   /* The CRTC driving this output, NULL if the output is not enabled */
   MetaCrtc *crtc;
 
@@ -52,6 +55,44 @@ typedef struct _MetaOutputPrivate
 
 G_DEFINE_TYPE_WITH_PRIVATE (MetaOutput, meta_output, G_TYPE_OBJECT)
 
+G_DEFINE_BOXED_TYPE (MetaOutputInfo, meta_output_info,
+                     meta_output_info_ref,
+                     meta_output_info_unref)
+
+MetaOutputInfo *
+meta_output_info_new (void)
+{
+  MetaOutputInfo *output_info;
+
+  output_info = g_new0 (MetaOutputInfo, 1);
+  g_ref_count_init (&output_info->ref_count);
+
+  return output_info;
+}
+
+MetaOutputInfo *
+meta_output_info_ref (MetaOutputInfo *output_info)
+{
+  g_ref_count_inc (&output_info->ref_count);
+  return output_info;
+}
+
+void
+meta_output_info_unref (MetaOutputInfo *output_info)
+{
+  if (g_ref_count_dec (&output_info->ref_count))
+    {
+      g_free (output_info->name);
+      g_free (output_info->vendor);
+      g_free (output_info->product);
+      g_free (output_info->serial);
+      g_free (output_info->modes);
+      g_free (output_info->possible_crtcs);
+      g_free (output_info->possible_clones);
+      g_free (output_info);
+    }
+}
+
 uint64_t
 meta_output_get_id (MetaOutput *output)
 {
@@ -71,7 +112,9 @@ meta_output_get_gpu (MetaOutput *output)
 const char *
 meta_output_get_name (MetaOutput *output)
 {
-  return output->name;
+  MetaOutputPrivate *priv = meta_output_get_instance_private (output);
+
+  return priv->info->name;
 }
 
 gboolean
@@ -115,6 +158,29 @@ meta_output_get_backlight (MetaOutput *output)
   return priv->backlight;
 }
 
+void
+meta_output_add_possible_clone (MetaOutput *output,
+                                MetaOutput *possible_clone)
+{
+  MetaOutputPrivate *priv = meta_output_get_instance_private (output);
+  MetaOutputInfo *output_info = priv->info;
+
+  output_info->n_possible_clones++;
+  output_info->possible_clones = g_renew (MetaOutput *,
+                                          output_info->possible_clones,
+                                          output_info->n_possible_clones);
+  output_info->possible_clones[output_info->n_possible_clones - 1] =
+    possible_clone;
+}
+
+const MetaOutputInfo *
+meta_output_get_info (MetaOutput *output)
+{
+  MetaOutputPrivate *priv = meta_output_get_instance_private (output);
+
+  return priv->info;
+}
+
 void
 meta_output_assign_crtc (MetaOutput                 *output,
                          MetaCrtc                   *crtc,
@@ -154,9 +220,10 @@ MetaMonitorTransform
 meta_output_logical_to_crtc_transform (MetaOutput           *output,
                                        MetaMonitorTransform  transform)
 {
+  MetaOutputPrivate *priv = meta_output_get_instance_private (output);
   MetaMonitorTransform panel_orientation_transform;
 
-  panel_orientation_transform = output->panel_orientation_transform;
+  panel_orientation_transform = priv->info->panel_orientation_transform;
   return meta_monitor_transform_transform (transform,
                                            panel_orientation_transform);
 }
@@ -165,10 +232,11 @@ MetaMonitorTransform
 meta_output_crtc_to_logical_transform (MetaOutput           *output,
                                        MetaMonitorTransform  transform)
 {
+  MetaOutputPrivate *priv = meta_output_get_instance_private (output);
   MetaMonitorTransform inverted_panel_orientation_transform;
 
   inverted_panel_orientation_transform =
-    meta_monitor_transform_invert (output->panel_orientation_transform);
+    meta_monitor_transform_invert (priv->info->panel_orientation_transform);
   return meta_monitor_transform_transform (transform,
                                            inverted_panel_orientation_transform);
 }
@@ -190,6 +258,9 @@ meta_output_set_property (GObject      *object,
     case PROP_GPU:
       priv->gpu = g_value_get_object (value);
       break;
+    case PROP_INFO:
+      priv->info = meta_output_info_ref (g_value_get_boxed (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -212,6 +283,9 @@ meta_output_get_property (GObject    *object,
     case PROP_GPU:
       g_value_set_object (value, priv->gpu);
       break;
+    case PROP_INFO:
+      g_value_set_boxed (value, priv->info);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -232,18 +306,13 @@ static void
 meta_output_finalize (GObject *object)
 {
   MetaOutput *output = META_OUTPUT (object);
-
-  g_free (output->name);
-  g_free (output->vendor);
-  g_free (output->product);
-  g_free (output->serial);
-  g_free (output->modes);
-  g_free (output->possible_crtcs);
-  g_free (output->possible_clones);
+  MetaOutputPrivate *priv = meta_output_get_instance_private (output);
 
   if (output->driver_notify)
     output->driver_notify (output);
 
+  g_clear_pointer (&priv->info, meta_output_info_unref);
+
   G_OBJECT_CLASS (meta_output_parent_class)->finalize (object);
 }
 
@@ -281,5 +350,13 @@ meta_output_class_init (MetaOutputClass *klass)
                          G_PARAM_READWRITE |
                          G_PARAM_CONSTRUCT_ONLY |
                          G_PARAM_STATIC_STRINGS);
+  obj_props[PROP_INFO] =
+    g_param_spec_boxed ("info",
+                        "info",
+                        "MetaOutputInfo",
+                        META_TYPE_OUTPUT_INFO,
+                        G_PARAM_READWRITE |
+                        G_PARAM_CONSTRUCT_ONLY |
+                        G_PARAM_STATIC_STRINGS);
   g_object_class_install_properties (object_class, N_PROPS, obj_props);
 }
diff --git a/src/backends/meta-output.h b/src/backends/meta-output.h
index 75b6ca1894..88d6dfc641 100644
--- a/src/backends/meta-output.h
+++ b/src/backends/meta-output.h
@@ -60,9 +60,9 @@ typedef enum
   META_CONNECTOR_TYPE_DSI = 16,
 } MetaConnectorType;
 
-struct _MetaOutput
+typedef struct _MetaOutputInfo
 {
-  GObject parent;
+  grefcount ref_count;
 
   char *name;
   char *vendor;
@@ -90,9 +90,6 @@ struct _MetaOutput
 
   gboolean supports_underscanning;
 
-  gpointer driver_private;
-  GDestroyNotify driver_notify;
-
   /*
    * Get a new preferred mode on hotplug events, to handle dynamic guest
    * resizing.
@@ -102,8 +99,35 @@ struct _MetaOutput
   int suggested_y;
 
   MetaTileInfo tile_info;
+} MetaOutputInfo;
+
+struct _MetaOutput
+{
+  GObject parent;
+
+  gpointer driver_private;
+  GDestroyNotify driver_notify;
 };
 
+#define META_TYPE_OUTPUT_INFO (meta_output_info_get_type ())
+META_EXPORT_TEST
+GType meta_output_info_get_type (void);
+
+META_EXPORT_TEST
+MetaOutputInfo * meta_output_info_new (void);
+
+META_EXPORT_TEST
+MetaOutputInfo * meta_output_info_ref (MetaOutputInfo *output_info);
+
+META_EXPORT_TEST
+void meta_output_info_unref (MetaOutputInfo *output_info);
+
+META_EXPORT_TEST
+void meta_output_info_parse_edid (MetaOutputInfo *output_info,
+                                  GBytes         *edid);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaOutputInfo, meta_output_info_unref)
+
 #define META_TYPE_OUTPUT (meta_output_get_type ())
 META_EXPORT_TEST G_DECLARE_FINAL_TYPE (MetaOutput, meta_output, META, OUTPUT, GObject)
 
@@ -129,6 +153,11 @@ void meta_output_set_backlight (MetaOutput *output,
 
 int meta_output_get_backlight (MetaOutput *output);
 
+void meta_output_add_possible_clone (MetaOutput *output,
+                                     MetaOutput *possible_clone);
+
+const MetaOutputInfo * meta_output_get_info (MetaOutput *output);
+
 META_EXPORT_TEST
 void meta_output_assign_crtc (MetaOutput                 *output,
                               MetaCrtc                   *crtc,
diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c
index 4d3aa25dfa..6b79a7a54b 100644
--- a/src/backends/native/meta-gpu-kms.c
+++ b/src/backends/native/meta-gpu-kms.c
@@ -264,9 +264,12 @@ static int
 compare_outputs (gconstpointer one,
                  gconstpointer two)
 {
-  const MetaOutput *o_one = one, *o_two = two;
+  MetaOutput *o_one = (MetaOutput *) one;
+  MetaOutput *o_two = (MetaOutput *) two;
+  const MetaOutputInfo *output_info_one = meta_output_get_info (o_one);
+  const MetaOutputInfo *output_info_two = meta_output_get_info (o_two);
 
-  return strcmp (o_one->name, o_two->name);
+  return strcmp (output_info_one->name, output_info_two->name);
 }
 
 static void
@@ -389,14 +392,7 @@ setup_output_clones (MetaGpu *gpu)
             continue;
 
           if (meta_output_kms_can_clone (output, other_output))
-            {
-              output->n_possible_clones++;
-              output->possible_clones = g_renew (MetaOutput *,
-                                                 output->possible_clones,
-                                                 output->n_possible_clones);
-              output->possible_clones[output->n_possible_clones - 1] =
-                other_output;
-            }
+            meta_output_add_possible_clone (output, other_output);
         }
     }
 }
diff --git a/src/backends/native/meta-output-kms.c b/src/backends/native/meta-output-kms.c
index 545df55ec8..61665b0bda 100644
--- a/src/backends/native/meta-output-kms.c
+++ b/src/backends/native/meta-output-kms.c
@@ -57,8 +57,9 @@ meta_output_kms_set_underscan (MetaOutput    *output,
                                MetaKmsUpdate *kms_update)
 {
   MetaOutputKms *output_kms = output->driver_private;
+  const MetaOutputInfo *output_info = meta_output_get_info (output);
 
-  if (!output->supports_underscanning)
+  if (!output_info->supports_underscanning)
     return;
 
   if (meta_output_is_underscanning (output))
@@ -153,8 +154,8 @@ meta_output_destroy_notify (MetaOutput *output)
 }
 
 static void
-add_common_modes (MetaOutput *output,
-                  MetaGpuKms *gpu_kms)
+add_common_modes (MetaOutputInfo *output_info,
+                  MetaGpuKms     *gpu_kms)
 {
   const drmModeModeInfo *drm_mode;
   MetaCrtcMode *crtc_mode;
@@ -165,9 +166,9 @@ add_common_modes (MetaOutput *output,
   unsigned max_vdisplay = 0;
   float max_refresh_rate = 0.0;
 
-  for (i = 0; i < output->n_modes; i++)
+  for (i = 0; i < output_info->n_modes; i++)
     {
-      drm_mode = output->modes[i]->driver_private;
+      drm_mode = output_info->modes[i]->driver_private;
       refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode);
       max_hdisplay = MAX (max_hdisplay, drm_mode->hdisplay);
       max_vdisplay = MAX (max_vdisplay, drm_mode->vdisplay);
@@ -211,11 +212,11 @@ add_common_modes (MetaOutput *output,
         }
     }
 
-  output->modes = g_renew (MetaCrtcMode *, output->modes,
-                           output->n_modes + array->len);
-  memcpy (output->modes + output->n_modes, array->pdata,
+  output_info->modes = g_renew (MetaCrtcMode *, output_info->modes,
+                                output_info->n_modes + array->len);
+  memcpy (output_info->modes + output_info->n_modes, array->pdata,
           array->len * sizeof (MetaCrtcMode *));
-  output->n_modes += array->len;
+  output_info->n_modes += array->len;
 
   g_ptr_array_free (array, TRUE);
 }
@@ -238,30 +239,29 @@ compare_modes (const void *one,
 }
 
 static gboolean
-init_output_modes (MetaOutput  *output,
-                   MetaGpuKms  *gpu_kms,
-                   GError     **error)
+init_output_modes (MetaOutputInfo    *output_info,
+                   MetaGpuKms        *gpu_kms,
+                   MetaKmsConnector  *kms_connector,
+                   GError           **error)
 {
-  MetaOutputKms *output_kms = output->driver_private;
   const MetaKmsConnectorState *connector_state;
   int i;
 
-  connector_state =
-    meta_kms_connector_get_current_state (output_kms->kms_connector);
+  connector_state = meta_kms_connector_get_current_state (kms_connector);
 
-  output->preferred_mode = NULL;
+  output_info->preferred_mode = NULL;
 
-  output->n_modes = connector_state->n_modes;
-  output->modes = g_new0 (MetaCrtcMode *, output->n_modes);
+  output_info->n_modes = connector_state->n_modes;
+  output_info->modes = g_new0 (MetaCrtcMode *, output_info->n_modes);
   for (i = 0; i < connector_state->n_modes; i++)
     {
       drmModeModeInfo *drm_mode = &connector_state->modes[i];
       MetaCrtcMode *crtc_mode;
 
       crtc_mode = meta_gpu_kms_get_mode_from_drm_mode (gpu_kms, drm_mode);
-      output->modes[i] = crtc_mode;
+      output_info->modes[i] = crtc_mode;
       if (drm_mode->type & DRM_MODE_TYPE_PREFERRED)
-        output->preferred_mode = output->modes[i];
+        output_info->preferred_mode = output_info->modes[i];
     }
 
   /* FIXME: MSC feature bit? */
@@ -269,20 +269,20 @@ init_output_modes (MetaOutput  *output,
    * a panel fitter capable of adjusting any mode to suit.
    */
   if (connector_state->has_scaling)
-    add_common_modes (output, gpu_kms);
+    add_common_modes (output_info, gpu_kms);
 
-  if (!output->modes)
+  if (!output_info->modes)
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                    "No modes available");
       return FALSE;
     }
 
-  qsort (output->modes, output->n_modes,
+  qsort (output_info->modes, output_info->n_modes,
          sizeof (MetaCrtcMode *), compare_modes);
 
-  if (!output->preferred_mode)
-    output->preferred_mode = output->modes[0];
+  if (!output_info->preferred_mode)
+    output_info->preferred_mode = output_info->modes[0];
 
   return TRUE;
 }
@@ -296,6 +296,7 @@ meta_create_kms_output (MetaGpuKms        *gpu_kms,
   MetaGpu *gpu = META_GPU (gpu_kms);
   uint32_t connector_id;
   uint32_t gpu_id;
+  g_autoptr (MetaOutputInfo) output_info = NULL;
   MetaOutput *output;
   MetaOutputKms *output_kms;
   const MetaKmsConnectorState *connector_state;
@@ -305,39 +306,26 @@ meta_create_kms_output (MetaGpuKms        *gpu_kms,
   gpu_id = meta_gpu_kms_get_id (gpu_kms);
   connector_id = meta_kms_connector_get_id (kms_connector);
 
-  output = g_object_new (META_TYPE_OUTPUT,
-                         "id", ((uint64_t) gpu_id << 32) | connector_id,
-                         "gpu", gpu,
-                         NULL);
-
-  output_kms = g_slice_new0 (MetaOutputKms);
-  output->driver_private = output_kms;
-  output->driver_notify = (GDestroyNotify) meta_output_destroy_notify;
-
-  output->name = g_strdup (meta_kms_connector_get_name (kms_connector));
-
-  output_kms->kms_connector = kms_connector;
+  output_info = meta_output_info_new ();
+  output_info->name = g_strdup (meta_kms_connector_get_name (kms_connector));
 
   connector_state = meta_kms_connector_get_current_state (kms_connector);
 
-  output->panel_orientation_transform =
+  output_info->panel_orientation_transform =
     connector_state->panel_orientation_transform;
-  if (meta_monitor_transform_is_rotated (output->panel_orientation_transform))
+  if (meta_monitor_transform_is_rotated (output_info->panel_orientation_transform))
     {
-      output->width_mm = connector_state->height_mm;
-      output->height_mm = connector_state->width_mm;
+      output_info->width_mm = connector_state->height_mm;
+      output_info->height_mm = connector_state->width_mm;
     }
   else
     {
-      output->width_mm = connector_state->width_mm;
-      output->height_mm = connector_state->height_mm;
+      output_info->width_mm = connector_state->width_mm;
+      output_info->height_mm = connector_state->height_mm;
     }
 
-  if (!init_output_modes (output, gpu_kms, error))
-    {
-      g_object_unref (output);
-      return NULL;
-    }
+  if (!init_output_modes (output_info, gpu_kms, kms_connector, error))
+    return NULL;
 
   crtcs = g_array_new (FALSE, FALSE, sizeof (MetaCrtc *));
 
@@ -352,8 +340,32 @@ meta_create_kms_output (MetaGpuKms        *gpu_kms,
         g_array_append_val (crtcs, crtc);
     }
 
-  output->n_possible_crtcs = crtcs->len;
-  output->possible_crtcs = (MetaCrtc **) g_array_free (crtcs, FALSE);
+  output_info->n_possible_crtcs = crtcs->len;
+  output_info->possible_crtcs = (MetaCrtc **) g_array_free (crtcs, FALSE);
+
+  output_info->suggested_x = connector_state->suggested_x;
+  output_info->suggested_y = connector_state->suggested_y;
+  output_info->hotplug_mode_update = connector_state->hotplug_mode_update;
+  output_info->supports_underscanning =
+    meta_kms_connector_is_underscanning_supported (kms_connector);
+
+  meta_output_info_parse_edid (output_info, connector_state->edid_data);
+
+  output_info->connector_type = meta_kms_connector_get_connector_type (kms_connector);
+
+  output_info->tile_info = connector_state->tile_info;
+
+  output = g_object_new (META_TYPE_OUTPUT,
+                         "id", ((uint64_t) gpu_id << 32) | connector_id,
+                         "gpu", gpu,
+                         "info", output_info,
+                         NULL);
+
+  output_kms = g_slice_new0 (MetaOutputKms);
+  output->driver_private = output_kms;
+  output->driver_notify = (GDestroyNotify) meta_output_destroy_notify;
+
+  output_kms->kms_connector = kms_connector;
 
   if (connector_state->current_crtc_id)
     {
@@ -389,29 +401,5 @@ meta_create_kms_output (MetaGpuKms        *gpu_kms,
       meta_output_unassign_crtc (output);
     }
 
-  output->suggested_x = connector_state->suggested_x;
-  output->suggested_y = connector_state->suggested_y;
-  output->hotplug_mode_update = connector_state->hotplug_mode_update;
-  output->supports_underscanning =
-    meta_kms_connector_is_underscanning_supported (kms_connector);
-
-  meta_output_parse_edid (output, connector_state->edid_data);
-
-  output->connector_type = meta_kms_connector_get_connector_type (kms_connector);
-
-  output->tile_info = connector_state->tile_info;
-
-  /* FIXME: backlight is a very driver specific thing unfortunately,
-     every DDX does its own thing, and the dumb KMS API does not include it.
-
-     For example, xf86-video-intel has a list of paths to probe in /sys/class/backlight
-     (one for each major HW maker, and then some).
-     We can't do the same because we're not root.
-     It might be best to leave backlight out of the story and rely on the setuid
-     helper in gnome-settings-daemon.
-     */
-  output->backlight_min = 0;
-  output->backlight_max = 0;
-
   return output;
 }
diff --git a/src/backends/x11/meta-gpu-xrandr.c b/src/backends/x11/meta-gpu-xrandr.c
index 12fd909259..ad1f8dce2e 100644
--- a/src/backends/x11/meta-gpu-xrandr.c
+++ b/src/backends/x11/meta-gpu-xrandr.c
@@ -69,9 +69,12 @@ static int
 compare_outputs (const void *one,
                  const void *two)
 {
-  const MetaOutput *o_one = one, *o_two = two;
+  MetaOutput *o_one = (MetaOutput *) one;
+  MetaOutput *o_two = (MetaOutput *) two;
+  const MetaOutputInfo *output_info_one = meta_output_get_info (o_one);
+  const MetaOutputInfo *output_info_two = meta_output_get_info (o_two);
 
-  return strcmp (o_one->name, o_two->name);
+  return strcmp (output_info_one->name, output_info_two->name);
 }
 
 static char *
@@ -210,11 +213,12 @@ meta_gpu_xrandr_read_current (MetaGpu  *gpu,
   for (l = outputs; l; l = l->next)
     {
       MetaOutput *output = l->data;
+      const MetaOutputInfo *output_info = meta_output_get_info (output);
       GList *k;
 
-      for (j = 0; j < output->n_possible_clones; j++)
+      for (j = 0; j < output_info->n_possible_clones; j++)
         {
-          RROutput clone = GPOINTER_TO_INT (output->possible_clones[j]);
+          RROutput clone = GPOINTER_TO_INT (output_info->possible_clones[j]);
 
           for (k = outputs; k; k = k->next)
             {
@@ -222,7 +226,7 @@ meta_gpu_xrandr_read_current (MetaGpu  *gpu,
 
               if (clone == (XID) meta_output_get_id (possible_clone))
                 {
-                  output->possible_clones[j] = possible_clone;
+                  output_info->possible_clones[j] = possible_clone;
                   break;
                 }
             }
diff --git a/src/backends/x11/meta-output-xrandr.c b/src/backends/x11/meta-output-xrandr.c
index bafb93f8f1..588fac2203 100644
--- a/src/backends/x11/meta-output-xrandr.c
+++ b/src/backends/x11/meta-output-xrandr.c
@@ -47,9 +47,8 @@
 #include "meta/util.h"
 
 static Display *
-xdisplay_from_output (MetaOutput *output)
+xdisplay_from_gpu (MetaGpu *gpu)
 {
-  MetaGpu *gpu = meta_output_get_gpu (output);
   MetaBackend *backend = meta_gpu_get_backend (gpu);
   MetaMonitorManager *monitor_manager =
     meta_backend_get_monitor_manager (backend);
@@ -59,6 +58,12 @@ xdisplay_from_output (MetaOutput *output)
   return meta_monitor_manager_xrandr_get_xdisplay (monitor_manager_xrandr);
 }
 
+static Display *
+xdisplay_from_output (MetaOutput *output)
+{
+  return xdisplay_from_gpu (meta_output_get_gpu (output));
+}
+
 static void
 output_set_presentation_xrandr (MetaOutput *output,
                                 gboolean    presentation)
@@ -140,7 +145,7 @@ meta_output_xrandr_apply_mode (MetaOutput *output)
 
   output_set_presentation_xrandr (output, meta_output_is_presentation (output));
 
-  if (output->supports_underscanning)
+  if (meta_output_get_info (output)->supports_underscanning)
     {
       output_set_underscanning_xrandr (output,
                                        meta_output_is_underscanning (output));
@@ -151,20 +156,23 @@ static int
 normalize_backlight (MetaOutput *output,
                      int         hw_value)
 {
-  return round ((double)(hw_value - output->backlight_min) /
-                (output->backlight_max - output->backlight_min) * 100.0);
+  const MetaOutputInfo *output_info = meta_output_get_info (output);
+
+  return round ((double) (hw_value - output_info->backlight_min) /
+                (output_info->backlight_max - output_info->backlight_min) * 100.0);
 }
 
 void
 meta_output_xrandr_change_backlight (MetaOutput *output,
                                      int         value)
 {
+  const MetaOutputInfo *output_info = meta_output_get_info (output);
   Display *xdisplay = xdisplay_from_output (output);
   Atom atom;
   int hw_value;
 
-  hw_value = round ((double) value / 100.0 * output->backlight_max +
-                    output->backlight_min);
+  hw_value = round ((double) value / 100.0 * output_info->backlight_max +
+                    output_info->backlight_min);
 
   atom = XInternAtom (xdisplay, "Backlight", False);
 
@@ -179,11 +187,11 @@ meta_output_xrandr_change_backlight (MetaOutput *output,
 }
 
 static gboolean
-output_get_integer_property (MetaOutput *output,
+output_get_integer_property (Display    *xdisplay,
+                             RROutput    output_id,
                              const char *propname,
                              gint       *value)
 {
-  Display *xdisplay = xdisplay_from_output (output);
   gboolean exists = FALSE;
   Atom atom, actual_type;
   int actual_format;
@@ -192,7 +200,7 @@ output_get_integer_property (MetaOutput *output,
 
   atom = XInternAtom (xdisplay, propname, False);
   XRRGetOutputProperty (xdisplay,
-                        (XID) meta_output_get_id (output),
+                        (XID) output_id,
                         atom,
                         0, G_MAXLONG, False, False, XA_INTEGER,
                         &actual_type, &actual_format,
@@ -208,10 +216,10 @@ output_get_integer_property (MetaOutput *output,
 }
 
 static gboolean
-output_get_property_exists (MetaOutput *output,
+output_get_property_exists (Display    *xdisplay,
+                            RROutput    output_id,
                             const char *propname)
 {
-  Display *xdisplay = xdisplay_from_output (output);
   gboolean exists = FALSE;
   Atom atom, actual_type;
   int actual_format;
@@ -220,7 +228,7 @@ output_get_property_exists (MetaOutput *output,
 
   atom = XInternAtom (xdisplay, propname, False);
   XRRGetOutputProperty (xdisplay,
-                        (XID) meta_output_get_id (output),
+                        (XID) output_id,
                         atom,
                         0, G_MAXLONG, False, False, AnyPropertyType,
                         &actual_type, &actual_format,
@@ -288,9 +296,9 @@ output_get_underscanning_xrandr (MetaOutput *output)
 }
 
 static gboolean
-output_get_supports_underscanning_xrandr (MetaOutput *output)
+output_get_supports_underscanning_xrandr (Display  *xdisplay,
+                                          RROutput  output_id)
 {
-  Display *xdisplay = xdisplay_from_output (output);
   Atom atom, actual_type;
   int actual_format, i;
   unsigned long nitems, bytes_after;
@@ -301,7 +309,7 @@ output_get_supports_underscanning_xrandr (MetaOutput *output)
 
   atom = XInternAtom (xdisplay, "underscan", False);
   XRRGetOutputProperty (xdisplay,
-                        (XID) meta_output_get_id (output),
+                        (XID) output_id,
                         atom,
                         0, G_MAXLONG, False, False, XA_ATOM,
                         &actual_type, &actual_format,
@@ -311,7 +319,7 @@ output_get_supports_underscanning_xrandr (MetaOutput *output)
     return FALSE;
 
   property_info = XRRQueryOutputProperty (xdisplay,
-                                          (XID) meta_output_get_id (output),
+                                          (XID) output_id,
                                           atom);
   values = (Atom *) property_info->values;
 
@@ -361,19 +369,18 @@ output_get_backlight_xrandr (MetaOutput *output)
 }
 
 static void
-output_get_backlight_limits_xrandr (MetaOutput *output)
+output_info_init_backlight_limits_xrandr (MetaOutputInfo     *output_info,
+                                          Display            *xdisplay,
+                                          xcb_randr_output_t  output_id)
 {
-  Display *xdisplay = xdisplay_from_output (output);
   Atom atom;
   xcb_connection_t *xcb_conn;
-  xcb_randr_output_t output_id;
   xcb_randr_query_output_property_cookie_t cookie;
   g_autofree xcb_randr_query_output_property_reply_t *reply = NULL;
 
   atom = XInternAtom (xdisplay, "Backlight", False);
 
   xcb_conn = XGetXCBConnection (xdisplay);
-  output_id = meta_output_get_id (output);
   cookie = xcb_randr_query_output_property (xcb_conn,
                                             output_id,
                                             (xcb_atom_t) atom);
@@ -387,13 +394,13 @@ output_get_backlight_limits_xrandr (MetaOutput *output)
 
   if (!reply->range || reply->length != 2)
     {
-      meta_verbose ("backlight %s was not range\n", output->name);
+      meta_verbose ("backlight %s was not range\n", output_info->name);
       return;
     }
 
   int32_t *values = xcb_randr_query_output_property_valid_values (reply);
-  output->backlight_min = values[0];
-  output->backlight_max = values[1];
+  output_info->backlight_min = values[0];
+  output_info->backlight_max = values[1];
 }
 
 static guint8 *
@@ -430,27 +437,21 @@ get_edid_property (Display  *xdisplay,
   return result;
 }
 
-GBytes *
-meta_output_xrandr_read_edid (MetaOutput *output)
+static GBytes *
+read_xrandr_edid (Display  *xdisplay,
+                  RROutput  output_id)
 {
-  Display *xdisplay = xdisplay_from_output (output);
   Atom edid_atom;
   guint8 *result;
   gsize len;
 
   edid_atom = XInternAtom (xdisplay, "EDID", FALSE);
-  result = get_edid_property (xdisplay,
-                              meta_output_get_id (output),
-                              edid_atom,
-                              &len);
+  result = get_edid_property (xdisplay, output_id, edid_atom, &len);
 
   if (!result)
     {
       edid_atom = XInternAtom (xdisplay, "EDID_DATA", FALSE);
-      result = get_edid_property (xdisplay,
-                                  meta_output_get_id (output),
-                                  edid_atom,
-                                  &len);
+      result = get_edid_property (xdisplay, output_id, edid_atom, &len);
     }
 
   if (result)
@@ -464,27 +465,39 @@ meta_output_xrandr_read_edid (MetaOutput *output)
   return NULL;
 }
 
+GBytes *
+meta_output_xrandr_read_edid (MetaOutput *output)
+{
+  Display *xdisplay = xdisplay_from_output (output);
+  RROutput output_id = (RROutput) meta_output_get_id (output);
+
+  return read_xrandr_edid (xdisplay, output_id);
+}
+
 static gboolean
-output_get_hotplug_mode_update (MetaOutput *output)
+output_get_hotplug_mode_update (Display  *xdisplay,
+                                RROutput  output_id)
 {
-  return output_get_property_exists (output, "hotplug_mode_update");
+  return output_get_property_exists (xdisplay, output_id, "hotplug_mode_update");
 }
 
 static gint
-output_get_suggested_x (MetaOutput *output)
+output_get_suggested_x (Display  *xdisplay,
+                        RROutput  output_id)
 {
   gint val;
-  if (output_get_integer_property (output, "suggested X", &val))
+  if (output_get_integer_property (xdisplay, output_id, "suggested X", &val))
     return val;
 
   return -1;
 }
 
 static gint
-output_get_suggested_y (MetaOutput *output)
+output_get_suggested_y (Display  *xdisplay,
+                        RROutput  output_id)
 {
   gint val;
-  if (output_get_integer_property (output, "suggested Y", &val))
+  if (output_get_integer_property (xdisplay, output_id, "suggested Y", &val))
     return val;
 
   return -1;
@@ -528,9 +541,9 @@ connector_type_from_atom (Display *xdisplay,
 }
 
 static MetaConnectorType
-output_get_connector_type_from_prop (MetaOutput *output)
+output_get_connector_type_from_prop (Display  *xdisplay,
+                                     RROutput  output_id)
 {
-  Display *xdisplay = xdisplay_from_output (output);
   Atom atom, actual_type, connector_type_atom;
   int actual_format;
   unsigned long nitems, bytes_after;
@@ -538,7 +551,7 @@ output_get_connector_type_from_prop (MetaOutput *output)
 
   atom = XInternAtom (xdisplay, "ConnectorType", False);
   XRRGetOutputProperty (xdisplay,
-                        (XID) meta_output_get_id (output),
+                        (XID) output_id,
                         atom,
                         0, G_MAXLONG, False, False, XA_ATOM,
                         &actual_type, &actual_format,
@@ -552,9 +565,9 @@ output_get_connector_type_from_prop (MetaOutput *output)
 }
 
 static MetaConnectorType
-output_get_connector_type_from_name (MetaOutput *output)
+output_info_get_connector_type_from_name (const MetaOutputInfo *output_info)
 {
-  const char *name = output->name;
+  const char *name = output_info->name;
 
   /* drmmode_display.c, which was copy/pasted across all the FOSS
    * xf86-video-* drivers, seems to name its outputs based on the
@@ -600,7 +613,9 @@ output_get_connector_type_from_name (MetaOutput *output)
 }
 
 static MetaConnectorType
-output_get_connector_type (MetaOutput *output)
+output_info_get_connector_type (MetaOutputInfo *output_info,
+                                Display        *xdisplay,
+                                RROutput        output_id)
 {
   MetaConnectorType ret;
 
@@ -611,12 +626,12 @@ output_get_connector_type (MetaOutput *output)
    * Try poking it first, without any expectations that it will work.
    * If it's not there, we thankfully have other bonghits to try next.
    */
-  ret = output_get_connector_type_from_prop (output);
+  ret = output_get_connector_type_from_prop (xdisplay, output_id);
   if (ret != META_CONNECTOR_TYPE_Unknown)
     return ret;
 
   /* Fall back to heuristics based on the output name. */
-  ret = output_get_connector_type_from_name (output);
+  ret = output_info_get_connector_type_from_name (output_info);
   if (ret != META_CONNECTOR_TYPE_Unknown)
     return ret;
 
@@ -624,9 +639,9 @@ output_get_connector_type (MetaOutput *output)
 }
 
 static gint
-output_get_panel_orientation_transform (MetaOutput *output)
+output_get_panel_orientation_transform (Display  *xdisplay,
+                                        RROutput  output_id)
 {
-  Display *xdisplay = xdisplay_from_output (output);
   unsigned long nitems, bytes_after;
   Atom atom, actual_type;
   int actual_format;
@@ -635,7 +650,7 @@ output_get_panel_orientation_transform (MetaOutput *output)
 
   atom = XInternAtom (xdisplay, "panel orientation", False);
   XRRGetOutputProperty (xdisplay,
-                        (XID) meta_output_get_id (output),
+                        (XID) output_id,
                         atom,
                         0, G_MAXLONG, False, False, XA_ATOM,
                         &actual_type, &actual_format,
@@ -658,27 +673,19 @@ output_get_panel_orientation_transform (MetaOutput *output)
 }
 
 static void
-output_get_tile_info (MetaOutput *output)
+output_info_init_tile_info (MetaOutputInfo *output_info,
+                            Display        *xdisplay,
+                            RROutput        output_id)
 {
-  MetaGpu *gpu = meta_output_get_gpu (output);
-  MetaBackend *backend = meta_gpu_get_backend (gpu);
-  MetaMonitorManager *monitor_manager =
-    meta_backend_get_monitor_manager (backend);
-  MetaMonitorManagerXrandr *monitor_manager_xrandr =
-    META_MONITOR_MANAGER_XRANDR (monitor_manager);
-  Display *xdisplay = xdisplay_from_output (output);
   Atom tile_atom;
   unsigned char *prop;
   unsigned long nitems, bytes_after;
   int actual_format;
   Atom actual_type;
 
-  if (!meta_monitor_manager_xrandr_has_randr15 (monitor_manager_xrandr))
-    return;
-
   tile_atom = XInternAtom (xdisplay, "TILE", FALSE);
   XRRGetOutputProperty (xdisplay,
-                        (XID) meta_output_get_id (output),
+                        (XID) output_id,
                         tile_atom, 0, 100, False,
                         False, AnyPropertyType,
                         &actual_type, &actual_format,
@@ -687,28 +694,29 @@ output_get_tile_info (MetaOutput *output)
   if (actual_type == XA_INTEGER && actual_format == 32 && nitems == 8)
     {
       long *values = (long *)prop;
-      output->tile_info.group_id = values[0];
-      output->tile_info.flags = values[1];
-      output->tile_info.max_h_tiles = values[2];
-      output->tile_info.max_v_tiles = values[3];
-      output->tile_info.loc_h_tile = values[4];
-      output->tile_info.loc_v_tile = values[5];
-      output->tile_info.tile_w = values[6];
-      output->tile_info.tile_h = values[7];
+
+      output_info->tile_info.group_id = values[0];
+      output_info->tile_info.flags = values[1];
+      output_info->tile_info.max_h_tiles = values[2];
+      output_info->tile_info.max_v_tiles = values[3];
+      output_info->tile_info.loc_h_tile = values[4];
+      output_info->tile_info.loc_v_tile = values[5];
+      output_info->tile_info.tile_w = values[6];
+      output_info->tile_info.tile_h = values[7];
     }
   XFree (prop);
 }
 
 
 static void
-output_get_modes (MetaOutput    *output,
-                  XRROutputInfo *xrandr_output)
+output_info_init_modes (MetaOutputInfo *output_info,
+                        MetaGpu        *gpu,
+                        XRROutputInfo  *xrandr_output)
 {
-  MetaGpu *gpu = meta_output_get_gpu (output);
   unsigned int i;
   unsigned int n_actual_modes;
 
-  output->modes = g_new0 (MetaCrtcMode *, xrandr_output->nmode);
+  output_info->modes = g_new0 (MetaCrtcMode *, xrandr_output->nmode);
 
   n_actual_modes = 0;
   for (i = 0; i < (unsigned int) xrandr_output->nmode; i++)
@@ -721,28 +729,27 @@ output_get_modes (MetaOutput    *output,
 
           if (xrandr_output->modes[i] == (XID) mode->mode_id)
             {
-              output->modes[n_actual_modes] = mode;
+              output_info->modes[n_actual_modes] = mode;
               n_actual_modes += 1;
               break;
             }
         }
     }
-  output->n_modes = n_actual_modes;
+  output_info->n_modes = n_actual_modes;
   if (n_actual_modes > 0)
-    output->preferred_mode = output->modes[0];
+    output_info->preferred_mode = output_info->modes[0];
 }
 
 static void
-output_get_crtcs (MetaOutput     *output,
-                  XRROutputInfo  *xrandr_output,
-                  MetaCrtc      **assigned_crtc)
+output_info_init_crtcs (MetaOutputInfo *output_info,
+                        MetaGpu        *gpu,
+                        XRROutputInfo  *xrandr_output)
 {
-  MetaGpu *gpu = meta_output_get_gpu (output);
   unsigned int i;
   unsigned int n_actual_crtcs;
   GList *l;
 
-  output->possible_crtcs = g_new0 (MetaCrtc *, xrandr_output->ncrtc);
+  output_info->possible_crtcs = g_new0 (MetaCrtc *, xrandr_output->ncrtc);
 
   n_actual_crtcs = 0;
   for (i = 0; i < (unsigned int) xrandr_output->ncrtc; i++)
@@ -753,27 +760,30 @@ output_get_crtcs (MetaOutput     *output,
 
           if ((XID) meta_crtc_get_id (crtc) == xrandr_output->crtcs[i])
             {
-              output->possible_crtcs[n_actual_crtcs] = crtc;
+              output_info->possible_crtcs[n_actual_crtcs] = crtc;
               n_actual_crtcs += 1;
               break;
             }
         }
     }
-  output->n_possible_crtcs = n_actual_crtcs;
+  output_info->n_possible_crtcs = n_actual_crtcs;
+}
+
+static MetaCrtc *
+find_assigned_crtc (MetaGpu       *gpu,
+                    XRROutputInfo *xrandr_output)
+{
+  GList *l;
 
-  meta_output_unassign_crtc (output);
   for (l = meta_gpu_get_crtcs (gpu); l; l = l->next)
     {
       MetaCrtc *crtc = l->data;
 
       if ((XID) meta_crtc_get_id (crtc) == xrandr_output->crtc)
-        {
-          *assigned_crtc = crtc;
-          return;
-        }
+        return crtc;
     }
 
-  *assigned_crtc = NULL;
+  return NULL;
 }
 
 MetaOutput *
@@ -782,45 +792,80 @@ meta_create_xrandr_output (MetaGpuXrandr *gpu_xrandr,
                            RROutput       output_id,
                            RROutput       primary_output)
 {
+  MetaGpu *gpu = META_GPU (gpu_xrandr);
+  MetaBackend *backend = meta_gpu_get_backend (gpu);
+  MetaMonitorManager *monitor_manager =
+    meta_backend_get_monitor_manager (backend);
+  MetaMonitorManagerXrandr *monitor_manager_xrandr =
+    META_MONITOR_MANAGER_XRANDR (monitor_manager);
+  Display *xdisplay =
+    meta_monitor_manager_xrandr_get_xdisplay (monitor_manager_xrandr);
+  g_autoptr (MetaOutputInfo) output_info = NULL;
   MetaOutput *output;
   GBytes *edid;
   MetaCrtc *assigned_crtc;
   unsigned int i;
 
-  output = g_object_new (META_TYPE_OUTPUT,
-                         "id", output_id,
-                         "gpu", gpu_xrandr,
-                         NULL);
-  output->name = g_strdup (xrandr_output->name);
+  output_info = meta_output_info_new ();
+
+  output_info->name = g_strdup (xrandr_output->name);
 
-  edid = meta_output_xrandr_read_edid (output);
-  meta_output_parse_edid (output, edid);
+  edid = read_xrandr_edid (xdisplay, output_id);
+  meta_output_info_parse_edid (output_info, edid);
   g_bytes_unref (edid);
 
-  output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
-  output->hotplug_mode_update = output_get_hotplug_mode_update (output);
-  output->suggested_x = output_get_suggested_x (output);
-  output->suggested_y = output_get_suggested_y (output);
-  output->connector_type = output_get_connector_type (output);
-  output->panel_orientation_transform =
-    output_get_panel_orientation_transform (output);
+  output_info->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
+  output_info->hotplug_mode_update = output_get_hotplug_mode_update (xdisplay,
+                                                                     output_id);
+  output_info->suggested_x = output_get_suggested_x (xdisplay, output_id);
+  output_info->suggested_y = output_get_suggested_y (xdisplay, output_id);
+  output_info->connector_type = output_info_get_connector_type (output_info,
+                                                                xdisplay,
+                                                                output_id);
+  output_info->panel_orientation_transform =
+    output_get_panel_orientation_transform (xdisplay, output_id);
 
   if (meta_monitor_transform_is_rotated (
-                                output->panel_orientation_transform))
+                                output_info->panel_orientation_transform))
     {
-      output->width_mm = xrandr_output->mm_height;
-      output->height_mm = xrandr_output->mm_width;
+      output_info->width_mm = xrandr_output->mm_height;
+      output_info->height_mm = xrandr_output->mm_width;
     }
   else
     {
-      output->width_mm = xrandr_output->mm_width;
-      output->height_mm = xrandr_output->mm_height;
+      output_info->width_mm = xrandr_output->mm_width;
+      output_info->height_mm = xrandr_output->mm_height;
+    }
+
+  if (!meta_monitor_manager_xrandr_has_randr15 (monitor_manager_xrandr))
+    output_info_init_tile_info (output_info, xdisplay, output_id);
+  output_info_init_modes (output_info, gpu, xrandr_output);
+  output_info_init_crtcs (output_info, gpu, xrandr_output);
+
+  output_info->n_possible_clones = xrandr_output->nclone;
+  output_info->possible_clones = g_new0 (MetaOutput *,
+                                         output_info->n_possible_clones);
+  /*
+   * We can build the list of clones now, because we don't have the list of
+   * outputs yet, so temporarily set the pointers to the bare XIDs, and then
+   * we'll fix them in a second pass.
+   */
+  for (i = 0; i < (unsigned int) xrandr_output->nclone; i++)
+    {
+      output_info->possible_clones[i] = GINT_TO_POINTER (xrandr_output->clones[i]);
     }
 
-  output_get_tile_info (output);
-  output_get_modes (output, xrandr_output);
-  output_get_crtcs (output, xrandr_output, &assigned_crtc);
+  output_info->supports_underscanning =
+    output_get_supports_underscanning_xrandr (xdisplay, output_id);
+  output_info_init_backlight_limits_xrandr (output_info, xdisplay, output_id);
+
+  output = g_object_new (META_TYPE_OUTPUT,
+                         "id", output_id,
+                         "gpu", gpu_xrandr,
+                         "info", output_info,
+                         NULL);
 
+  assigned_crtc = find_assigned_crtc (gpu, xrandr_output);
   if (assigned_crtc)
     {
       MetaOutputAssignment output_assignment;
@@ -832,28 +877,15 @@ meta_create_xrandr_output (MetaGpuXrandr *gpu_xrandr,
       };
       meta_output_assign_crtc (output, assigned_crtc, &output_assignment);
     }
-
-  output->n_possible_clones = xrandr_output->nclone;
-  output->possible_clones = g_new0 (MetaOutput *,
-                                    output->n_possible_clones);
-  /*
-   * We can build the list of clones now, because we don't have the list of
-   * outputs yet, so temporarily set the pointers to the bare XIDs, and then
-   * we'll fix them in a second pass.
-   */
-  for (i = 0; i < (unsigned int) xrandr_output->nclone; i++)
+  else
     {
-      output->possible_clones[i] = GINT_TO_POINTER (xrandr_output->clones[i]);
+      meta_output_unassign_crtc (output);
     }
 
-  output->supports_underscanning =
-    output_get_supports_underscanning_xrandr (output);
-  output_get_backlight_limits_xrandr (output);
-
-  if (!(output->backlight_min == 0 && output->backlight_max == 0))
+  if (!(output_info->backlight_min == 0 && output_info->backlight_max == 0))
     meta_output_set_backlight (output, output_get_backlight_xrandr (output));
 
-  if (output->n_modes == 0 || output->n_possible_crtcs == 0)
+  if (output_info->n_modes == 0 || output_info->n_possible_crtcs == 0)
     {
       g_object_unref (output);
       return NULL;
diff --git a/src/tests/headless-start-test.c b/src/tests/headless-start-test.c
index 302ee85573..223ef1a498 100644
--- a/src/tests/headless-start-test.c
+++ b/src/tests/headless-start-test.c
@@ -124,6 +124,7 @@ meta_test_headless_monitor_connect (void)
   MetaGpu *gpu;
   MetaCrtc *crtc;
   MetaCrtc **possible_crtcs;
+  g_autoptr (MetaOutputInfo) output_info = NULL;
   MetaOutput *output;
   GList *logical_monitors;
   ClutterActor *stage;
@@ -151,20 +152,25 @@ meta_test_headless_monitor_connect (void)
   possible_crtcs = g_new0 (MetaCrtc *, 1);
   possible_crtcs[0] = g_list_first (test_setup->crtcs)->data;
 
+  output_info = meta_output_info_new ();
+
+  output_info->name = g_strdup ("DP-1");
+  output_info->vendor = g_strdup ("MetaProduct's Inc.");
+  output_info->product = g_strdup ("MetaMonitor");
+  output_info->serial = g_strdup ("0x987654");
+  output_info->preferred_mode = modes[0];
+  output_info->n_modes = 1;
+  output_info->modes = modes;
+  output_info->n_possible_crtcs = 1;
+  output_info->possible_crtcs = possible_crtcs;
+  output_info->connector_type = META_CONNECTOR_TYPE_DisplayPort;
+
   output = g_object_new (META_TYPE_OUTPUT,
                          "id", 1,
                          "gpu", gpu,
+                         "info", output_info,
                          NULL);
-  output->name = g_strdup ("DP-1");
-  output->vendor = g_strdup ("MetaProduct's Inc.");
-  output->product = g_strdup ("MetaMonitor");
-  output->serial = g_strdup ("0x987654");
-  output->preferred_mode = modes[0];
-  output->n_modes = 1;
-  output->modes = modes;
-  output->n_possible_crtcs = 1;
-  output->possible_crtcs = possible_crtcs;
-  output->connector_type = META_CONNECTOR_TYPE_DisplayPort;
+
   test_setup->outputs = g_list_append (NULL, output);
 
   meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup);
diff --git a/src/tests/monitor-test-utils.c b/src/tests/monitor-test-utils.c
index 61d85c6273..484ec1f19d 100644
--- a/src/tests/monitor-test-utils.c
+++ b/src/tests/monitor-test-utils.c
@@ -598,6 +598,7 @@ create_monitor_test_setup (MonitorTestCaseSetup *setup,
       int scale;
       gboolean is_laptop_panel;
       const char *serial;
+      g_autoptr (MetaOutputInfo) output_info = NULL;
 
       crtc_index = setup->outputs[i].crtc;
       if (crtc_index == -1)
@@ -649,9 +650,45 @@ create_monitor_test_setup (MonitorTestCaseSetup *setup,
       if (!serial)
         serial = "0x123456";
 
+      output_info = meta_output_info_new ();
+
+      output_info->name = (is_laptop_panel
+                           ? g_strdup_printf ("eDP-%d", ++n_laptop_panels)
+                           : g_strdup_printf ("DP-%d", ++n_normal_panels));
+      output_info->vendor = g_strdup ("MetaProduct's Inc.");
+      output_info->product = g_strdup ("MetaMonitor");
+      output_info->serial = g_strdup (serial);
+      if (setup->outputs[i].hotplug_mode)
+        {
+          output_info->suggested_x = setup->outputs[i].suggested_x;
+          output_info->suggested_y = setup->outputs[i].suggested_y;
+        }
+      else
+        {
+          output_info->suggested_x = -1;
+          output_info->suggested_y = -1;
+        }
+      output_info->hotplug_mode_update = hotplug_mode_update;
+      output_info->width_mm = setup->outputs[i].width_mm;
+      output_info->height_mm = setup->outputs[i].height_mm;
+      output_info->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
+      output_info->preferred_mode = preferred_mode;
+      output_info->n_modes = n_modes;
+      output_info->modes = modes;
+      output_info->n_possible_crtcs = n_possible_crtcs;
+      output_info->possible_crtcs = possible_crtcs;
+      output_info->n_possible_clones = 0;
+      output_info->possible_clones = NULL;
+      output_info->connector_type = (is_laptop_panel ? META_CONNECTOR_TYPE_eDP
+                                     : META_CONNECTOR_TYPE_DisplayPort);
+      output_info->tile_info = setup->outputs[i].tile_info;
+      output_info->panel_orientation_transform =
+        setup->outputs[i].panel_orientation_transform;
+
       output = g_object_new (META_TYPE_OUTPUT,
                              "id", i,
                              "gpu", test_get_gpu (),
+                             "info", output_info,
                              NULL);
 
       if (crtc)
@@ -664,39 +701,6 @@ create_monitor_test_setup (MonitorTestCaseSetup *setup,
           meta_output_assign_crtc (output, crtc, &output_assignment);
         }
 
-      output->name = (is_laptop_panel ? g_strdup_printf ("eDP-%d",
-                                                  ++n_laptop_panels)
-                               : g_strdup_printf ("DP-%d",
-                                                  ++n_normal_panels));
-      output->vendor = g_strdup ("MetaProduct's Inc.");
-      output->product = g_strdup ("MetaMonitor");
-      output->serial = g_strdup (serial);
-      if (setup->outputs[i].hotplug_mode)
-        {
-          output->suggested_x = setup->outputs[i].suggested_x;
-          output->suggested_y = setup->outputs[i].suggested_y;
-        }
-      else
-        {
-          output->suggested_x = -1;
-          output->suggested_y = -1;
-        }
-      output->hotplug_mode_update = hotplug_mode_update;
-      output->width_mm = setup->outputs[i].width_mm;
-      output->height_mm = setup->outputs[i].height_mm;
-      output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
-      output->preferred_mode = preferred_mode;
-      output->n_modes = n_modes;
-      output->modes = modes;
-      output->n_possible_crtcs = n_possible_crtcs;
-      output->possible_crtcs = possible_crtcs;
-      output->n_possible_clones = 0;
-      output->possible_clones = NULL;
-      output->connector_type = (is_laptop_panel ? META_CONNECTOR_TYPE_eDP
-                                         : META_CONNECTOR_TYPE_DisplayPort);
-      output->tile_info = setup->outputs[i].tile_info;
-      output->panel_orientation_transform =
-        setup->outputs[i].panel_orientation_transform;
       output->driver_private = output_test;
       output->driver_notify = (GDestroyNotify) meta_output_test_destroy_notify;
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]