[gnome-flashback] output: move output info to GfOutputInfo struct



commit 20caba8c50ab0407aa09a3d9805905ba3bac0328
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Fri Jul 10 11:49:44 2020 +0300

    output: move output info to GfOutputInfo struct
    
    Based on mutter commit:
    https://gitlab.gnome.org/GNOME/mutter/-/commit/1406348be4c7cdbbc1f0

 backends/Makefile.am                 |   2 +
 backends/gf-gpu-xrandr.c             |  21 ++-
 backends/gf-gpu.c                    |   8 +-
 backends/gf-monitor-config-manager.c |  11 +-
 backends/gf-monitor-manager.c        | 110 +++++++----
 backends/gf-monitor-normal.c         |  23 ++-
 backends/gf-monitor-private.h        |   3 +
 backends/gf-monitor-tiled.c          | 120 +++++++-----
 backends/gf-monitor.c                |  83 +++++----
 backends/gf-output-info-private.h    |  99 ++++++++++
 backends/gf-output-info.c            | 120 ++++++++++++
 backends/gf-output-private.h         |  63 +------
 backends/gf-output-xrandr.c          | 347 +++++++++++++++++++----------------
 backends/gf-output.c                 | 149 ++++++++-------
 14 files changed, 737 insertions(+), 422 deletions(-)
---
diff --git a/backends/Makefile.am b/backends/Makefile.am
index db26527..17d1196 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -73,6 +73,8 @@ libbackends_la_SOURCES = \
        gf-monitors-config.c \
        gf-orientation-manager-private.h \
        gf-orientation-manager.c \
+       gf-output-info-private.h \
+       gf-output-info.c \
        gf-output-private.h \
        gf-output-xrandr-private.h \
        gf-output-xrandr.c \
diff --git a/backends/gf-gpu-xrandr.c b/backends/gf-gpu-xrandr.c
index 17df07d..d3ffd78 100644
--- a/backends/gf-gpu-xrandr.c
+++ b/backends/gf-gpu-xrandr.c
@@ -49,9 +49,18 @@ static gint
 compare_outputs (const void *one,
                  const void *two)
 {
-  const GfOutput *o_one = one, *o_two = two;
+  GfOutput *o_one;
+  GfOutput *o_two;
+  const GfOutputInfo *output_info_one;
+  const GfOutputInfo *output_info_two;
 
-  return strcmp (o_one->name, o_two->name);
+  o_one = (GfOutput *) one;
+  o_two = (GfOutput *) two;
+
+  output_info_one = gf_output_get_info (o_one);
+  output_info_two = gf_output_get_info (o_two);
+
+  return strcmp (output_info_one->name, output_info_two->name);
 }
 
 static char *
@@ -203,13 +212,15 @@ gf_gpu_xrandr_read_current (GfGpu   *gpu,
   for (l = outputs; l; l = l->next)
     {
       GfOutput *output;
+      const GfOutputInfo *output_info;
       GList *k;
 
       output = l->data;
+      output_info = gf_output_get_info (output);
 
-      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)
             {
@@ -217,7 +228,7 @@ gf_gpu_xrandr_read_current (GfGpu   *gpu,
 
               if (clone == (XID) gf_output_get_id (possible_clone))
                 {
-                  output->possible_clones[j] = possible_clone;
+                  output_info->possible_clones[j] = possible_clone;
                   break;
                 }
             }
diff --git a/backends/gf-gpu.c b/backends/gf-gpu.c
index 3a41e05..8759892 100644
--- a/backends/gf-gpu.c
+++ b/backends/gf-gpu.c
@@ -179,9 +179,13 @@ gf_gpu_has_hotplug_mode_update (GfGpu *gpu)
 
   for (l = priv->outputs; l; l = l->next)
     {
-      GfOutput *output = l->data;
+      GfOutput *output;
+      const GfOutputInfo *output_info;
 
-      if (output->hotplug_mode_update)
+      output = l->data;
+      output_info = gf_output_get_info (output);
+
+      if (output_info->hotplug_mode_update)
         return TRUE;
     }
 
diff --git a/backends/gf-monitor-config-manager.c b/backends/gf-monitor-config-manager.c
index 2d36a97..e167dc5 100644
--- a/backends/gf-monitor-config-manager.c
+++ b/backends/gf-monitor-config-manager.c
@@ -624,16 +624,19 @@ find_unassigned_crtc (GfOutput  *output,
                       GArray    *reserved_crtcs)
 {
   GfCrtc *crtc;
+  const GfOutputInfo *output_info;
   unsigned int i;
 
   crtc = gf_output_get_assigned_crtc (output);
   if (crtc && !is_crtc_assigned (crtc, crtc_assignments))
     return crtc;
 
+  output_info = gf_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;
@@ -645,9 +648,9 @@ find_unassigned_crtc (GfOutput  *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/backends/gf-monitor-manager.c b/backends/gf-monitor-manager.c
index f9a34bf..8aa1f54 100644
--- a/backends/gf-monitor-manager.c
+++ b/backends/gf-monitor-manager.c
@@ -990,7 +990,12 @@ get_connector_type_name (GfConnectorType connector_type)
 static gboolean
 is_main_tiled_monitor_output (GfOutput *output)
 {
-  return output->tile_info.loc_h_tile == 0 && output->tile_info.loc_v_tile == 0;
+  const GfOutputInfo *output_info;
+
+  output_info = gf_output_get_info (output);
+
+  return (output_info->tile_info.loc_h_tile == 0 &&
+          output_info->tile_info.loc_v_tile == 0);
 }
 
 static void
@@ -1018,8 +1023,11 @@ rebuild_monitors (GfMonitorManager *manager)
       for (k = gf_gpu_get_outputs (gpu); k; k = k->next)
         {
           GfOutput *output = k->data;
+          const GfOutputInfo *output_info;
+
+          output_info = gf_output_get_info (output);
 
-          if (output->tile_info.group_id)
+          if (output_info->tile_info.group_id)
             {
               if (is_main_tiled_monitor_output (output))
                 {
@@ -1139,73 +1147,94 @@ gf_monitor_manager_handle_get_resources (GfDBusDisplayConfig   *skeleton,
 
   for (l = combined_outputs, i = 0; l; l = l->next, i++)
     {
-      GfOutput *output = l->data;
+      GfOutput *output;
+      const GfOutputInfo *output_info;
       GVariantBuilder crtcs, modes, clones, properties;
       GBytes *edid;
       GfCrtc *crtc;
-      gint crtc_index;
+      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;
+
+      output = l->data;
+      output_info = gf_output_get_info (output);
 
       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++)
         {
           GfCrtc *possible_crtc;
           guint possible_crtc_index;
 
-          possible_crtc = output->possible_crtcs[j];
+          possible_crtc = output_info->possible_crtcs[j];
           possible_crtc_index = g_list_index (combined_crtcs, possible_crtc);
 
           g_variant_builder_add (&crtcs, "u", possible_crtc_index);
         }
 
       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++)
         {
           guint 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++)
         {
           guint 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 = gf_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 = gf_output_is_primary (output);
+      is_presentation = gf_output_is_presentation (output);
+      is_underscanning = gf_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 (gf_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 (gf_output_is_primary (output)));
+                             g_variant_new_boolean (is_primary));
       g_variant_builder_add (&properties, "{sv}", "presentation",
-                             g_variant_new_boolean (gf_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 (gf_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);
 
@@ -1217,18 +1246,21 @@ gf_monitor_manager_handle_get_resources (GfDBusDisplayConfig   *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 = gf_output_get_assigned_crtc (output);
@@ -1239,7 +1271,7 @@ gf_monitor_manager_handle_get_resources (GfDBusDisplayConfig   *skeleton,
                              gf_output_get_id (output),
                              crtc_index,
                              &crtcs,
-                             output->name,
+                             gf_output_get_name (output),
                              &modes,
                              &clones,
                              &properties);
@@ -1291,6 +1323,7 @@ gf_monitor_manager_handle_change_backlight (GfDBusDisplayConfig   *skeleton,
   GfMonitorManagerClass *manager_class;
   GList *combined_outputs;
   GfOutput *output;
+  const GfOutputInfo *output_info;
   int new_backlight;
 
   manager_class = GF_MONITOR_MANAGER_GET_CLASS (manager);
@@ -1326,8 +1359,11 @@ gf_monitor_manager_handle_change_backlight (GfDBusDisplayConfig   *skeleton,
       return TRUE;
     }
 
+  output_info = gf_output_get_info (output);
+
   if (gf_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,
diff --git a/backends/gf-monitor-normal.c b/backends/gf-monitor-normal.c
index f808b1b..40440d7 100644
--- a/backends/gf-monitor-normal.c
+++ b/backends/gf-monitor-normal.c
@@ -36,21 +36,23 @@ generate_modes (GfMonitorNormal *normal)
 {
   GfMonitor *monitor;
   GfOutput *output;
+  const GfOutputInfo *output_info;
   GfCrtcModeFlag preferred_mode_flags;
   guint i;
 
   monitor = GF_MONITOR (normal);
   output = gf_monitor_get_main_output (monitor);
-  preferred_mode_flags = output->preferred_mode->flags;
+  output_info = gf_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++)
     {
       GfCrtcMode *crtc_mode;
       GfMonitorMode *mode;
       gboolean replace;
       GfCrtc *crtc;
 
-      crtc_mode = output->modes[i];
+      crtc_mode = output_info->modes[i];
 
       mode = g_new0 (GfMonitorMode, 1);
       mode->monitor = monitor;
@@ -76,12 +78,12 @@ generate_modes (GfMonitorNormal *normal)
 
       if (!gf_monitor_add_mode (monitor, mode, replace))
         {
-          g_assert (crtc_mode != output->preferred_mode);
+          g_assert (crtc_mode != output_info->preferred_mode);
           gf_monitor_mode_free (mode);
           continue;
         }
 
-      if (crtc_mode == output->preferred_mode)
+      if (crtc_mode == output_info->preferred_mode)
         gf_monitor_set_preferred_mode (monitor, mode);
 
       crtc = gf_output_get_assigned_crtc (output);
@@ -132,14 +134,15 @@ gf_monitor_normal_get_suggested_position (GfMonitor *monitor,
                                           gint      *x,
                                           gint      *y)
 {
-  GfOutput *output;
+  const GfOutputInfo *output_info;
 
-  output = gf_monitor_get_main_output (monitor);
-  if (output->suggested_x < 0 && output->suggested_y < 0)
+  output_info = gf_monitor_get_main_output_info (monitor);
+
+  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;
 }
diff --git a/backends/gf-monitor-private.h b/backends/gf-monitor-private.h
index 3805fc4..85b9132 100644
--- a/backends/gf-monitor-private.h
+++ b/backends/gf-monitor-private.h
@@ -25,6 +25,7 @@
 #include "gf-gpu-private.h"
 #include "gf-monitor-manager-enums-private.h"
 #include "gf-monitor-manager-types-private.h"
+#include "gf-output-info-private.h"
 #include "gf-rectangle.h"
 
 G_BEGIN_DECLS
@@ -113,6 +114,8 @@ GfMonitorModeSpec   gf_monitor_create_spec                (GfMonitor
                                                            int                         height,
                                                            GfCrtcMode                 *crtc_mode);
 
+const GfOutputInfo *gf_monitor_get_main_output_info       (GfMonitor                  *self);
+
 void                gf_monitor_generate_spec              (GfMonitor                  *monitor);
 
 gboolean            gf_monitor_add_mode                   (GfMonitor                  *monitor,
diff --git a/backends/gf-monitor-tiled.c b/backends/gf-monitor-tiled.c
index 12b527a..4d750f3 100644
--- a/backends/gf-monitor-tiled.c
+++ b/backends/gf-monitor-tiled.c
@@ -53,24 +53,31 @@ static gboolean
 is_crtc_mode_tiled (GfOutput   *output,
                     GfCrtcMode *crtc_mode)
 {
-  return (crtc_mode->width == (gint) output->tile_info.tile_w &&
-          crtc_mode->height == (gint) output->tile_info.tile_h);
+  const GfOutputInfo *output_info;
+
+  output_info = gf_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 GfCrtcMode *
 find_tiled_crtc_mode (GfOutput   *output,
                       GfCrtcMode *reference_crtc_mode)
 {
+  const GfOutputInfo *output_info;
   GfCrtcMode *crtc_mode;
   guint i;
 
-  crtc_mode = output->preferred_mode;
+  output_info = gf_output_get_info (output);
+  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;
@@ -104,13 +111,15 @@ calculate_tiled_size (GfMonitor *monitor,
 
   for (l = outputs; l; l = l->next)
     {
-      GfOutput *output = l->data;
+      const GfOutputInfo *output_info;
 
-      if (output->tile_info.loc_v_tile == 0)
-        width += output->tile_info.tile_w;
+      output_info = gf_output_get_info (l->data);
 
-      if (output->tile_info.loc_h_tile == 0)
-        height += output->tile_info.tile_h;
+      if (output_info->tile_info.loc_v_tile == 0)
+        width += output_info->tile_info.tile_w;
+
+      if (output_info->tile_info.loc_h_tile == 0)
+        height += output_info->tile_info.tile_h;
     }
 
   *out_width = width;
@@ -152,14 +161,16 @@ create_tiled_monitor_mode (GfMonitorTiled *tiled,
   for (l = outputs, i = 0; l; l = l->next, i++)
     {
       GfOutput *output;
+      const GfOutputInfo *output_info;
       GfCrtcMode *tiled_crtc_mode;
 
       output = l->data;
+      output_info = gf_output_get_info (output);
       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", gf_output_get_name (output));
           gf_monitor_mode_free ((GfMonitorMode *) mode);
           return NULL;
         }
@@ -167,7 +178,8 @@ create_tiled_monitor_mode (GfMonitorTiled *tiled,
       mode->parent.crtc_modes[i].output = output;
       mode->parent.crtc_modes[i].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;
@@ -228,6 +240,7 @@ generate_tiled_monitor_modes (GfMonitorTiled *tiled)
 {
   GfMonitor *monitor;
   GfOutput *main_output;
+  const GfOutputInfo *main_output_info;
   GList *tiled_modes;
   GfMonitorMode *best_mode;
   guint i;
@@ -235,17 +248,18 @@ generate_tiled_monitor_modes (GfMonitorTiled *tiled)
 
   monitor = GF_MONITOR (tiled);
   main_output = gf_monitor_get_main_output (monitor);
+  main_output_info = gf_output_get_info (main_output);
 
   tiled_modes = NULL;
   best_mode = NULL;
 
-  for (i = 0; i < main_output->n_modes; i++)
+  for (i = 0; i < main_output_info->n_modes; i++)
     {
       GfCrtcMode *reference_crtc_mode;
       GfMonitorMode *mode;
       gboolean is_preferred;
 
-      reference_crtc_mode = main_output->modes[i];
+      reference_crtc_mode = main_output_info->modes[i];
 
       if (!is_crtc_mode_tiled (main_output, reference_crtc_mode))
         continue;
@@ -295,18 +309,20 @@ generate_untiled_monitor_modes (GfMonitorTiled *tiled)
 {
   GfMonitor *monitor;
   GfOutput *main_output;
+  const GfOutputInfo *main_output_info;
   guint i;
 
   monitor = GF_MONITOR (tiled);
   main_output = gf_monitor_get_main_output (monitor);
+  main_output_info = gf_output_get_info (main_output);
 
-  for (i = 0; i < main_output->n_modes; i++)
+  for (i = 0; i < main_output_info->n_modes; i++)
     {
       GfCrtcMode *crtc_mode;
       GfMonitorMode *mode;
       GfMonitorMode *preferred_mode;
 
-      crtc_mode = main_output->modes[i];
+      crtc_mode = main_output_info->modes[i];
       mode = create_untiled_monitor_mode (tiled, main_output, crtc_mode);
 
       if (!mode)
@@ -329,7 +345,7 @@ generate_untiled_monitor_modes (GfMonitorTiled *tiled)
         }
 
       preferred_mode = gf_monitor_get_preferred_mode (monitor);
-      if (!preferred_mode && crtc_mode == main_output->preferred_mode)
+      if (!preferred_mode && crtc_mode == main_output_info->preferred_mode)
         gf_monitor_set_preferred_mode (monitor, mode);
     }
 }
@@ -446,15 +462,18 @@ generate_modes (GfMonitorTiled *tiled)
 static int
 count_untiled_crtc_modes (GfOutput *output)
 {
+  const GfOutputInfo *output_info;
   gint count;
   guint i;
 
+  output_info = gf_output_get_info (output);
+
   count = 0;
-  for (i = 0; i < output->n_modes; i++)
+  for (i = 0; i < output_info->n_modes; i++)
     {
       GfCrtcMode *crtc_mode;
 
-      crtc_mode = output->modes[i];
+      crtc_mode = output_info->modes[i];
       if (!is_crtc_mode_tiled (output, crtc_mode))
         count++;
     }
@@ -511,10 +530,12 @@ add_tiled_monitor_outputs (GfGpu          *gpu,
   for (l = outputs; l; l = l->next)
     {
       GfOutput *output;
+      const GfOutputInfo *output_info;
 
       output = l->data;
+      output_info = gf_output_get_info (output);
 
-      if (output->tile_info.group_id != tiled->tile_group_id)
+      if (output_info->tile_info.group_id != tiled->tile_group_id)
         continue;
 
       gf_monitor_append_output (monitor, output);
@@ -528,60 +549,62 @@ calculate_tile_coordinate (GfMonitor          *monitor,
                            gint               *out_x,
                            gint               *out_y)
 {
+  const GfOutputInfo *output_info;
   GList *outputs;
   GList *l;
   gint x;
   gint y;
 
+  output_info = gf_output_get_info (output);
   outputs = gf_monitor_get_outputs (monitor);
   x = y = 0;
 
   for (l = outputs; l; l = l->next)
     {
-      GfOutput *other_output;
+      const GfOutputInfo *other_output_info;
 
-      other_output = l->data;
+      other_output_info = gf_output_get_info (l->data);
 
       switch (crtc_transform)
         {
           case GF_MONITOR_TRANSFORM_NORMAL:
           case GF_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 GF_MONITOR_TRANSFORM_180:
           case GF_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 GF_MONITOR_TRANSFORM_270:
           case GF_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 GF_MONITOR_TRANSFORM_90:
           case GF_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;
 
           default:
@@ -722,9 +745,12 @@ gf_monitor_tiled_new (GfGpu            *gpu,
                       GfMonitorManager *monitor_manager,
                       GfOutput         *output)
 {
+  const GfOutputInfo *output_info;
   GfMonitorTiled *tiled;
   GfMonitor *monitor;
 
+  output_info = gf_output_get_info (output);
+
   tiled = g_object_new (GF_TYPE_MONITOR_TILED,
                         "gpu", gpu,
                         NULL);
@@ -733,7 +759,7 @@ gf_monitor_tiled_new (GfGpu            *gpu,
 
   tiled->monitor_manager = monitor_manager;
 
-  tiled->tile_group_id = output->tile_info.group_id;
+  tiled->tile_group_id = output_info->tile_info.group_id;
   gf_monitor_set_winsys_id (monitor, gf_output_get_id (output));
 
   tiled->origin_output = output;
diff --git a/backends/gf-monitor.c b/backends/gf-monitor.c
index e5c44de..deaa526 100644
--- a/backends/gf-monitor.c
+++ b/backends/gf-monitor.c
@@ -603,12 +603,12 @@ gf_monitor_create_spec (GfMonitor  *monitor,
                         int         height,
                         GfCrtcMode *crtc_mode)
 {
-  GfOutput *output;
+  const GfOutputInfo *output_info;
   GfMonitorModeSpec spec;
 
-  output = gf_monitor_get_main_output (monitor);
+  output_info = gf_monitor_get_main_output_info (monitor);
 
-  if (gf_monitor_transform_is_rotated (output->panel_orientation_transform))
+  if (gf_monitor_transform_is_rotated (output_info->panel_orientation_transform))
     {
       int temp;
 
@@ -625,22 +625,32 @@ gf_monitor_create_spec (GfMonitor  *monitor,
   return spec;
 }
 
+const GfOutputInfo *
+gf_monitor_get_main_output_info (GfMonitor *self)
+{
+  GfOutput *output;
+
+  output = gf_monitor_get_main_output (self);
+
+  return gf_output_get_info (output);
+}
+
 void
 gf_monitor_generate_spec (GfMonitor *monitor)
 {
   GfMonitorPrivate *priv;
-  GfOutput *output;
+  const GfOutputInfo *output_info;
   GfMonitorSpec *monitor_spec;
 
   priv = gf_monitor_get_instance_private (monitor);
-  output = gf_monitor_get_main_output (monitor);
+  output_info = gf_monitor_get_main_output_info (monitor);
 
   monitor_spec = g_new0 (GfMonitorSpec, 1);
 
-  monitor_spec->connector = g_strdup (output->name);
-  monitor_spec->vendor = g_strdup (output->vendor);
-  monitor_spec->product = g_strdup (output->product);
-  monitor_spec->serial = g_strdup (output->serial);
+  monitor_spec->connector = g_strdup (output_info->name);
+  monitor_spec->vendor = g_strdup (output_info->vendor);
+  monitor_spec->product = g_strdup (output_info->product);
+  monitor_spec->serial = g_strdup (output_info->serial);
 
   priv->spec = monitor_spec;
 }
@@ -730,11 +740,11 @@ gf_monitor_is_primary (GfMonitor *monitor)
 gboolean
 gf_monitor_supports_underscanning (GfMonitor *monitor)
 {
-  GfOutput *output;
+  const GfOutputInfo *output_info;
 
-  output = gf_monitor_get_main_output (monitor);
+  output_info = gf_monitor_get_main_output_info (monitor);
 
-  return output->supports_underscanning;
+  return output_info->supports_underscanning;
 }
 
 gboolean
@@ -750,11 +760,11 @@ gf_monitor_is_underscanning (GfMonitor *monitor)
 gboolean
 gf_monitor_is_laptop_panel (GfMonitor *monitor)
 {
-  GfOutput *output;
+  const GfOutputInfo *output_info;
 
-  output = gf_monitor_get_main_output (monitor);
+  output_info = gf_monitor_get_main_output_info (monitor);
 
-  switch (output->connector_type)
+  switch (output_info->connector_type)
     {
       case GF_CONNECTOR_TYPE_eDP:
       case GF_CONNECTOR_TYPE_LVDS:
@@ -828,57 +838,62 @@ gf_monitor_get_physical_dimensions (GfMonitor *monitor,
                                     gint      *width_mm,
                                     gint      *height_mm)
 {
-  GfOutput *output;
+  const GfOutputInfo *output_info;
 
-  output = gf_monitor_get_main_output (monitor);
+  output_info = gf_monitor_get_main_output_info (monitor);
 
-  *width_mm = output->width_mm;
-  *height_mm = output->height_mm;
+  *width_mm = output_info->width_mm;
+  *height_mm = output_info->height_mm;
 }
 
 const gchar *
 gf_monitor_get_connector (GfMonitor *monitor)
 {
-  GfOutput *output;
+  const GfOutputInfo *output_info;
 
-  output = gf_monitor_get_main_output (monitor);
-  return output->name;
+  output_info = gf_monitor_get_main_output_info (monitor);
+
+  return output_info->name;
 }
 
 const gchar *
 gf_monitor_get_vendor (GfMonitor *monitor)
 {
-  GfOutput *output;
+  const GfOutputInfo *output_info;
 
-  output = gf_monitor_get_main_output (monitor);
-  return output->vendor;
+  output_info = gf_monitor_get_main_output_info (monitor);
+
+  return output_info->vendor;
 }
 
 const gchar *
 gf_monitor_get_product (GfMonitor *monitor)
 {
-  GfOutput *output;
+  const GfOutputInfo *output_info;
 
-  output = gf_monitor_get_main_output (monitor);
-  return output->product;
+  output_info = gf_monitor_get_main_output_info (monitor);
+
+  return output_info->product;
 }
 
 const gchar *
 gf_monitor_get_serial (GfMonitor *monitor)
 {
-  GfOutput *output;
+  const GfOutputInfo *output_info;
 
-  output = gf_monitor_get_main_output (monitor);
-  return output->serial;
+  output_info = gf_monitor_get_main_output_info (monitor);
+
+  return output_info->serial;
 }
 
 GfConnectorType
 gf_monitor_get_connector_type (GfMonitor *monitor)
 {
-  GfOutput *output;
+  const GfOutputInfo *output_info;
 
-  output = gf_monitor_get_main_output (monitor);
-  return output->connector_type;
+  output_info = gf_monitor_get_main_output_info (monitor);
+
+  return output_info->connector_type;
 }
 
 GfMonitorTransform
diff --git a/backends/gf-output-info-private.h b/backends/gf-output-info-private.h
new file mode 100644
index 0000000..2337314
--- /dev/null
+++ b/backends/gf-output-info-private.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2001 Havoc Pennington
+ * Copyright (C) 2003 Rob Adams
+ * Copyright (C) 2004-2006 Elijah Newren
+ * Copyright (C) 2013 Red Hat Inc.
+ * Copyright (C) 2017-2020 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GF_OUTPUT_INFO_PRIVATE_H
+#define GF_OUTPUT_INFO_PRIVATE_H
+
+#include <glib-object.h>
+
+#include "gf-monitor-manager-enums-private.h"
+#include "gf-monitor-manager-types-private.h"
+#include "gf-monitor-transform.h"
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+  guint32 group_id;
+  guint32 flags;
+  guint32 max_h_tiles;
+  guint32 max_v_tiles;
+  guint32 loc_h_tile;
+  guint32 loc_v_tile;
+  guint32 tile_w;
+  guint32 tile_h;
+} GfTileInfo;
+
+typedef struct
+{
+  grefcount            ref_count;
+
+  char                *name;
+  char                *vendor;
+  char                *product;
+  char                *serial;
+  int                  width_mm;
+  int                  height_mm;
+
+  GfConnectorType      connector_type;
+  GfMonitorTransform   panel_orientation_transform;
+
+  GfCrtcMode          *preferred_mode;
+  GfCrtcMode         **modes;
+  guint                n_modes;
+
+  GfCrtc             **possible_crtcs;
+  guint                n_possible_crtcs;
+
+  GfOutput           **possible_clones;
+  guint                n_possible_clones;
+
+  int                  backlight_min;
+  int                  backlight_max;
+
+  gboolean             supports_underscanning;
+
+  /* Get a new preferred mode on hotplug events, to handle
+   * dynamic guest resizing
+   */
+  gboolean             hotplug_mode_update;
+  int                  suggested_x;
+  int                  suggested_y;
+
+  GfTileInfo           tile_info;
+} GfOutputInfo;
+
+#define GF_TYPE_OUTPUT_INFO (gf_output_info_get_type ())
+
+GType         gf_output_info_get_type   (void);
+
+GfOutputInfo *gf_output_info_new        (void);
+
+GfOutputInfo *gf_output_info_ref        (GfOutputInfo *self);
+
+void          gf_output_info_unref      (GfOutputInfo *self);
+
+void          gf_output_info_parse_edid (GfOutputInfo *self,
+                                         GBytes       *edid);
+
+G_END_DECLS
+
+#endif
diff --git a/backends/gf-output-info.c b/backends/gf-output-info.c
new file mode 100644
index 0000000..83b5e0a
--- /dev/null
+++ b/backends/gf-output-info.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2001, 2002 Havoc Pennington
+ * Copyright (C) 2002, 2003 Red Hat Inc.
+ * Some ICCCM manager selection code derived from fvwm2,
+ * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team
+ * Copyright (C) 2003 Rob Adams
+ * Copyright (C) 2004-2006 Elijah Newren
+ * Copyright (C) 2013 Red Hat Inc.
+ * Copyright (C) 2017-2020 Alberts Muktupāvels
+ * Copyright (C) 2017 Red Hat
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "gf-output-info-private.h"
+
+#include "gf-edid-private.h"
+
+G_DEFINE_BOXED_TYPE (GfOutputInfo,
+                     gf_output_info,
+                     gf_output_info_ref,
+                     gf_output_info_unref)
+
+GfOutputInfo *
+gf_output_info_new (void)
+{
+  GfOutputInfo *self;
+
+  self = g_new0 (GfOutputInfo, 1);
+  g_ref_count_init (&self->ref_count);
+
+  return self;
+}
+
+GfOutputInfo *
+gf_output_info_ref (GfOutputInfo *self)
+{
+  g_ref_count_inc (&self->ref_count);
+
+  return self;
+}
+
+void
+gf_output_info_unref (GfOutputInfo *self)
+{
+  if (g_ref_count_dec (&self->ref_count))
+    {
+      g_free (self->name);
+      g_free (self->vendor);
+      g_free (self->product);
+      g_free (self->serial);
+      g_free (self->modes);
+      g_free (self->possible_crtcs);
+      g_free (self->possible_clones);
+      g_free (self);
+    }
+}
+
+void
+gf_output_info_parse_edid (GfOutputInfo *output_info,
+                           GBytes       *edid)
+{
+  MonitorInfo *parsed_edid;
+  gsize len;
+
+  if (!edid)
+    {
+      output_info->vendor = g_strdup ("unknown");
+      output_info->product = g_strdup ("unknown");
+      output_info->serial = g_strdup ("unknown");
+      return;
+    }
+
+  parsed_edid = decode_edid (g_bytes_get_data (edid, &len));
+  if (parsed_edid)
+    {
+      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_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_info->product, g_free);
+          output_info->product = g_strdup_printf ("0x%04x", (unsigned) parsed_edid->product_code);
+        }
+
+      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_info->serial, g_free);
+          output_info->serial = g_strdup_printf ("0x%08x", parsed_edid->serial_number);
+        }
+
+      g_free (parsed_edid);
+    }
+
+  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");
+}
diff --git a/backends/gf-output-private.h b/backends/gf-output-private.h
index 4d8956d..f7cbbb0 100644
--- a/backends/gf-output-private.h
+++ b/backends/gf-output-private.h
@@ -29,23 +29,10 @@
 #include <stdint.h>
 
 #include "gf-gpu-private.h"
-#include "gf-monitor-manager-enums-private.h"
-#include "gf-monitor-manager-types-private.h"
+#include "gf-output-info-private.h"
 
 G_BEGIN_DECLS
 
-typedef struct
-{
-  guint32 group_id;
-  guint32 flags;
-  guint32 max_h_tiles;
-  guint32 max_v_tiles;
-  guint32 loc_h_tile;
-  guint32 loc_v_tile;
-  guint32 tile_w;
-  guint32 tile_h;
-} GfTileInfo;
-
 typedef struct
 {
   GfOutput *output;
@@ -56,44 +43,10 @@ typedef struct
 
 struct _GfOutput
 {
-  GObject              parent;
-
-  gchar               *name;
-  gchar               *vendor;
-  gchar               *product;
-  gchar               *serial;
-  gint                 width_mm;
-  gint                 height_mm;
-
-  GfConnectorType      connector_type;
-  GfMonitorTransform   panel_orientation_transform;
-
-  GfCrtcMode          *preferred_mode;
-  GfCrtcMode         **modes;
-  guint                n_modes;
-
-  GfCrtc             **possible_crtcs;
-  guint                n_possible_crtcs;
-
-  GfOutput           **possible_clones;
-  guint                n_possible_clones;
+  GObject        parent;
 
-  gint                 backlight_min;
-  gint                 backlight_max;
-
-  gboolean             supports_underscanning;
-
-  gpointer             driver_private;
-  GDestroyNotify       driver_notify;
-
-  /* Get a new preferred mode on hotplug events, to handle
-   * dynamic guest resizing
-   */
-  gboolean             hotplug_mode_update;
-  gint                 suggested_x;
-  gint                 suggested_y;
-
-  GfTileInfo           tile_info;
+  gpointer       driver_private;
+  GDestroyNotify driver_notify;
 };
 
 #define GF_TYPE_OUTPUT (gf_output_get_type ())
@@ -103,6 +56,8 @@ uint64_t            gf_output_get_id                    (GfOutput
 
 GfGpu              *gf_output_get_gpu                   (GfOutput                 *self);
 
+const GfOutputInfo *gf_output_get_info                  (GfOutput                 *self);
+
 const char         *gf_output_get_name                  (GfOutput                 *self);
 
 void                gf_output_assign_crtc               (GfOutput                 *self,
@@ -113,9 +68,6 @@ void                gf_output_unassign_crtc             (GfOutput
 
 GfCrtc             *gf_output_get_assigned_crtc         (GfOutput                 *self);
 
-void                gf_output_parse_edid                (GfOutput                 *self,
-                                                         GBytes                   *edid);
-
 gboolean            gf_output_is_laptop                 (GfOutput                 *self);
 
 GfMonitorTransform  gf_output_logical_to_crtc_transform (GfOutput                 *self,
@@ -135,6 +87,9 @@ void                gf_output_set_backlight             (GfOutput
 
 int                 gf_output_get_backlight             (GfOutput                 *self);
 
+void                gf_output_add_possible_clone        (GfOutput                 *self,
+                                                         GfOutput                 *possible_clone);
+
 static inline GfOutputAssignment *
 gf_find_output_assignment (GfOutputAssignment **outputs,
                            unsigned int         n_outputs,
diff --git a/backends/gf-output-xrandr.c b/backends/gf-output-xrandr.c
index 46fe1dc..33affae 100644
--- a/backends/gf-output-xrandr.c
+++ b/backends/gf-output-xrandr.c
@@ -35,14 +35,12 @@
 #include "gf-monitor-manager-xrandr-private.h"
 
 static Display *
-xdisplay_from_output (GfOutput *output)
+xdisplay_from_gpu (GfGpu *gpu)
 {
-  GfGpu *gpu;
   GfBackend *backend;
   GfMonitorManager *monitor_manager;
   GfMonitorManagerXrandr *monitor_manager_xrandr;
 
-  gpu = gf_output_get_gpu (output);
   backend = gf_gpu_get_backend (gpu);
   monitor_manager = gf_backend_get_monitor_manager (backend);
   monitor_manager_xrandr = GF_MONITOR_MANAGER_XRANDR (monitor_manager);
@@ -50,6 +48,12 @@ xdisplay_from_output (GfOutput *output)
   return gf_monitor_manager_xrandr_get_xdisplay (monitor_manager_xrandr);
 }
 
+static Display *
+xdisplay_from_output (GfOutput *output)
+{
+  return xdisplay_from_gpu (gf_output_get_gpu (output));
+}
+
 static void
 output_set_presentation_xrandr (GfOutput *output,
                                 gboolean  presentation)
@@ -157,20 +161,47 @@ get_edid_property (Display  *xdisplay,
   return result;
 }
 
+static GBytes *
+read_xrandr_edid (Display  *xdisplay,
+                  RROutput  output_id)
+{
+  Atom edid_atom;
+  guint8 *result;
+  gsize len;
+
+  edid_atom = XInternAtom (xdisplay, "EDID", FALSE);
+  result = get_edid_property (xdisplay, output_id, edid_atom, &len);
+
+  if (!result)
+    {
+      edid_atom = XInternAtom (xdisplay, "EDID_DATA", FALSE);
+      result = get_edid_property (xdisplay, output_id, edid_atom,  &len);
+    }
+
+  if (result)
+    {
+      if (len > 0 && len % 128 == 0)
+        return g_bytes_new_take (result, len);
+      else
+        g_free (result);
+    }
+
+  return NULL;
+}
+
 static gboolean
-output_get_property_exists (GfOutput    *output,
+output_get_property_exists (Display     *xdisplay,
+                            RROutput     output_id,
                             const gchar *propname)
 {
-  Display *xdisplay;
   gboolean exists;
   Atom atom, actual_type;
   gint actual_format;
   gulong nitems, bytes_after;
   guchar *buffer;
 
-  xdisplay = xdisplay_from_output (output);
   atom = XInternAtom (xdisplay, propname, False);
-  XRRGetOutputProperty (xdisplay, (XID) gf_output_get_id (output), atom,
+  XRRGetOutputProperty (xdisplay, (XID) output_id, atom,
                         0, G_MAXLONG, False, False, AnyPropertyType,
                         &actual_type, &actual_format,
                         &nitems, &bytes_after, &buffer);
@@ -184,26 +215,26 @@ output_get_property_exists (GfOutput    *output,
 }
 
 static gboolean
-output_get_hotplug_mode_update (GfOutput *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 gboolean
-output_get_integer_property (GfOutput    *output,
+output_get_integer_property (Display     *xdisplay,
+                             RROutput     output_id,
                              const gchar *propname,
                              gint        *value)
 {
-  Display *xdisplay;
   gboolean exists;
   Atom atom, actual_type;
   gint actual_format;
   gulong nitems, bytes_after;
   guchar *buffer;
 
-  xdisplay = xdisplay_from_output (output);
   atom = XInternAtom (xdisplay, propname, False);
-  XRRGetOutputProperty (xdisplay, (XID) gf_output_get_id (output), atom,
+  XRRGetOutputProperty (xdisplay, (XID) output_id, atom,
                         0, G_MAXLONG, False, False, XA_INTEGER,
                         &actual_type, &actual_format,
                         &nitems, &bytes_after, &buffer);
@@ -220,22 +251,24 @@ output_get_integer_property (GfOutput    *output,
 }
 
 static gint
-output_get_suggested_x (GfOutput *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 (GfOutput *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;
@@ -278,18 +311,17 @@ connector_type_from_atom (Display *xdisplay,
 }
 
 static GfConnectorType
-output_get_connector_type_from_prop (GfOutput *output)
+output_get_connector_type_from_prop (Display  *xdisplay,
+                                     RROutput  output_id)
 {
-  Display *xdisplay;
   Atom atom, actual_type, connector_type_atom;
   gint actual_format;
   gulong nitems, bytes_after;
   guchar *buffer;
   GfConnectorType ret;
 
-  xdisplay = xdisplay_from_output (output);
   atom = XInternAtom (xdisplay, "ConnectorType", False);
-  XRRGetOutputProperty (xdisplay, (XID) gf_output_get_id (output), atom,
+  XRRGetOutputProperty (xdisplay, (XID) output_id, atom,
                         0, G_MAXLONG, False, False, XA_ATOM,
                         &actual_type, &actual_format,
                         &nitems, &bytes_after, &buffer);
@@ -310,11 +342,11 @@ output_get_connector_type_from_prop (GfOutput *output)
 }
 
 static GfConnectorType
-output_get_connector_type_from_name (GfOutput *output)
+output_info_get_connector_type_from_name (const GfOutputInfo *output_info)
 {
   const gchar *name;
 
-  name = output->name;
+  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
@@ -360,7 +392,9 @@ output_get_connector_type_from_name (GfOutput *output)
 }
 
 static GfConnectorType
-output_get_connector_type (GfOutput *output)
+output_info_get_connector_type (GfOutputInfo *output_info,
+                                Display      *xdisplay,
+                                RROutput      output_id)
 {
   GfConnectorType ret;
 
@@ -371,12 +405,12 @@ output_get_connector_type (GfOutput *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 != GF_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 != GF_CONNECTOR_TYPE_Unknown)
     return ret;
 
@@ -384,9 +418,9 @@ output_get_connector_type (GfOutput *output)
 }
 
 static GfMonitorTransform
-output_get_panel_orientation_transform (GfOutput *output)
+output_get_panel_orientation_transform (Display  *xdisplay,
+                                        RROutput  output_id)
 {
-  Display *xdisplay;
   unsigned long nitems;
   unsigned long bytes_after;
   Atom atom;
@@ -396,12 +430,11 @@ output_get_panel_orientation_transform (GfOutput *output)
   char *str;
   GfMonitorTransform transform;
 
-  xdisplay = xdisplay_from_output (output);
   buffer = NULL;
   str = NULL;
 
   atom = XInternAtom (xdisplay, "panel orientation", False);
-  XRRGetOutputProperty (xdisplay, (XID) gf_output_get_id (output), atom,
+  XRRGetOutputProperty (xdisplay, (XID) output_id, atom,
                         0, G_MAXLONG, False, False, XA_ATOM,
                         &actual_type, &actual_format,
                         &nitems, &bytes_after, &buffer);
@@ -430,30 +463,18 @@ output_get_panel_orientation_transform (GfOutput *output)
 }
 
 static void
-output_get_tile_info (GfOutput *output)
+output_info_init_tile_info (GfOutputInfo *output_info,
+                            Display      *xdisplay,
+                            RROutput      output_id)
 {
-  GfGpu *gpu;
-  GfBackend *backend;
-  GfMonitorManager *monitor_manager;
-  GfMonitorManagerXrandr *monitor_manager_xrandr;
-  Display *xdisplay;
   Atom tile_atom;
   guchar *prop;
   gulong nitems, bytes_after;
   gint actual_format;
   Atom actual_type;
 
-  gpu = gf_output_get_gpu (output);
-  backend = gf_gpu_get_backend (gpu);
-  monitor_manager = gf_backend_get_monitor_manager (backend);
-  monitor_manager_xrandr = GF_MONITOR_MANAGER_XRANDR (monitor_manager);
-
-  if (!gf_monitor_manager_xrandr_has_randr15 (monitor_manager_xrandr))
-    return;
-
-  xdisplay = gf_monitor_manager_xrandr_get_xdisplay (monitor_manager_xrandr);
   tile_atom = XInternAtom (xdisplay, "TILE", FALSE);
-  XRRGetOutputProperty (xdisplay, gf_output_get_id (output),
+  XRRGetOutputProperty (xdisplay, (XID) output_id,
                         tile_atom, 0, 100, False,
                         False, AnyPropertyType,
                         &actual_type, &actual_format,
@@ -462,14 +483,15 @@ output_get_tile_info (GfOutput *output)
   if (actual_type == XA_INTEGER && actual_format == 32 && nitems == 8)
     {
       glong *values = (glong *) 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];
     }
 
   if (prop)
@@ -477,16 +499,14 @@ output_get_tile_info (GfOutput *output)
 }
 
 static void
-output_get_modes (GfOutput      *output,
-                  XRROutputInfo *xrandr_output)
+output_info_init_modes (GfOutputInfo  *output_info,
+                        GfGpu         *gpu,
+                        XRROutputInfo *xrandr_output)
 {
-  GfGpu *gpu;
   guint j;
   guint n_actual_modes;
 
-  gpu = gf_output_get_gpu (output);
-
-  output->modes = g_new0 (GfCrtcMode *, xrandr_output->nmode);
+  output_info->modes = g_new0 (GfCrtcMode *, xrandr_output->nmode);
 
   n_actual_modes = 0;
   for (j = 0; j < (guint) xrandr_output->nmode; j++)
@@ -499,31 +519,28 @@ output_get_modes (GfOutput      *output,
 
           if (xrandr_output->modes[j] == (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 (GfOutput       *output,
-                  XRROutputInfo  *xrandr_output,
-                  GfCrtc        **assigned_crtc)
+output_info_init_crtcs (GfOutputInfo  *output_info,
+                        GfGpu         *gpu,
+                        XRROutputInfo *xrandr_output)
 {
-  GfGpu *gpu;
   guint j;
   guint n_actual_crtcs;
   GList *l;
 
-  gpu = gf_output_get_gpu (output);
-
-  output->possible_crtcs = g_new0 (GfCrtc *, xrandr_output->ncrtc);
+  output_info->possible_crtcs = g_new0 (GfCrtc *, xrandr_output->ncrtc);
 
   n_actual_crtcs = 0;
   for (j = 0; j < (guint) xrandr_output->ncrtc; j++)
@@ -534,27 +551,30 @@ output_get_crtcs (GfOutput       *output,
 
           if ((XID) gf_crtc_get_id (crtc) == xrandr_output->crtcs[j])
             {
-              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 GfCrtc *
+find_assigned_crtc (GfGpu         *gpu,
+                    XRROutputInfo *xrandr_output)
+{
+  GList *l;
 
-  gf_output_unassign_crtc (output);
   for (l = gf_gpu_get_crtcs (gpu); l; l = l->next)
     {
       GfCrtc *crtc = l->data;
 
       if ((XID) gf_crtc_get_id (crtc) == xrandr_output->crtc)
-        {
-          *assigned_crtc = crtc;
-          return;
-        }
+        return crtc;
     }
 
-  *assigned_crtc = NULL;
+  return NULL;
 }
 
 static gboolean
@@ -631,9 +651,9 @@ output_get_underscanning_xrandr (GfOutput *output)
 }
 
 static gboolean
-output_get_supports_underscanning_xrandr (GfOutput *output)
+output_get_supports_underscanning_xrandr (Display  *xdisplay,
+                                          RROutput  output_id)
 {
-  Display *xdisplay;
   Atom atom, actual_type;
   gint actual_format, i;
   gulong nitems, bytes_after;
@@ -642,9 +662,8 @@ output_get_supports_underscanning_xrandr (GfOutput *output)
   Atom *values;
   gboolean supports_underscanning = FALSE;
 
-  xdisplay = xdisplay_from_output (output);
   atom = XInternAtom (xdisplay, "underscan", False);
-  XRRGetOutputProperty (xdisplay, (XID) gf_output_get_id (output), atom,
+  XRRGetOutputProperty (xdisplay, (XID) output_id, atom,
                         0, G_MAXLONG, False, False, XA_ATOM,
                         &actual_type, &actual_format,
                         &nitems, &bytes_after, &buffer);
@@ -657,9 +676,7 @@ output_get_supports_underscanning_xrandr (GfOutput *output)
       return FALSE;
     }
 
-  property_info = XRRQueryOutputProperty (xdisplay,
-                                          (XID) gf_output_get_id (output),
-                                          atom);
+  property_info = XRRQueryOutputProperty (xdisplay, (XID) output_id, atom);
   values = (Atom *) property_info->values;
 
   for (i = 0; i < property_info->num_values; i++)
@@ -683,8 +700,12 @@ static int
 normalize_backlight (GfOutput *output,
                      gint      hw_value)
 {
-  return round ((gdouble) (hw_value - output->backlight_min) /
-                (output->backlight_max - output->backlight_min) * 100.0);
+  const GfOutputInfo *output_info;
+
+  output_info = gf_output_get_info (output);
+
+  return round ((double) (hw_value - output_info->backlight_min) /
+                (output_info->backlight_max - output_info->backlight_min) * 100.0);
 }
 
 static gint
@@ -722,21 +743,19 @@ output_get_backlight_xrandr (GfOutput *output)
 }
 
 static void
-output_get_backlight_limits_xrandr (GfOutput *output)
+output_info_init_backlight_limits_xrandr (GfOutputInfo       *output_info,
+                                          Display            *xdisplay,
+                                          xcb_randr_output_t  output_id)
 {
-  Display *xdisplay;
   Atom atom;
   xcb_connection_t *xcb_conn;
-  xcb_randr_output_t output_id;
   xcb_randr_query_output_property_cookie_t cookie;
   xcb_randr_query_output_property_reply_t *reply;
   int32_t *values;
 
-  xdisplay = xdisplay_from_output (output);
   atom = XInternAtom (xdisplay, "Backlight", False);
 
   xcb_conn = XGetXCBConnection (xdisplay);
-  output_id = gf_output_get_id (output);
   cookie = xcb_randr_query_output_property (xcb_conn,
                                             output_id,
                                             (xcb_atom_t) atom);
@@ -749,15 +768,15 @@ output_get_backlight_limits_xrandr (GfOutput *output)
 
   if (!reply->range || reply->length != 2)
     {
-      g_warning ("backlight %s was not range\n", output->name);
+      g_warning ("backlight %s was not range\n", output_info->name);
       g_free (reply);
       return;
     }
 
   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];
 
   g_free (reply);
 }
@@ -768,43 +787,81 @@ gf_create_xrandr_output (GfGpuXrandr   *gpu_xrandr,
                          RROutput       output_id,
                          RROutput       primary_output)
 {
+  GfGpu *gpu;
+  GfBackend *backend;
+  GfMonitorManager *monitor_manager;
+  GfMonitorManagerXrandr *monitor_manager_xrandr;
+  Display *xdisplay;
+  GfOutputInfo *output_info;
   GfOutput *output;
   GBytes *edid;
   GfCrtc *assigned_crtc;
   unsigned int i;
 
-  output = g_object_new (GF_TYPE_OUTPUT,
-                         "id", output_id,
-                         "gpu", GF_GPU (gpu_xrandr),
-                         NULL);
+  gpu = GF_GPU (gpu_xrandr);
+  backend = gf_gpu_get_backend (gpu);
+  monitor_manager = gf_backend_get_monitor_manager (backend);
+  monitor_manager_xrandr = GF_MONITOR_MANAGER_XRANDR (monitor_manager);
+  xdisplay = gf_monitor_manager_xrandr_get_xdisplay (monitor_manager_xrandr);
 
-  output->name = g_strdup (xrandr_output->name);
+  output_info = gf_output_info_new ();
 
-  edid = gf_output_xrandr_read_edid (output);
-  gf_output_parse_edid (output, edid);
-  g_bytes_unref (edid);
+  output_info->name = g_strdup (xrandr_output->name);
 
-  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);
+  edid = read_xrandr_edid (xdisplay, output_id);
+  gf_output_info_parse_edid (output_info, edid);
+  g_bytes_unref (edid);
 
-  if (gf_monitor_transform_is_rotated (output->panel_orientation_transform))
+  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 (gf_monitor_transform_is_rotated (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 (!gf_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 (GfOutput *, 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 (GF_TYPE_OUTPUT,
+                         "id", output_id,
+                         "gpu", gpu_xrandr,
+                         "info", output_info,
+                         NULL);
+
+  assigned_crtc = find_assigned_crtc (gpu, xrandr_output);
   if (assigned_crtc)
     {
       GfOutputAssignment output_assignment;
@@ -817,32 +874,23 @@ gf_create_xrandr_output (GfGpuXrandr   *gpu_xrandr,
 
       gf_output_assign_crtc (output, assigned_crtc, &output_assignment);
     }
-
-  output->n_possible_clones = xrandr_output->nclone;
-  output->possible_clones = g_new0 (GfOutput *, 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]);
+      gf_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))
     gf_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)
     {
+      gf_output_info_unref (output_info);
       g_object_unref (output);
       return NULL;
     }
 
+  gf_output_info_unref (output_info);
+
   return output;
 }
 
@@ -850,35 +898,12 @@ GBytes *
 gf_output_xrandr_read_edid (GfOutput *output)
 {
   Display *xdisplay;
-  Atom edid_atom;
-  guint8 *result;
-  gsize len;
+  RROutput output_id;
 
   xdisplay = xdisplay_from_output (output);
-  edid_atom = XInternAtom (xdisplay, "EDID", FALSE);
-  result = get_edid_property (xdisplay,
-                              gf_output_get_id (output),
-                              edid_atom,
-                              &len);
-
-  if (!result)
-    {
-      edid_atom = XInternAtom (xdisplay, "EDID_DATA", FALSE);
-      result = get_edid_property (xdisplay,
-                                  gf_output_get_id (output),
-                                  edid_atom,
-                                  &len);
-    }
-
-  if (result)
-    {
-      if (len > 0 && len % 128 == 0)
-        return g_bytes_new_take (result, len);
-      else
-        g_free (result);
-    }
+  output_id = (RROutput) gf_output_get_id (output);
 
-  return NULL;
+  return read_xrandr_edid (xdisplay, output_id);
 }
 
 void
@@ -896,7 +921,7 @@ gf_output_xrandr_apply_mode (GfOutput *output)
 
   output_set_presentation_xrandr (output, gf_output_is_presentation (output));
 
-  if (output->supports_underscanning)
+  if (gf_output_get_info (output)->supports_underscanning)
     {
       output_set_underscanning_xrandr (output,
                                        gf_output_is_underscanning (output));
@@ -907,12 +932,14 @@ void
 gf_output_xrandr_change_backlight (GfOutput *output,
                                    int       value)
 {
+  const GfOutputInfo *output_info;
   Display *xdisplay;
   gint hw_value;
   Atom atom;
 
+  output_info = gf_output_get_info (output);
   xdisplay = xdisplay_from_output (output);
-  hw_value = round ((gdouble) 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);
 
   xcb_randr_change_output_property (XGetXCBConnection (xdisplay),
diff --git a/backends/gf-output.c b/backends/gf-output.c
index 28d34d8..a26ddf5 100644
--- a/backends/gf-output.c
+++ b/backends/gf-output.c
@@ -27,24 +27,25 @@
  */
 
 #include "config.h"
-#include "gf-edid-private.h"
 #include "gf-output-private.h"
 
 typedef struct
 {
-  uint64_t  id;
+  uint64_t      id;
 
-  GfGpu    *gpu;
+  GfGpu        *gpu;
+
+  GfOutputInfo *info;
 
   /* The CRTC driving this output, NULL if the output is not enabled */
-  GfCrtc   *crtc;
+  GfCrtc       *crtc;
 
-  gboolean  is_primary;
-  gboolean  is_presentation;
+  gboolean      is_primary;
+  gboolean      is_presentation;
 
-  gboolean  is_underscanning;
+  gboolean      is_underscanning;
 
-  int       backlight;
+  int           backlight;
 } GfOutputPrivate;
 
 enum
@@ -53,6 +54,7 @@ enum
 
   PROP_ID,
   PROP_GPU,
+  PROP_INFO,
 
   LAST_PROP
 };
@@ -79,16 +81,12 @@ static void
 gf_output_finalize (GObject *object)
 {
   GfOutput *output;
+  GfOutputPrivate *priv;
 
   output = GF_OUTPUT (object);
+  priv = gf_output_get_instance_private (output);
 
-  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);
+  g_clear_pointer (&priv->info, gf_output_info_unref);
 
   if (output->driver_notify)
     output->driver_notify (output);
@@ -118,6 +116,10 @@ gf_output_get_property (GObject    *object,
         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, property_id, pspec);
         break;
@@ -146,6 +148,10 @@ gf_output_set_property (GObject      *object,
         priv->gpu = g_value_get_object (value);
         break;
 
+      case PROP_INFO:
+        priv->info = gf_output_info_ref (g_value_get_boxed (value));
+        break;
+
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
         break;
@@ -184,6 +190,15 @@ gf_output_class_init (GfOutputClass *output_class)
                          G_PARAM_CONSTRUCT_ONLY |
                          G_PARAM_STATIC_STRINGS);
 
+  output_properties[PROP_INFO] =
+    g_param_spec_boxed ("info",
+                        "info",
+                        "GfOutputInfo",
+                        GF_TYPE_OUTPUT_INFO,
+                        G_PARAM_READWRITE |
+                        G_PARAM_CONSTRUCT_ONLY |
+                        G_PARAM_STATIC_STRINGS);
+
   g_object_class_install_properties (object_class, LAST_PROP,
                                      output_properties);
 }
@@ -218,10 +233,24 @@ gf_output_get_gpu (GfOutput *output)
   return priv->gpu;
 }
 
+const GfOutputInfo *
+gf_output_get_info (GfOutput *self)
+{
+  GfOutputPrivate *priv;
+
+  priv = gf_output_get_instance_private (self);
+
+  return priv->info;
+}
+
 const char *
 gf_output_get_name (GfOutput *self)
 {
-  return self->name;
+  GfOutputPrivate *priv;
+
+  priv = gf_output_get_instance_private (self);
+
+  return priv->info->name;
 }
 
 void
@@ -265,59 +294,14 @@ gf_output_get_assigned_crtc (GfOutput *output)
   return priv->crtc;
 }
 
-void
-gf_output_parse_edid (GfOutput *output,
-                      GBytes   *edid)
-{
-  MonitorInfo *parsed_edid;
-  gsize len;
-
-  if (!edid)
-    {
-      output->vendor = g_strdup ("unknown");
-      output->product = g_strdup ("unknown");
-      output->serial = g_strdup ("unknown");
-      return;
-    }
-
-  parsed_edid = decode_edid (g_bytes_get_data (edid, &len));
-  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->product = g_strndup (parsed_edid->dsc_product_name, 14);
-      if (!g_utf8_validate (output->product, -1, NULL) || output->product[0] == '\0')
-        {
-          g_clear_pointer (&output->product, g_free);
-          output->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')
-        {
-          g_clear_pointer (&output->serial, g_free);
-          output->serial = g_strdup_printf ("0x%08x", parsed_edid->serial_number);
-        }
-
-      g_free (parsed_edid);
-    }
-
-  if (!output->vendor)
-    output->vendor = g_strdup ("unknown");
-
-  if (!output->product)
-    output->product = g_strdup ("unknown");
-
-  if (!output->serial)
-    output->serial = g_strdup ("unknown");
-}
-
 gboolean
 gf_output_is_laptop (GfOutput *output)
 {
-  switch (output->connector_type)
+  const GfOutputInfo *output_info;
+
+  output_info = gf_output_get_info (output);
+
+  switch (output_info->connector_type)
     {
       case GF_CONNECTOR_TYPE_LVDS:
       case GF_CONNECTOR_TYPE_eDP:
@@ -349,18 +333,27 @@ GfMonitorTransform
 gf_output_logical_to_crtc_transform (GfOutput           *output,
                                      GfMonitorTransform  transform)
 {
-  return gf_monitor_transform_transform (transform,
-                                         output->panel_orientation_transform);
+  GfOutputPrivate *priv;
+  GfMonitorTransform panel_orientation_transform;
+
+  priv = gf_output_get_instance_private (output);
+
+  panel_orientation_transform = priv->info->panel_orientation_transform;
+
+  return gf_monitor_transform_transform (transform, panel_orientation_transform);
 }
 
 GfMonitorTransform
 gf_output_crtc_to_logical_transform (GfOutput           *output,
                                      GfMonitorTransform  transform)
 {
+  GfOutputPrivate *priv;
   GfMonitorTransform panel_orientation_transform;
   GfMonitorTransform inverted_transform;
 
-  panel_orientation_transform = output->panel_orientation_transform;
+  priv = gf_output_get_instance_private (output);
+
+  panel_orientation_transform = priv->info->panel_orientation_transform;
   inverted_transform = gf_monitor_transform_invert (panel_orientation_transform);
 
   return gf_monitor_transform_transform (transform, inverted_transform);
@@ -416,3 +409,21 @@ gf_output_get_backlight (GfOutput *self)
 
   return priv->backlight;
 }
+
+void
+gf_output_add_possible_clone (GfOutput *self,
+                              GfOutput *possible_clone)
+{
+  GfOutputPrivate *priv;
+  GfOutputInfo *output_info;
+
+  priv = gf_output_get_instance_private (self);
+
+  output_info = priv->info;
+
+  output_info->n_possible_clones++;
+  output_info->possible_clones = g_renew (GfOutput *,
+                                          output_info->possible_clones,
+                                          output_info->n_possible_clones);
+  output_info->possible_clones[output_info->n_possible_clones - 1] = possible_clone;
+}


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