[gnome-flashback] backends: implement GfMonitorManagerXrandr



commit 659728c32e6dcc8ab7bdbb4a9302447afdb6b962
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Thu Sep 21 00:41:40 2017 +0300

    backends: implement GfMonitorManagerXrandr

 backends/gf-monitor-manager-private.h |   70 ++-
 backends/gf-monitor-manager-xrandr.c  | 1465 ++++++++++++++++++++++++++++++++-
 backends/gf-monitor-manager.c         |  708 ++++++++++++++++-
 configure.ac                          |    2 +
 4 files changed, 2222 insertions(+), 23 deletions(-)
---
diff --git a/backends/gf-monitor-manager-private.h b/backends/gf-monitor-manager-private.h
index 4941c93..69a5b04 100644
--- a/backends/gf-monitor-manager-private.h
+++ b/backends/gf-monitor-manager-private.h
@@ -157,36 +157,68 @@ typedef struct
 
 GType                       gf_monitor_manager_get_type                     (void);
 
-GfBackend                  *gf_monitor_manager_get_backend                  (GfMonitorManager   *manager);
+GfBackend                  *gf_monitor_manager_get_backend                  (GfMonitorManager            
*manager);
 
-GfMonitor                  *gf_monitor_manager_get_primary_monitor          (GfMonitorManager   *manager);
+void                        gf_monitor_manager_rebuild_derived              (GfMonitorManager            
*manager,
+                                                                             GfMonitorsConfig            
*config);
 
-GfMonitor                  *gf_monitor_manager_get_laptop_panel             (GfMonitorManager   *manager);
+GfMonitor                  *gf_monitor_manager_get_primary_monitor          (GfMonitorManager            
*manager);
 
-GfMonitor                  *gf_monitor_manager_get_monitor_from_spec        (GfMonitorManager   *manager,
-                                                                             GfMonitorSpec      
*monitor_spec);
+GfMonitor                  *gf_monitor_manager_get_laptop_panel             (GfMonitorManager            
*manager);
 
-GList                      *gf_monitor_manager_get_monitors                 (GfMonitorManager   *manager);
+GfMonitor                  *gf_monitor_manager_get_monitor_from_spec        (GfMonitorManager            
*manager,
+                                                                             GfMonitorSpec               
*monitor_spec);
 
-void                        gf_monitor_manager_tiled_monitor_added          (GfMonitorManager   *manager,
-                                                                             GfMonitor          *monitor);
+GList                      *gf_monitor_manager_get_monitors                 (GfMonitorManager            
*manager);
 
-void                        gf_monitor_manager_tiled_monitor_removed        (GfMonitorManager   *manager,
-                                                                             GfMonitor          *monitor);
+gboolean                    gf_monitor_manager_has_hotplug_mode_update      (GfMonitorManager            
*manager);
+void                        gf_monitor_manager_read_current_state           (GfMonitorManager            
*manager);
+void                        gf_monitor_manager_on_hotplug                   (GfMonitorManager            
*manager);
 
-gboolean                    gf_monitor_manager_is_transform_handled         (GfMonitorManager   *manager,
-                                                                             GfCrtc             *crtc,
-                                                                             GfMonitorTransform  transform);
+void                        gf_monitor_manager_tiled_monitor_added          (GfMonitorManager            
*manager,
+                                                                             GfMonitor                   
*monitor);
 
-gboolean                    gf_monitor_manager_is_lid_closed                (GfMonitorManager   *manager);
+void                        gf_monitor_manager_tiled_monitor_removed        (GfMonitorManager            
*manager,
+                                                                             GfMonitor                   
*monitor);
 
-gfloat                      gf_monitor_manager_calculate_monitor_mode_scale (GfMonitorManager   *manager,
-                                                                             GfMonitor          *monitor,
-                                                                             GfMonitorMode      
*monitor_mode);
+gboolean                    gf_monitor_manager_is_transform_handled         (GfMonitorManager            
*manager,
+                                                                             GfCrtc                      
*crtc,
+                                                                             GfMonitorTransform           
transform);
 
-GfMonitorManagerCapability  gf_monitor_manager_get_capabilities             (GfMonitorManager   *manager);
+GfMonitorsConfig           *gf_monitor_manager_ensure_configured            (GfMonitorManager            
*manager);
 
-GfLogicalMonitorLayoutMode  gf_monitor_manager_get_default_layout_mode      (GfMonitorManager   *manager);
+void                        gf_monitor_manager_update_logical_state_derived (GfMonitorManager            
*manager,
+                                                                             GfMonitorsConfig            
*config);
+
+gboolean                    gf_monitor_manager_is_lid_closed                (GfMonitorManager            
*manager);
+
+gfloat                      gf_monitor_manager_calculate_monitor_mode_scale (GfMonitorManager            
*manager,
+                                                                             GfMonitor                   
*monitor,
+                                                                             GfMonitorMode               
*monitor_mode);
+
+gfloat                     *gf_monitor_manager_calculate_supported_scales   (GfMonitorManager            
*manager,
+                                                                             GfLogicalMonitorLayoutMode   
layout_mode,
+                                                                             GfMonitor                   
*monitor,
+                                                                             GfMonitorMode               
*monitor_mode,
+                                                                             gint                        
*n_supported_scales);
+
+gboolean                    gf_monitor_manager_is_scale_supported           (GfMonitorManager            
*manager,
+                                                                             GfLogicalMonitorLayoutMode   
layout_mode,
+                                                                             GfMonitor                   
*monitor,
+                                                                             GfMonitorMode               
*monitor_mode,
+                                                                             gfloat                       
scale);
+
+GfMonitorManagerCapability  gf_monitor_manager_get_capabilities             (GfMonitorManager            
*manager);
+
+GfLogicalMonitorLayoutMode  gf_monitor_manager_get_default_layout_mode      (GfMonitorManager            
*manager);
+
+GfMonitorConfigManager     *gf_monitor_manager_get_config_manager           (GfMonitorManager            
*manager);
+
+void                        gf_monitor_manager_clear_output                 (GfOutput                    
*output);
+
+void                        gf_monitor_manager_clear_mode                   (GfCrtcMode                  
*mode);
+
+void                        gf_monitor_manager_clear_crtc                   (GfCrtc                      
*crtc);
 
 static inline gboolean
 gf_monitor_transform_is_rotated (GfMonitorTransform transform)
diff --git a/backends/gf-monitor-manager-xrandr.c b/backends/gf-monitor-manager-xrandr.c
index 3f1e63e..2d04a44 100644
--- a/backends/gf-monitor-manager-xrandr.c
+++ b/backends/gf-monitor-manager-xrandr.c
@@ -27,18 +27,32 @@
 
 #include "config.h"
 
+#include <math.h>
+#include <stdlib.h>
 #include <string.h>
 #include <X11/Xatom.h>
 #include <X11/extensions/Xrandr.h>
 #include <X11/extensions/dpms.h>
+#include <X11/Xlib-xcb.h>
+#include <xcb/randr.h>
 
 #include "gf-backend-x11-private.h"
 #include "gf-crtc-private.h"
+#include "gf-monitor-config-manager-private.h"
 #include "gf-monitor-manager-xrandr-private.h"
 #include "gf-monitor-private.h"
 #include "gf-monitor-tiled-private.h"
 #include "gf-output-private.h"
 
+#define ALL_ROTATIONS (RR_Rotate_0 | RR_Rotate_90 | RR_Rotate_180 | RR_Rotate_270)
+#define ALL_TRANSFORMS ((1 << (GF_MONITOR_TRANSFORM_FLIPPED_270 + 1)) - 1)
+
+/* Look for DPI_FALLBACK in:
+ * http://git.gnome.org/browse/gnome-settings-daemon/tree/plugins/xsettings/gsd-xsettings-manager.c
+ * for the reasoning
+ */
+#define DPI_FALLBACK 96.0
+
 struct _GfMonitorManagerXrandr
 {
   GfMonitorManager    parent;
@@ -54,6 +68,8 @@ struct _GfMonitorManagerXrandr
 
   XRRScreenResources *resources;
 
+  Time                last_xrandr_set_timestamp;
+
   gint                max_screen_width;
   gint                max_screen_height;
 };
@@ -65,6 +81,239 @@ typedef struct
 
 G_DEFINE_TYPE (GfMonitorManagerXrandr, gf_monitor_manager_xrandr, GF_TYPE_MONITOR_MANAGER)
 
+static gboolean
+xrandr_set_crtc_config (GfMonitorManagerXrandr *xrandr,
+                        gboolean                save_timestamp,
+                        xcb_randr_crtc_t        crtc,
+                        xcb_timestamp_t         timestamp,
+                        gint                    x,
+                        gint                    y,
+                        xcb_randr_mode_t        mode,
+                        xcb_randr_rotation_t    rotation,
+                        xcb_randr_output_t     *outputs,
+                        gint                    n_outputs)
+{
+  xcb_connection_t *xcb_conn;
+  xcb_timestamp_t config_timestamp;
+  xcb_randr_set_crtc_config_cookie_t cookie;
+  xcb_randr_set_crtc_config_reply_t *reply;
+  xcb_generic_error_t *xcb_error;
+
+  xcb_conn = XGetXCBConnection (xrandr->xdisplay);
+  config_timestamp = xrandr->resources->configTimestamp;
+  cookie = xcb_randr_set_crtc_config (xcb_conn, crtc,
+                                      timestamp, config_timestamp,
+                                      x, y, mode, rotation,
+                                      n_outputs, outputs);
+
+  xcb_error = NULL;
+  reply = xcb_randr_set_crtc_config_reply (xcb_conn, cookie, &xcb_error);
+  if (xcb_error || !reply)
+    {
+      g_free (xcb_error);
+      g_free (reply);
+
+      return FALSE;
+    }
+
+  if (save_timestamp)
+    xrandr->last_xrandr_set_timestamp = reply->timestamp;
+
+  g_free (reply);
+
+  return TRUE;
+}
+
+static void
+output_set_presentation_xrandr (GfMonitorManagerXrandr *xrandr,
+                                GfOutput               *output,
+                                gboolean                presentation)
+{
+  Atom atom;
+  gint value;
+
+  atom = XInternAtom (xrandr->xdisplay, "_GNOME_FLASHBACK_PRESENTATION_OUTPUT", False);
+  value= presentation;
+
+  xcb_randr_change_output_property (XGetXCBConnection (xrandr->xdisplay),
+                                    (XID) output->winsys_id,
+                                    atom, XCB_ATOM_CARDINAL, 32,
+                                    XCB_PROP_MODE_REPLACE,
+                                    1, &value);
+}
+
+static void
+output_set_underscanning_xrandr (GfMonitorManagerXrandr *xrandr,
+                                 GfOutput               *output,
+                                 gboolean                underscanning)
+{
+  Atom prop, valueatom;
+  const gchar *value;
+
+  prop = XInternAtom (xrandr->xdisplay, "underscan", False);
+
+  value = underscanning ? "on" : "off";
+  valueatom = XInternAtom (xrandr->xdisplay, value, False);
+
+  xcb_randr_change_output_property (XGetXCBConnection (xrandr->xdisplay),
+                                    (XID) output->winsys_id,
+                                    prop, XCB_ATOM_ATOM, 32,
+                                    XCB_PROP_MODE_REPLACE,
+                                    1, &valueatom);
+
+  /* Configure the border at the same time. Currently, we use a
+   * 5% of the width/height of the mode. In the future, we should
+   * make the border configurable.
+   */
+  if (underscanning)
+    {
+      uint32_t border_value;
+
+      prop = XInternAtom (xrandr->xdisplay, "underscan hborder", False);
+      border_value = output->crtc->current_mode->width * 0.05;
+
+      xcb_randr_change_output_property (XGetXCBConnection (xrandr->xdisplay),
+                                        (XID) output->winsys_id,
+                                        prop, XCB_ATOM_INTEGER, 32,
+                                        XCB_PROP_MODE_REPLACE,
+                                        1, &border_value);
+
+      prop = XInternAtom (xrandr->xdisplay, "underscan vborder", False);
+      border_value = output->crtc->current_mode->height * 0.05;
+
+      xcb_randr_change_output_property (XGetXCBConnection (xrandr->xdisplay),
+                                        (XID) output->winsys_id,
+                                        prop, XCB_ATOM_INTEGER, 32,
+                                        XCB_PROP_MODE_REPLACE,
+                                        1, &border_value);
+    }
+}
+
+static gboolean
+is_crtc_assignment_changed (GfCrtc      *crtc,
+                            GfCrtcInfo **crtc_infos,
+                            guint        n_crtc_infos)
+{
+  guint i;
+
+  for (i = 0; i < n_crtc_infos; i++)
+    {
+      GfCrtcInfo *crtc_info = crtc_infos[i];
+      guint j;
+
+      if (crtc_info->crtc != crtc)
+        continue;
+
+      if (crtc->current_mode != crtc_info->mode)
+        return TRUE;
+
+      if (crtc->rect.x != crtc_info->x)
+        return TRUE;
+
+      if (crtc->rect.y != crtc_info->y)
+        return TRUE;
+
+      if (crtc->transform != crtc_info->transform)
+        return TRUE;
+
+      for (j = 0; j < crtc_info->outputs->len; j++)
+        {
+          GfOutput *output = ((GfOutput**) crtc_info->outputs->pdata)[j];
+
+          if (output->crtc != crtc)
+            return TRUE;
+        }
+
+      return FALSE;
+    }
+
+  return crtc->current_mode != NULL;
+}
+
+static gboolean
+is_output_assignment_changed (GfOutput      *output,
+                              GfCrtcInfo   **crtc_infos,
+                              guint          n_crtc_infos,
+                              GfOutputInfo **output_infos,
+                              guint          n_output_infos)
+{
+  gboolean output_is_found = FALSE;
+  guint i;
+
+  for (i = 0; i < n_output_infos; i++)
+    {
+      GfOutputInfo *output_info = output_infos[i];
+
+      if (output_info->output != output)
+        continue;
+
+      if (output->is_primary != output_info->is_primary)
+        return TRUE;
+
+      if (output->is_presentation != output_info->is_presentation)
+        return TRUE;
+
+      if (output->is_underscanning != output_info->is_underscanning)
+        return TRUE;
+
+      output_is_found = TRUE;
+    }
+
+  if (!output_is_found)
+    return output->crtc != NULL;
+
+  for (i = 0; i < n_crtc_infos; i++)
+    {
+      GfCrtcInfo *crtc_info = crtc_infos[i];
+      guint j;
+
+      for (j = 0; j < crtc_info->outputs->len; j++)
+        {
+          GfOutput *crtc_info_output;
+
+          crtc_info_output = ((GfOutput**) crtc_info->outputs->pdata)[j];
+
+          if (crtc_info_output == output &&
+              crtc_info->crtc == output->crtc)
+            return FALSE;
+        }
+    }
+
+  return TRUE;
+}
+
+static gboolean
+is_assignments_changed (GfMonitorManager  *manager,
+                        GfCrtcInfo       **crtc_infos,
+                        guint              n_crtc_infos,
+                        GfOutputInfo     **output_infos,
+                        guint              n_output_infos)
+{
+  guint i;
+
+  for (i = 0; i < manager->n_crtcs; i++)
+    {
+      GfCrtc *crtc = &manager->crtcs[i];
+
+      if (is_crtc_assignment_changed (crtc, crtc_infos, n_crtc_infos))
+        return TRUE;
+    }
+
+  for (i = 0; i < manager->n_outputs; i++)
+    {
+      GfOutput *output = &manager->outputs[i];
+
+      if (is_output_assignment_changed (output,
+                                        crtc_infos,
+                                        n_crtc_infos,
+                                        output_infos,
+                                        n_output_infos))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
 static guint8 *
 get_edid_property (Display  *xdisplay,
                    RROutput  output,
@@ -128,6 +377,892 @@ read_output_edid (GfMonitorManagerXrandr *xrandr,
   return NULL;
 }
 
+static GfMonitorTransform
+gf_monitor_transform_from_xrandr (Rotation rotation)
+{
+  static const GfMonitorTransform y_reflected_map[4] = {
+    GF_MONITOR_TRANSFORM_FLIPPED_180,
+    GF_MONITOR_TRANSFORM_FLIPPED_90,
+    GF_MONITOR_TRANSFORM_FLIPPED,
+    GF_MONITOR_TRANSFORM_FLIPPED_270
+  };
+  GfMonitorTransform ret;
+
+  switch (rotation & 0x7F)
+    {
+      default:
+      case RR_Rotate_0:
+        ret = GF_MONITOR_TRANSFORM_NORMAL;
+        break;
+
+      case RR_Rotate_90:
+        ret = GF_MONITOR_TRANSFORM_90;
+        break;
+
+      case RR_Rotate_180:
+        ret = GF_MONITOR_TRANSFORM_180;
+        break;
+
+      case RR_Rotate_270:
+        ret = GF_MONITOR_TRANSFORM_270;
+        break;
+    }
+
+  if (rotation & RR_Reflect_X)
+    return ret + 4;
+  else if (rotation & RR_Reflect_Y)
+    return y_reflected_map[ret];
+  else
+    return ret;
+}
+
+static xcb_randr_rotation_t
+gf_monitor_transform_to_xrandr (GfMonitorTransform transform)
+{
+  xcb_randr_rotation_t rotation;
+
+  rotation = XCB_RANDR_ROTATION_ROTATE_0;
+
+  switch (transform)
+    {
+      case GF_MONITOR_TRANSFORM_NORMAL:
+        rotation = XCB_RANDR_ROTATION_ROTATE_0;
+        break;
+
+      case GF_MONITOR_TRANSFORM_90:
+        rotation = XCB_RANDR_ROTATION_ROTATE_90;
+        break;
+
+      case GF_MONITOR_TRANSFORM_180:
+        rotation = XCB_RANDR_ROTATION_ROTATE_180;
+        break;
+
+      case GF_MONITOR_TRANSFORM_270:
+        rotation = XCB_RANDR_ROTATION_ROTATE_270;
+        break;
+
+      case GF_MONITOR_TRANSFORM_FLIPPED:
+        rotation = XCB_RANDR_ROTATION_REFLECT_X | XCB_RANDR_ROTATION_ROTATE_0;
+        break;
+
+      case GF_MONITOR_TRANSFORM_FLIPPED_90:
+        rotation = XCB_RANDR_ROTATION_REFLECT_X | XCB_RANDR_ROTATION_ROTATE_90;
+        break;
+
+      case GF_MONITOR_TRANSFORM_FLIPPED_180:
+        rotation = XCB_RANDR_ROTATION_REFLECT_X | XCB_RANDR_ROTATION_ROTATE_180;
+        break;
+
+      case GF_MONITOR_TRANSFORM_FLIPPED_270:
+        rotation = XCB_RANDR_ROTATION_REFLECT_X | XCB_RANDR_ROTATION_ROTATE_270;
+        break;
+
+      default:
+        g_assert_not_reached ();
+        break;
+    }
+
+  return rotation;
+}
+
+static GfMonitorTransform
+gf_monitor_transform_from_xrandr_all (Rotation rotation)
+{
+  GfMonitorTransform ret;
+
+  /* Handle the common cases first (none or all) */
+  if (rotation == 0 || rotation == RR_Rotate_0)
+    return (1 << GF_MONITOR_TRANSFORM_NORMAL);
+
+  /* All rotations and one reflection -> all of them by composition */
+  if ((rotation & ALL_ROTATIONS) &&
+      ((rotation & RR_Reflect_X) || (rotation & RR_Reflect_Y)))
+    return ALL_TRANSFORMS;
+
+  ret = 1 << GF_MONITOR_TRANSFORM_NORMAL;
+  if (rotation & RR_Rotate_90)
+    ret |= 1 << GF_MONITOR_TRANSFORM_90;
+  if (rotation & RR_Rotate_180)
+    ret |= 1 << GF_MONITOR_TRANSFORM_180;
+  if (rotation & RR_Rotate_270)
+    ret |= 1 << GF_MONITOR_TRANSFORM_270;
+  if (rotation & (RR_Rotate_0 | RR_Reflect_X))
+    ret |= 1 << GF_MONITOR_TRANSFORM_FLIPPED;
+  if (rotation & (RR_Rotate_90 | RR_Reflect_X))
+    ret |= 1 << GF_MONITOR_TRANSFORM_FLIPPED_90;
+  if (rotation & (RR_Rotate_180 | RR_Reflect_X))
+    ret |= 1 << GF_MONITOR_TRANSFORM_FLIPPED_180;
+  if (rotation & (RR_Rotate_270 | RR_Reflect_X))
+    ret |= 1 << GF_MONITOR_TRANSFORM_FLIPPED_270;
+
+  return ret;
+}
+
+static gboolean
+output_get_property_exists (GfMonitorManagerXrandr *xrandr,
+                            GfOutput               *output,
+                            const gchar            *propname)
+{
+  gboolean exists;
+  Atom atom, actual_type;
+  gint actual_format;
+  gulong nitems, bytes_after;
+  guchar *buffer;
+
+  atom = XInternAtom (xrandr->xdisplay, propname, False);
+  XRRGetOutputProperty (xrandr->xdisplay, (XID) output->winsys_id, atom,
+                        0, G_MAXLONG, False, False, AnyPropertyType,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &buffer);
+
+  exists = (actual_type != None);
+
+  if (buffer)
+    XFree (buffer);
+
+  return exists;
+}
+
+static gboolean
+output_get_hotplug_mode_update (GfMonitorManagerXrandr *xrandr,
+                                GfOutput               *output)
+{
+  return output_get_property_exists (xrandr, output, "hotplug_mode_update");
+}
+
+static gboolean
+output_get_integer_property (GfMonitorManagerXrandr *xrandr,
+                             GfOutput               *output,
+                             const gchar            *propname,
+                             gint                   *value)
+{
+  gboolean exists;
+  Atom atom, actual_type;
+  gint actual_format;
+  gulong nitems, bytes_after;
+  guchar *buffer;
+
+  atom = XInternAtom (xrandr->xdisplay, propname, False);
+  XRRGetOutputProperty (xrandr->xdisplay, (XID) output->winsys_id, atom,
+                        0, G_MAXLONG, False, False, XA_INTEGER,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &buffer);
+
+  exists = (actual_type == XA_INTEGER && actual_format == 32 && nitems == 1);
+
+  if (exists && value != NULL)
+    *value = ((gint*) buffer)[0];
+
+  if (buffer)
+    XFree (buffer);
+
+  return exists;
+}
+
+static gint
+output_get_suggested_x (GfMonitorManagerXrandr *xrandr,
+                        GfOutput               *output)
+{
+  gint val;
+
+  if (output_get_integer_property (xrandr, output, "suggested X", &val))
+    return val;
+
+  return -1;
+}
+
+static gint
+output_get_suggested_y (GfMonitorManagerXrandr *xrandr,
+                        GfOutput               *output)
+{
+  gint val;
+
+  if (output_get_integer_property (xrandr, output, "suggested Y", &val))
+    return val;
+
+  return -1;
+}
+
+static GfConnectorType
+connector_type_from_atom (GfMonitorManagerXrandr *xrandr,
+                          Atom                    atom)
+{
+  Display *xdisplay;
+
+  xdisplay = xrandr->xdisplay;
+
+  if (atom == XInternAtom (xdisplay, "HDMI", True))
+    return GF_CONNECTOR_TYPE_HDMIA;
+  if (atom == XInternAtom (xdisplay, "VGA", True))
+    return GF_CONNECTOR_TYPE_VGA;
+  /* Doesn't have a DRM equivalent, but means an internal panel.
+   * We could pick either LVDS or eDP here. */
+  if (atom == XInternAtom (xdisplay, "Panel", True))
+    return GF_CONNECTOR_TYPE_LVDS;
+  if (atom == XInternAtom (xdisplay, "DVI", True) ||
+      atom == XInternAtom (xdisplay, "DVI-I", True))
+    return GF_CONNECTOR_TYPE_DVII;
+  if (atom == XInternAtom (xdisplay, "DVI-A", True))
+    return GF_CONNECTOR_TYPE_DVIA;
+  if (atom == XInternAtom (xdisplay, "DVI-D", True))
+    return GF_CONNECTOR_TYPE_DVID;
+  if (atom == XInternAtom (xdisplay, "DisplayPort", True))
+    return GF_CONNECTOR_TYPE_DisplayPort;
+  if (atom == XInternAtom (xdisplay, "TV", True))
+    return GF_CONNECTOR_TYPE_TV;
+  if (atom == XInternAtom (xdisplay, "TV-Composite", True))
+    return GF_CONNECTOR_TYPE_Composite;
+  if (atom == XInternAtom (xdisplay, "TV-SVideo", True))
+    return GF_CONNECTOR_TYPE_SVIDEO;
+  /* Another set of mismatches. */
+  if (atom == XInternAtom (xdisplay, "TV-SCART", True))
+    return GF_CONNECTOR_TYPE_TV;
+  if (atom == XInternAtom (xdisplay, "TV-C4", True))
+    return GF_CONNECTOR_TYPE_TV;
+
+  return GF_CONNECTOR_TYPE_Unknown;
+}
+
+static GfConnectorType
+output_get_connector_type_from_prop (GfMonitorManagerXrandr *xrandr,
+                                     GfOutput               *output)
+{
+  Atom atom, actual_type, connector_type_atom;
+  gint actual_format;
+  gulong nitems, bytes_after;
+  guchar *buffer;
+  GfConnectorType ret;
+
+  atom = XInternAtom (xrandr->xdisplay, "ConnectorType", False);
+  XRRGetOutputProperty (xrandr->xdisplay, (XID) output->winsys_id, atom,
+                        0, G_MAXLONG, False, False, XA_ATOM,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &buffer);
+
+  if (actual_type != XA_ATOM || actual_format != 32 || nitems < 1)
+    {
+      if (buffer)
+        XFree (buffer);
+
+      return GF_CONNECTOR_TYPE_Unknown;
+    }
+
+  connector_type_atom = ((Atom *) buffer)[0];
+  ret = connector_type_from_atom (xrandr, connector_type_atom);
+  XFree (buffer);
+
+  return ret;
+}
+
+static GfConnectorType
+output_get_connector_type_from_name (GfMonitorManagerXrandr *xrandr,
+                                     GfOutput               *output)
+{
+  const gchar *name;
+
+  name = output->name;
+
+  /* drmmode_display.c, which was copy/pasted across all the FOSS
+   * xf86-video-* drivers, seems to name its outputs based on the
+   * connector type, so look for that....
+   *
+   * SNA has its own naming scheme, because what else did you expect
+   * from SNA, but it's not too different, so we can thankfully use
+   * that with minor changes.
+   *
+   * http://cgit.freedesktop.org/xorg/xserver/tree/hw/xfree86/drivers/modesetting/drmmode_display.c#n953
+   * http://cgit.freedesktop.org/xorg/driver/xf86-video-intel/tree/src/sna/sna_display.c#n3486
+   */
+
+  if (g_str_has_prefix (name, "DVI"))
+    return GF_CONNECTOR_TYPE_DVII;
+  if (g_str_has_prefix (name, "LVDS"))
+    return GF_CONNECTOR_TYPE_LVDS;
+  if (g_str_has_prefix (name, "HDMI"))
+    return GF_CONNECTOR_TYPE_HDMIA;
+  if (g_str_has_prefix (name, "VGA"))
+    return GF_CONNECTOR_TYPE_VGA;
+  /* SNA uses DP, not DisplayPort. Test for both. */
+  if (g_str_has_prefix (name, "DP") || g_str_has_prefix (name, "DisplayPort"))
+    return GF_CONNECTOR_TYPE_DisplayPort;
+  if (g_str_has_prefix (name, "eDP"))
+    return GF_CONNECTOR_TYPE_eDP;
+  if (g_str_has_prefix (name, "Virtual"))
+    return GF_CONNECTOR_TYPE_VIRTUAL;
+  if (g_str_has_prefix (name, "Composite"))
+    return GF_CONNECTOR_TYPE_Composite;
+  if (g_str_has_prefix (name, "S-video"))
+    return GF_CONNECTOR_TYPE_SVIDEO;
+  if (g_str_has_prefix (name, "TV"))
+    return GF_CONNECTOR_TYPE_TV;
+  if (g_str_has_prefix (name, "CTV"))
+    return GF_CONNECTOR_TYPE_Composite;
+  if (g_str_has_prefix (name, "DSI"))
+    return GF_CONNECTOR_TYPE_DSI;
+  if (g_str_has_prefix (name, "DIN"))
+    return GF_CONNECTOR_TYPE_9PinDIN;
+
+  return GF_CONNECTOR_TYPE_Unknown;
+}
+
+static GfConnectorType
+output_get_connector_type (GfMonitorManagerXrandr *xrandr,
+                           GfOutput               *output)
+{
+  GfConnectorType ret;
+
+  /* The "ConnectorType" property is considered mandatory since RandR 1.3,
+   * but none of the FOSS drivers support it, because we're a bunch of
+   * professional software developers.
+   *
+   * 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 (xrandr, output);
+  if (ret != GF_CONNECTOR_TYPE_Unknown)
+    return ret;
+
+  /* Fall back to heuristics based on the output name. */
+  ret = output_get_connector_type_from_name (xrandr, output);
+  if (ret != GF_CONNECTOR_TYPE_Unknown)
+    return ret;
+
+  return GF_CONNECTOR_TYPE_Unknown;
+}
+
+static void
+output_get_tile_info (GfMonitorManagerXrandr *xrandr,
+                      GfOutput               *output)
+{
+  Atom tile_atom;
+  guchar *prop;
+  gulong nitems, bytes_after;
+  gint actual_format;
+  Atom actual_type;
+
+  if (xrandr->has_randr15 == FALSE)
+    return;
+
+  tile_atom = XInternAtom (xrandr->xdisplay, "TILE", FALSE);
+  XRRGetOutputProperty (xrandr->xdisplay, output->winsys_id,
+                        tile_atom, 0, 100, False,
+                        False, AnyPropertyType,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &prop);
+
+  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];
+    }
+
+  if (prop)
+    XFree (prop);
+}
+
+static void
+output_get_modes (GfMonitorManager *manager,
+                  GfOutput         *output,
+                  XRROutputInfo    *xrandr_output)
+{
+  guint j, k;
+  guint n_actual_modes;
+
+  output->modes = g_new0 (GfCrtcMode *, xrandr_output->nmode);
+
+  n_actual_modes = 0;
+  for (j = 0; j < (guint) xrandr_output->nmode; j++)
+    {
+      for (k = 0; k < manager->n_modes; k++)
+        {
+          if (xrandr_output->modes[j] == (XID) manager->modes[k].mode_id)
+            {
+              output->modes[n_actual_modes] = &manager->modes[k];
+              n_actual_modes += 1;
+              break;
+            }
+        }
+    }
+
+  output->n_modes = n_actual_modes;
+  if (n_actual_modes > 0)
+    output->preferred_mode = output->modes[0];
+}
+
+static void
+output_get_crtcs (GfMonitorManager *manager,
+                  GfOutput         *output,
+                  XRROutputInfo    *xrandr_output)
+{
+  guint j, k;
+  guint n_actual_crtcs;
+
+  output->possible_crtcs = g_new0 (GfCrtc *, xrandr_output->ncrtc);
+
+  n_actual_crtcs = 0;
+  for (j = 0; j < (guint) xrandr_output->ncrtc; j++)
+    {
+      for (k = 0; k < manager->n_crtcs; k++)
+        {
+          if ((XID) manager->crtcs[k].crtc_id == xrandr_output->crtcs[j])
+            {
+              output->possible_crtcs[n_actual_crtcs] = &manager->crtcs[k];
+              n_actual_crtcs += 1;
+              break;
+            }
+        }
+    }
+  output->n_possible_crtcs = n_actual_crtcs;
+
+  output->crtc = NULL;
+  for (j = 0; j < manager->n_crtcs; j++)
+    {
+      if ((XID) manager->crtcs[j].crtc_id == xrandr_output->crtc)
+        {
+          output->crtc = &manager->crtcs[j];
+          break;
+        }
+    }
+}
+
+static gboolean
+output_get_boolean_property (GfMonitorManagerXrandr *xrandr,
+                             GfOutput               *output,
+                             const gchar            *propname)
+{
+  Atom atom, actual_type;
+  gint actual_format;
+  gulong nitems, bytes_after;
+  guchar *buffer;
+  gboolean value;
+
+  atom = XInternAtom (xrandr->xdisplay, propname, False);
+  XRRGetOutputProperty (xrandr->xdisplay, (XID) output->winsys_id, atom,
+                        0, G_MAXLONG, False, False, XA_CARDINAL,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &buffer);
+
+  if (actual_type != XA_CARDINAL || actual_format != 32 || nitems < 1)
+    {
+      if (buffer)
+        XFree (buffer);
+
+      return FALSE;
+    }
+
+  value = ((gint*) buffer)[0];
+  XFree (buffer);
+
+  return value;
+}
+
+static gboolean
+output_get_presentation_xrandr (GfMonitorManagerXrandr *xrandr,
+                                GfOutput               *output)
+{
+  return output_get_boolean_property (xrandr, output, "_GNOME_FLASHBACK_PRESENTATION_OUTPUT");
+}
+
+static gboolean
+output_get_underscanning_xrandr (GfMonitorManagerXrandr *xrandr,
+                                 GfOutput               *output)
+{
+  Atom atom, actual_type;
+  gint actual_format;
+  gulong nitems, bytes_after;
+  guchar *buffer;
+  gchar *str;
+  gboolean value;
+
+  atom = XInternAtom (xrandr->xdisplay, "underscan", False);
+  XRRGetOutputProperty (xrandr->xdisplay, (XID) output->winsys_id, atom,
+                        0, G_MAXLONG, False, False, XA_ATOM,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &buffer);
+
+  if (actual_type != XA_ATOM || actual_format != 32 || nitems < 1)
+    {
+      if (buffer)
+        XFree (buffer);
+
+      return FALSE;
+    }
+
+  str = XGetAtomName (xrandr->xdisplay, *(Atom *)buffer);
+  XFree (buffer);
+
+  value = !strcmp (str, "on");
+  XFree (str);
+
+  return value;
+}
+
+static gboolean
+output_get_supports_underscanning_xrandr (GfMonitorManagerXrandr *xrandr,
+                                          GfOutput               *output)
+{
+  Atom atom, actual_type;
+  gint actual_format, i;
+  gulong nitems, bytes_after;
+  guchar *buffer;
+  XRRPropertyInfo *property_info;
+  Atom *values;
+  gboolean supports_underscanning = FALSE;
+
+  atom = XInternAtom (xrandr->xdisplay, "underscan", False);
+  XRRGetOutputProperty (xrandr->xdisplay, (XID) output->winsys_id, atom,
+                        0, G_MAXLONG, False, False, XA_ATOM,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &buffer);
+
+  if (actual_type != XA_ATOM || actual_format != 32 || nitems < 1)
+    {
+      if (buffer)
+        XFree (buffer);
+
+      return FALSE;
+    }
+
+  property_info = XRRQueryOutputProperty (xrandr->xdisplay,
+                                          (XID) output->winsys_id,
+                                          atom);
+  values = (Atom *) property_info->values;
+
+  for (i = 0; i < property_info->num_values; i++)
+    {
+      /* The output supports underscanning if "on" is a valid value
+       * for the underscan property.
+       */
+      gchar *name = XGetAtomName (xrandr->xdisplay, values[i]);
+      if (strcmp (name, "on") == 0)
+        supports_underscanning = TRUE;
+
+      XFree (name);
+    }
+
+  XFree (property_info);
+
+  return supports_underscanning;
+}
+
+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);
+}
+
+static gint
+output_get_backlight_xrandr (GfMonitorManagerXrandr *xrandr,
+                             GfOutput               *output)
+{
+  gint value = -1;
+  Atom atom, actual_type;
+  gint actual_format;
+  gulong nitems, bytes_after;
+  guchar *buffer;
+
+  atom = XInternAtom (xrandr->xdisplay, "Backlight", False);
+  XRRGetOutputProperty (xrandr->xdisplay, (XID) output->winsys_id, atom,
+                        0, G_MAXLONG, False, False, XA_INTEGER,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &buffer);
+
+  if (actual_type != XA_INTEGER || actual_format != 32 || nitems < 1)
+    {
+      if (buffer)
+        XFree (buffer);
+
+      return FALSE;
+    }
+
+  value = ((gint*) buffer)[0];
+  XFree (buffer);
+
+  if (value > 0)
+    return normalize_backlight (output, value);
+  else
+    return -1;
+}
+
+static void
+output_get_backlight_limits_xrandr (GfMonitorManagerXrandr *xrandr,
+                                    GfOutput               *output)
+{
+  Atom atom;
+  xcb_connection_t *xcb_conn;
+  xcb_randr_query_output_property_cookie_t cookie;
+  xcb_randr_query_output_property_reply_t *reply;
+  int32_t *values;
+
+  atom = XInternAtom (xrandr->xdisplay, "Backlight", False);
+
+  xcb_conn = XGetXCBConnection (xrandr->xdisplay);
+  cookie = xcb_randr_query_output_property (xcb_conn,
+                                            (xcb_randr_output_t) output->winsys_id,
+                                            (xcb_atom_t) atom);
+
+  reply = xcb_randr_query_output_property_reply (xcb_conn, cookie, NULL);
+
+  /* This can happen on systems without backlights. */
+  if (reply == NULL)
+    return;
+
+  if (!reply->range || reply->length != 2)
+    {
+      g_warning ("backlight %s was not range\n", output->name);
+      g_free (reply);
+      return;
+    }
+
+  values = xcb_randr_query_output_property_valid_values (reply);
+
+  output->backlight_min = values[0];
+  output->backlight_max = values[1];
+
+  g_free (reply);
+}
+
+static gint
+compare_outputs (const void *one,
+                 const void *two)
+{
+  const GfOutput *o_one = one, *o_two = two;
+
+  return strcmp (o_one->name, o_two->name);
+}
+
+static void
+apply_crtc_assignments (GfMonitorManager  *manager,
+                        gboolean           save_timestamp,
+                        GfCrtcInfo       **crtcs,
+                        guint              n_crtcs,
+                        GfOutputInfo     **outputs,
+                        guint              n_outputs)
+{
+  GfMonitorManagerXrandr *xrandr;
+  gint width, height, width_mm, height_mm;
+  guint i;
+
+  xrandr = GF_MONITOR_MANAGER_XRANDR (manager);
+
+  XGrabServer (xrandr->xdisplay);
+
+  /* First compute the new size of the screen (framebuffer) */
+  width = 0; height = 0;
+  for (i = 0; i < n_crtcs; i++)
+    {
+      GfCrtcInfo *crtc_info = crtcs[i];
+      GfCrtc *crtc = crtc_info->crtc;
+
+      crtc->is_dirty = TRUE;
+
+      if (crtc_info->mode == NULL)
+        continue;
+
+      if (gf_monitor_transform_is_rotated (crtc_info->transform))
+        {
+          width = MAX (width, crtc_info->x + crtc_info->mode->height);
+          height = MAX (height, crtc_info->y + crtc_info->mode->width);
+        }
+      else
+        {
+          width = MAX (width, crtc_info->x + crtc_info->mode->width);
+          height = MAX (height, crtc_info->y + crtc_info->mode->height);
+        }
+    }
+
+  /* Second disable all newly disabled CRTCs, or CRTCs that in the previous
+   * configuration would be outside the new framebuffer (otherwise X complains
+   * loudly when resizing)
+   * CRTC will be enabled again after resizing the FB
+   */
+  for (i = 0; i < n_crtcs; i++)
+    {
+      GfCrtcInfo *crtc_info = crtcs[i];
+      GfCrtc *crtc = crtc_info->crtc;
+
+      if (crtc_info->mode == NULL ||
+          crtc->rect.x + crtc->rect.width > width ||
+          crtc->rect.y + crtc->rect.height > height)
+        {
+          xrandr_set_crtc_config (xrandr,
+                                  save_timestamp,
+                                  (xcb_randr_crtc_t) crtc->crtc_id,
+                                  XCB_CURRENT_TIME,
+                                  0, 0, XCB_NONE,
+                                  XCB_RANDR_ROTATION_ROTATE_0,
+                                  NULL, 0);
+
+          crtc->rect.x = 0;
+          crtc->rect.y = 0;
+          crtc->rect.width = 0;
+          crtc->rect.height = 0;
+          crtc->current_mode = NULL;
+        }
+    }
+
+  /* Disable CRTCs not mentioned in the list */
+  for (i = 0; i < manager->n_crtcs; i++)
+    {
+      GfCrtc *crtc = &manager->crtcs[i];
+
+      if (crtc->is_dirty)
+        {
+          crtc->is_dirty = FALSE;
+          continue;
+        }
+
+      if (crtc->current_mode == NULL)
+        continue;
+
+      xrandr_set_crtc_config (xrandr,
+                              save_timestamp,
+                              (xcb_randr_crtc_t) crtc->crtc_id,
+                              XCB_CURRENT_TIME,
+                              0, 0, XCB_NONE,
+                              XCB_RANDR_ROTATION_ROTATE_0,
+                              NULL, 0);
+
+      crtc->rect.x = 0;
+      crtc->rect.y = 0;
+      crtc->rect.width = 0;
+      crtc->rect.height = 0;
+      crtc->current_mode = NULL;
+    }
+
+  g_assert (width > 0 && height > 0);
+  /* The 'physical size' of an X screen is meaningless if that screen
+   * can consist of many monitors. So just pick a size that make the
+   * dpi 96.
+   *
+   * Firefox and Evince apparently believe what X tells them.
+   */
+  width_mm = (width / DPI_FALLBACK) * 25.4 + 0.5;
+  height_mm = (height / DPI_FALLBACK) * 25.4 + 0.5;
+  XRRSetScreenSize (xrandr->xdisplay, xrandr->xroot,
+                    width, height, width_mm, height_mm);
+
+  for (i = 0; i < n_crtcs; i++)
+    {
+      GfCrtcInfo *crtc_info = crtcs[i];
+      GfCrtc *crtc = crtc_info->crtc;
+
+      if (crtc_info->mode != NULL)
+        {
+          GfCrtcMode *mode;
+          xcb_randr_output_t *output_ids;
+          guint j, n_output_ids;
+          xcb_randr_rotation_t rotation;
+
+          mode = crtc_info->mode;
+
+          n_output_ids = crtc_info->outputs->len;
+          output_ids = g_new0 (xcb_randr_output_t, n_output_ids);
+
+          for (j = 0; j < n_output_ids; j++)
+            {
+              GfOutput *output;
+
+              output = ((GfOutput**) crtc_info->outputs->pdata)[j];
+
+              output->is_dirty = TRUE;
+              output->crtc = crtc;
+
+              output_ids[j] = output->winsys_id;
+            }
+
+          rotation = gf_monitor_transform_to_xrandr (crtc_info->transform);
+          if (!xrandr_set_crtc_config (xrandr,
+                                       save_timestamp,
+                                       (xcb_randr_crtc_t) crtc->crtc_id,
+                                       XCB_CURRENT_TIME,
+                                       crtc_info->x, crtc_info->y,
+                                       (xcb_randr_mode_t) mode->mode_id,
+                                       rotation,
+                                       output_ids, n_output_ids))
+            {
+              g_warning ("Configuring CRTC %d with mode %d (%d x %d @ %f) at position %d, %d and transform 
%u failed\n",
+                         (guint) (crtc->crtc_id), (guint) (mode->mode_id),
+                         mode->width, mode->height, (gfloat) mode->refresh_rate,
+                         crtc_info->x, crtc_info->y, crtc_info->transform);
+
+              g_free (output_ids);
+              continue;
+            }
+
+          if (gf_monitor_transform_is_rotated (crtc_info->transform))
+            {
+              width = mode->height;
+              height = mode->width;
+            }
+          else
+            {
+              width = mode->width;
+              height = mode->height;
+            }
+
+          crtc->rect.x = crtc_info->x;
+          crtc->rect.y = crtc_info->y;
+          crtc->rect.width = width;
+          crtc->rect.height = height;
+          crtc->current_mode = mode;
+          crtc->transform = crtc_info->transform;
+
+          g_free (output_ids);
+        }
+    }
+
+  for (i = 0; i < n_outputs; i++)
+    {
+      GfOutputInfo *output_info = outputs[i];
+      GfOutput *output = output_info->output;
+
+      if (output_info->is_primary)
+        {
+          XRRSetOutputPrimary (xrandr->xdisplay, xrandr->xroot,
+                               (XID)output_info->output->winsys_id);
+        }
+
+      output_set_presentation_xrandr (xrandr, output_info->output,
+                                      output_info->is_presentation);
+
+      if (output_get_supports_underscanning_xrandr (xrandr, output_info->output))
+        output_set_underscanning_xrandr (xrandr, output_info->output,
+                                         output_info->is_underscanning);
+
+      output->is_primary = output_info->is_primary;
+      output->is_presentation = output_info->is_presentation;
+      output->is_underscanning = output_info->is_underscanning;
+    }
+
+  /* Disable outputs not mentioned in the list */
+  for (i = 0; i < manager->n_outputs; i++)
+    {
+      GfOutput *output = &manager->outputs[i];
+
+      if (output->is_dirty)
+        {
+          output->is_dirty = FALSE;
+          continue;
+        }
+
+      output->crtc = NULL;
+      output->is_primary = FALSE;
+    }
+
+  XUngrabServer (xrandr->xdisplay);
+  XFlush (xrandr->xdisplay);
+}
+
 static GQuark
 gf_monitor_data_quark (void)
 {
@@ -293,6 +1428,217 @@ gf_monitor_manager_xrandr_finalize (GObject *object)
 static void
 gf_monitor_manager_xrandr_read_current (GfMonitorManager *manager)
 {
+  GfMonitorManagerXrandr *xrandr;
+  XRRScreenResources *resources;
+  CARD16 dpms_state;
+  BOOL dpms_enabled;
+  gint min_width;
+  gint min_height;
+  Screen *screen;
+  guint i, j, k;
+  RROutput primary_output;
+  guint n_actual_outputs;
+
+  xrandr = GF_MONITOR_MANAGER_XRANDR (manager);
+
+  if (DPMSCapable (xrandr->xdisplay) &&
+      DPMSInfo (xrandr->xdisplay, &dpms_state, &dpms_enabled) &&
+      dpms_enabled)
+    {
+      switch (dpms_state)
+        {
+          case DPMSModeOn:
+            manager->power_save_mode = GF_POWER_SAVE_ON;
+            break;
+
+          case DPMSModeStandby:
+            manager->power_save_mode = GF_POWER_SAVE_STANDBY;
+            break;
+
+          case DPMSModeSuspend:
+            manager->power_save_mode = GF_POWER_SAVE_SUSPEND;
+            break;
+
+          case DPMSModeOff:
+            manager->power_save_mode = GF_POWER_SAVE_OFF;
+            break;
+
+          default:
+            manager->power_save_mode = GF_POWER_SAVE_UNSUPPORTED;
+            break;
+        }
+    }
+  else
+    {
+      manager->power_save_mode = GF_POWER_SAVE_UNSUPPORTED;
+    }
+
+  XRRGetScreenSizeRange (xrandr->xdisplay, xrandr->xroot,
+                         &min_width, &min_height,
+                         &xrandr->max_screen_width,
+                         &xrandr->max_screen_height);
+
+  /* This is updated because we called RRUpdateConfiguration below */
+  screen = ScreenOfDisplay (xrandr->xdisplay, DefaultScreen (xrandr->xdisplay));
+  manager->screen_width = WidthOfScreen (screen);
+  manager->screen_height = HeightOfScreen (screen);
+
+  g_clear_pointer (&xrandr->resources, XRRFreeScreenResources);
+  resources = XRRGetScreenResourcesCurrent (xrandr->xdisplay, xrandr->xroot);
+
+  if (!resources)
+    return;
+
+  xrandr->resources = resources;
+  manager->n_outputs = resources->noutput;
+  manager->n_crtcs = resources->ncrtc;
+  manager->n_modes = resources->nmode;
+  manager->outputs = g_new0 (GfOutput, manager->n_outputs);
+  manager->modes = g_new0 (GfCrtcMode, manager->n_modes);
+  manager->crtcs = g_new0 (GfCrtc, manager->n_crtcs);
+
+  for (i = 0; i < (guint) resources->nmode; i++)
+    {
+      XRRModeInfo *xmode;
+      GfCrtcMode *mode;
+
+      xmode = &resources->modes[i];
+      mode = &manager->modes[i];
+
+      mode->mode_id = xmode->id;
+      mode->width = xmode->width;
+      mode->height = xmode->height;
+      mode->refresh_rate = (xmode->dotClock / ((gfloat) xmode->hTotal * xmode->vTotal));
+      mode->flags = xmode->modeFlags;
+      mode->name = g_strdup_printf ("%dx%d", xmode->width, xmode->height);
+    }
+
+  for (i = 0; i < (guint) resources->ncrtc; i++)
+    {
+      XRRCrtcInfo *crtc;
+      GfCrtc *gf_crtc;
+
+      crtc = XRRGetCrtcInfo (xrandr->xdisplay, resources, resources->crtcs[i]);
+      gf_crtc = &manager->crtcs[i];
+
+      gf_crtc->crtc_id = resources->crtcs[i];
+      gf_crtc->rect.x = crtc->x;
+      gf_crtc->rect.y = crtc->y;
+      gf_crtc->rect.width = crtc->width;
+      gf_crtc->rect.height = crtc->height;
+      gf_crtc->is_dirty = FALSE;
+      gf_crtc->transform = gf_monitor_transform_from_xrandr (crtc->rotation);
+      gf_crtc->all_transforms = gf_monitor_transform_from_xrandr_all (crtc->rotations);
+
+      for (j = 0; j < (guint) resources->nmode; j++)
+        {
+          if (resources->modes[j].id == crtc->mode)
+            {
+              gf_crtc->current_mode = &manager->modes[j];
+              break;
+            }
+        }
+
+      XRRFreeCrtcInfo (crtc);
+    }
+
+  primary_output = XRRGetOutputPrimary (xrandr->xdisplay, xrandr->xroot);
+
+  n_actual_outputs = 0;
+  for (i = 0; i < (guint) resources->noutput; i++)
+    {
+      XRROutputInfo *xrandr_output;
+      GfOutput *output;
+
+      xrandr_output = XRRGetOutputInfo (xrandr->xdisplay, resources,
+                                        resources->outputs[i]);
+
+      if (!xrandr_output)
+        continue;
+
+      output = &manager->outputs[n_actual_outputs];
+
+      if (xrandr_output->connection != RR_Disconnected)
+        {
+          GBytes *edid;
+
+          output->winsys_id = resources->outputs[i];
+          output->name = g_strdup (xrandr_output->name);
+
+          edid = read_output_edid (xrandr, output->winsys_id);
+          gf_output_parse_edid (output, edid);
+          g_bytes_unref (edid);
+
+          output->width_mm = xrandr_output->mm_width;
+          output->height_mm = xrandr_output->mm_height;
+          output->hotplug_mode_update = output_get_hotplug_mode_update (xrandr, output);
+          output->suggested_x = output_get_suggested_x (xrandr, output);
+          output->suggested_y = output_get_suggested_y (xrandr, output);
+          output->connector_type = output_get_connector_type (xrandr, output);
+
+          output_get_tile_info (xrandr, output);
+          output_get_modes (manager, output, xrandr_output);
+          output_get_crtcs (manager, output, xrandr_output);
+
+          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 (j = 0; j < (guint) xrandr_output->nclone; j++)
+            {
+              output->possible_clones[j] = GINT_TO_POINTER (xrandr_output->clones[j]);
+            }
+
+          output->is_primary = ((XID) output->winsys_id == primary_output);
+          output->is_presentation = output_get_presentation_xrandr (xrandr, output);
+          output->is_underscanning = output_get_underscanning_xrandr (xrandr, output);
+          output->supports_underscanning = output_get_supports_underscanning_xrandr (xrandr, output);
+
+          output_get_backlight_limits_xrandr (xrandr, output);
+
+          if (!(output->backlight_min == 0 && output->backlight_max == 0))
+            output->backlight = output_get_backlight_xrandr (xrandr, output);
+          else
+            output->backlight = -1;
+
+          if (output->n_modes == 0 || output->n_possible_crtcs == 0)
+            gf_monitor_manager_clear_output (output);
+          else
+            n_actual_outputs++;
+        }
+
+      XRRFreeOutputInfo (xrandr_output);
+    }
+
+  manager->n_outputs = n_actual_outputs;
+
+  /* Sort the outputs for easier handling in GfMonitorConfig */
+  qsort (manager->outputs, manager->n_outputs, sizeof (GfOutput), compare_outputs);
+
+  /* Now fix the clones */
+  for (i = 0; i < manager->n_outputs; i++)
+    {
+      GfOutput *output;
+
+      output = &manager->outputs[i];
+
+      for (j = 0; j < output->n_possible_clones; j++)
+        {
+          RROutput clone = GPOINTER_TO_INT (output->possible_clones[j]);
+
+          for (k = 0; k < manager->n_outputs; k++)
+            {
+              if (clone == (XID) manager->outputs[k].winsys_id)
+                {
+                  output->possible_clones[j] = &manager->outputs[k];
+                  break;
+                }
+            }
+        }
+    }
 }
 
 static GBytes *
@@ -309,6 +1655,21 @@ gf_monitor_manager_xrandr_read_edid (GfMonitorManager *manager,
 static void
 gf_monitor_manager_xrandr_ensure_initial_config (GfMonitorManager *manager)
 {
+  GfMonitorConfigManager *config_manager;
+  GfMonitorsConfig *config;
+
+  gf_monitor_manager_ensure_configured (manager);
+
+  /* Normally we don't rebuild our data structures until we see the
+   * RRScreenNotify event, but at least at startup we want to have the
+   * right configuration immediately.
+   */
+  gf_monitor_manager_read_current_state (manager);
+
+  config_manager = gf_monitor_manager_get_config_manager (manager);
+  config = gf_monitor_config_manager_get_current (config_manager);
+
+  gf_monitor_manager_update_logical_state_derived (manager, config);
 }
 
 static gboolean
@@ -317,8 +1678,53 @@ gf_monitor_manager_xrandr_apply_monitors_config (GfMonitorManager        *manage
                                                  GfMonitorsConfigMethod   method,
                                                  GError                 **error)
 {
-  g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Not implemented");
-  return FALSE;
+  GPtrArray *crtc_infos;
+  GPtrArray *output_infos;
+
+  if (!config)
+    {
+      gf_monitor_manager_rebuild_derived (manager, NULL);
+      return TRUE;
+    }
+
+  if (!gf_monitor_config_manager_assign (manager, config,
+                                         &crtc_infos, &output_infos,
+                                         error))
+    return FALSE;
+
+  if (method != GF_MONITORS_CONFIG_METHOD_VERIFY)
+    {
+      /*
+       * If the assignment has not changed, we won't get any notification about
+       * any new configuration from the X server; but we still need to update
+       * our own configuration, as something not applicable in Xrandr might
+       * have changed locally, such as the logical monitors scale. This means we
+       * must check that our new assignment actually changes anything, otherwise
+       * just update the logical state.
+       */
+      if (is_assignments_changed (manager,
+                                  (GfCrtcInfo **) crtc_infos->pdata,
+                                  crtc_infos->len,
+                                  (GfOutputInfo **) output_infos->pdata,
+                                  output_infos->len))
+        {
+          apply_crtc_assignments (manager,
+                                  TRUE,
+                                  (GfCrtcInfo **) crtc_infos->pdata,
+                                  crtc_infos->len,
+                                  (GfOutputInfo **) output_infos->pdata,
+                                  output_infos->len);
+        }
+      else
+        {
+          gf_monitor_manager_rebuild_derived (manager, config);
+        }
+    }
+
+  g_ptr_array_free (crtc_infos, TRUE);
+  g_ptr_array_free (output_infos, TRUE);
+
+  return TRUE;
 }
 
 static void
@@ -362,6 +1768,23 @@ gf_monitor_manager_xrandr_change_backlight (GfMonitorManager *manager,
                                             GfOutput         *output,
                                             gint              value)
 {
+  GfMonitorManagerXrandr *xrandr;
+  gint hw_value;
+  Atom atom;
+
+  xrandr = GF_MONITOR_MANAGER_XRANDR (manager);
+
+  hw_value = round ((gdouble) value / 100.0 * output->backlight_max + output->backlight_min);
+  atom = XInternAtom (xrandr->xdisplay, "Backlight", False);
+
+  xcb_randr_change_output_property (XGetXCBConnection (xrandr->xdisplay),
+                                    (XID)output->winsys_id,
+                                    atom, XCB_ATOM_INTEGER, 32,
+                                    XCB_PROP_MODE_REPLACE,
+                                    1, &hw_value);
+
+  /* We're not selecting for property notifies, so update the value immediately */
+  output->backlight = normalize_backlight (output, hw_value);
 }
 
 static void
@@ -594,5 +2017,41 @@ gboolean
 gf_monitor_manager_xrandr_handle_xevent (GfMonitorManagerXrandr *xrandr,
                                          XEvent                 *event)
 {
-  return FALSE;
+  GfMonitorManager *manager;
+  XRRScreenResources *resources;
+
+  manager = GF_MONITOR_MANAGER (xrandr);
+
+  if ((event->type - xrandr->rr_event_base) != RRScreenChangeNotify)
+    return FALSE;
+
+  XRRUpdateConfiguration (event);
+  gf_monitor_manager_read_current_state (manager);
+
+  resources = xrandr->resources;
+  if (!resources)
+    return TRUE;
+
+  if (resources->timestamp < resources->configTimestamp)
+    {
+      gf_monitor_manager_on_hotplug (manager);
+    }
+  else
+    {
+      GfMonitorsConfig *config;
+
+      config = NULL;
+
+      if (resources->timestamp == xrandr->last_xrandr_set_timestamp)
+        {
+          GfMonitorConfigManager *config_manager;
+
+          config_manager = gf_monitor_manager_get_config_manager (manager);
+          config = gf_monitor_config_manager_get_current (config_manager);
+        }
+
+      gf_monitor_manager_rebuild_derived (manager, config);
+    }
+
+  return TRUE;
 }
diff --git a/backends/gf-monitor-manager.c b/backends/gf-monitor-manager.c
index 5d2dea4..1f8e18e 100644
--- a/backends/gf-monitor-manager.c
+++ b/backends/gf-monitor-manager.c
@@ -26,14 +26,26 @@
  */
 
 #include "config.h"
+
+#include <string.h>
+
+#include "gf-crtc-private.h"
+#include "gf-logical-monitor-private.h"
+#include "gf-monitor-config-manager-private.h"
 #include "gf-monitor-manager-private.h"
-#include "gf-monitor-spec-private.h"
+#include "gf-monitor-normal-private.h"
 #include "gf-monitor-private.h"
+#include "gf-monitor-spec-private.h"
+#include "gf-monitor-tiled-private.h"
+#include "gf-monitors-config-private.h"
+#include "gf-output-private.h"
 
 typedef struct
 {
   GfBackend *backend;
 
+  gboolean   in_init;
+
   guint      bus_name_id;
 } GfMonitorManagerPrivate;
 
@@ -65,6 +77,25 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GfMonitorManager, gf_monitor_manager, GF_DBUS_
                                   G_ADD_PRIVATE (GfMonitorManager)
                                   G_IMPLEMENT_INTERFACE (GF_DBUS_TYPE_DISPLAY_CONFIG, 
gf_monitor_manager_display_config_init))
 
+static void
+gf_monitor_manager_update_monitor_modes_derived (GfMonitorManager *manager)
+{
+  GList *l;
+
+  for (l = manager->monitors; l; l = l->next)
+    {
+      GfMonitor *monitor = l->data;
+
+      gf_monitor_derive_current_mode (monitor);
+    }
+}
+
+static void
+gf_monitor_manager_notify_monitors_changed (GfMonitorManager *manager)
+{
+  g_signal_emit_by_name (manager, "monitors-changed");
+}
+
 static GfMonitor *
 find_monitor (GfMonitorManager *monitor_manager,
               MonitorMatchFunc  match_func)
@@ -85,6 +116,373 @@ find_monitor (GfMonitorManager *monitor_manager,
 }
 
 static gboolean
+gf_monitor_manager_is_config_applicable (GfMonitorManager  *manager,
+                                         GfMonitorsConfig  *config,
+                                         GError           **error)
+{
+  GList *l;
+
+  for (l = config->logical_monitor_configs; l; l = l->next)
+    {
+      GfLogicalMonitorConfig *logical_monitor_config = l->data;
+      gfloat scale = logical_monitor_config->scale;
+      GList *k;
+
+      for (k = logical_monitor_config->monitor_configs; k; k = k->next)
+        {
+          GfMonitorConfig *monitor_config = k->data;
+          GfMonitorSpec *monitor_spec = monitor_config->monitor_spec;
+          GfMonitorModeSpec *mode_spec = monitor_config->mode_spec;
+          GfMonitor *monitor;
+          GfMonitorMode *monitor_mode;
+
+          monitor = gf_monitor_manager_get_monitor_from_spec (manager, monitor_spec);
+          if (!monitor)
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Specified monitor not found");
+
+              return FALSE;
+            }
+
+          monitor_mode = gf_monitor_get_mode_from_spec (monitor, mode_spec);
+          if (!monitor_mode)
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Specified monitor mode not available");
+
+              return FALSE;
+            }
+
+          if (!gf_monitor_manager_is_scale_supported (manager, config->layout_mode,
+                                                      monitor, monitor_mode, scale))
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Scale not supported by backend");
+
+              return FALSE;
+            }
+
+        }
+    }
+
+  return TRUE;
+}
+
+static gboolean
+gf_monitor_manager_is_config_complete (GfMonitorManager *manager,
+                                       GfMonitorsConfig *config)
+{
+  GList *l;
+  guint configured_monitor_count = 0;
+  guint expected_monitor_count = 0;
+
+  for (l = config->logical_monitor_configs; l; l = l->next)
+    {
+      GfLogicalMonitorConfig *logical_monitor_config = l->data;
+      GList *k;
+
+      for (k = logical_monitor_config->monitor_configs; k; k = k->next)
+        configured_monitor_count++;
+    }
+
+  for (l = manager->monitors; l; l = l->next)
+    {
+      GfMonitor *monitor = l->data;
+
+      if (gf_monitor_is_laptop_panel (monitor))
+        {
+          if (!gf_monitor_manager_is_lid_closed (manager))
+            expected_monitor_count++;
+        }
+      else
+        {
+          expected_monitor_count++;
+        }
+    }
+
+  if (configured_monitor_count != expected_monitor_count)
+    return FALSE;
+
+  return gf_monitor_manager_is_config_applicable (manager, config, NULL);
+}
+
+static gboolean
+should_use_stored_config (GfMonitorManager *manager)
+{
+  GfMonitorManagerPrivate *priv;
+
+  priv = gf_monitor_manager_get_instance_private (manager);
+
+  return (priv->in_init || !gf_monitor_manager_has_hotplug_mode_update (manager));
+}
+
+static gfloat
+derive_configured_global_scale (GfMonitorManager *manager,
+                                GfMonitorsConfig *config)
+{
+  GfLogicalMonitorConfig *logical_monitor_config;
+
+  logical_monitor_config = config->logical_monitor_configs->data;
+
+  return logical_monitor_config->scale;
+}
+
+static gfloat
+calculate_monitor_scale (GfMonitorManager *manager,
+                         GfMonitor        *monitor)
+{
+  GfMonitorMode *monitor_mode;
+
+  monitor_mode = gf_monitor_get_current_mode (monitor);
+  return gf_monitor_manager_calculate_monitor_mode_scale (manager, monitor,
+                                                          monitor_mode);
+}
+
+static gfloat
+derive_calculated_global_scale (GfMonitorManager *manager)
+{
+  GfMonitor *primary_monitor;
+
+  primary_monitor = gf_monitor_manager_get_primary_monitor (manager);
+  if (!primary_monitor)
+    return 1.0;
+
+  return calculate_monitor_scale (manager, primary_monitor);
+}
+
+static GfLogicalMonitor *
+logical_monitor_from_layout (GfMonitorManager *manager,
+                             GList            *logical_monitors,
+                             GfRectangle      *layout)
+{
+  GList *l;
+
+  for (l = logical_monitors; l; l = l->next)
+    {
+      GfLogicalMonitor *logical_monitor = l->data;
+
+      if (gf_rectangle_equal (layout, &logical_monitor->rect))
+        return logical_monitor;
+    }
+
+  return NULL;
+}
+
+static gfloat
+derive_scale_from_config (GfMonitorManager *manager,
+                          GfMonitorsConfig *config,
+                          GfRectangle      *layout)
+{
+  GList *l;
+
+  for (l = config->logical_monitor_configs; l; l = l->next)
+    {
+      GfLogicalMonitorConfig *logical_monitor_config = l->data;
+
+      if (gf_rectangle_equal (layout, &logical_monitor_config->layout))
+        return logical_monitor_config->scale;
+    }
+
+  g_warning ("Missing logical monitor, using scale 1");
+  return 1.0;
+}
+
+static void
+gf_monitor_manager_set_primary_logical_monitor (GfMonitorManager *manager,
+                                                GfLogicalMonitor *logical_monitor)
+{
+  manager->primary_logical_monitor = logical_monitor;
+  if (logical_monitor)
+    gf_logical_monitor_make_primary (logical_monitor);
+}
+
+static void
+gf_monitor_manager_rebuild_logical_monitors_derived (GfMonitorManager *manager,
+                                                     GfMonitorsConfig *config)
+{
+  GList *logical_monitors = NULL;
+  GList *l;
+  gint monitor_number;
+  GfLogicalMonitor *primary_logical_monitor = NULL;
+  gboolean use_global_scale;
+  gfloat global_scale = 0.0;
+  GfMonitorManagerCapability capabilities;
+
+  monitor_number = 0;
+
+  capabilities = gf_monitor_manager_get_capabilities (manager);
+  use_global_scale = !!(capabilities & GF_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED);
+
+  if (use_global_scale)
+    {
+      if (config)
+        global_scale = derive_configured_global_scale (manager, config);
+      else
+        global_scale = derive_calculated_global_scale (manager);
+    }
+
+  for (l = manager->monitors; l; l = l->next)
+    {
+      GfMonitor *monitor = l->data;
+      GfLogicalMonitor *logical_monitor;
+      GfRectangle layout;
+
+      if (!gf_monitor_is_active (monitor))
+        continue;
+
+      gf_monitor_derive_layout (monitor, &layout);
+      logical_monitor = logical_monitor_from_layout (manager, logical_monitors,
+                                                     &layout);
+      if (logical_monitor)
+        {
+          gf_logical_monitor_add_monitor (logical_monitor, monitor);
+        }
+      else
+        {
+          gfloat scale;
+
+          if (use_global_scale)
+            scale = global_scale;
+          else if (config)
+            scale = derive_scale_from_config (manager, config, &layout);
+          else
+            scale = calculate_monitor_scale (manager, monitor);
+
+          g_assert (scale > 0);
+
+          logical_monitor = gf_logical_monitor_new_derived (manager, monitor,
+                                                            &layout, scale,
+                                                            monitor_number);
+
+          logical_monitors = g_list_append (logical_monitors, logical_monitor);
+          monitor_number++;
+        }
+
+      if (gf_monitor_is_primary (monitor))
+        primary_logical_monitor = logical_monitor;
+    }
+
+  manager->logical_monitors = logical_monitors;
+
+  /*
+   * If no monitor was marked as primary, fall back on marking the first
+   * logical monitor the primary one.
+   */
+  if (!primary_logical_monitor && manager->logical_monitors)
+    primary_logical_monitor = g_list_first (manager->logical_monitors)->data;
+
+  gf_monitor_manager_set_primary_logical_monitor (manager, primary_logical_monitor);
+}
+
+static gboolean
+gf_monitor_manager_apply_monitors_config (GfMonitorManager        *manager,
+                                          GfMonitorsConfig        *config,
+                                          GfMonitorsConfigMethod   method,
+                                          GError                 **error)
+{
+  GfMonitorManagerClass *manager_class;
+
+  g_assert (!config || !(config->flags & GF_MONITORS_CONFIG_FLAG_MIGRATED));
+
+  manager_class = GF_MONITOR_MANAGER_GET_CLASS (manager);
+
+  if (!manager_class->apply_monitors_config (manager, config, method, error))
+    return FALSE;
+
+  switch (method)
+    {
+      case GF_MONITORS_CONFIG_METHOD_TEMPORARY:
+      case GF_MONITORS_CONFIG_METHOD_PERSISTENT:
+        gf_monitor_config_manager_set_current (manager->config_manager, config);
+        break;
+
+      case GF_MONITORS_CONFIG_METHOD_VERIFY:
+      default:
+        break;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+is_main_tiled_monitor_output (GfOutput *output)
+{
+  return output->tile_info.loc_h_tile == 0 && output->tile_info.loc_v_tile == 0;
+}
+
+static void
+rebuild_monitors (GfMonitorManager *manager)
+{
+  guint i;
+
+  if (manager->monitors)
+    {
+      g_list_free_full (manager->monitors, g_object_unref);
+      manager->monitors = NULL;
+    }
+
+  for (i = 0; i < manager->n_outputs; i++)
+    {
+      GfOutput *output = &manager->outputs[i];
+
+      if (output->tile_info.group_id)
+        {
+          if (is_main_tiled_monitor_output (output))
+            {
+              GfMonitorTiled *monitor_tiled;
+
+              monitor_tiled = gf_monitor_tiled_new (manager, output);
+              manager->monitors = g_list_append (manager->monitors, monitor_tiled);
+            }
+        }
+      else
+        {
+          GfMonitorNormal *monitor_normal;
+
+          monitor_normal = gf_monitor_normal_new (manager, output);
+          manager->monitors = g_list_append (manager->monitors, monitor_normal);
+        }
+    }
+}
+
+static void
+free_output_array (GfOutput *old_outputs,
+                   gint      n_old_outputs)
+{
+  gint i;
+
+  for (i = 0; i < n_old_outputs; i++)
+    gf_monitor_manager_clear_output (&old_outputs[i]);
+
+  g_free (old_outputs);
+}
+
+static void
+free_mode_array (GfCrtcMode *old_modes,
+                 gint        n_old_modes)
+{
+  gint i;
+
+  for (i = 0; i < n_old_modes; i++)
+    gf_monitor_manager_clear_mode (&old_modes[i]);
+
+  g_free (old_modes);
+}
+
+static void
+free_crtc_array (GfCrtc *old_crtcs,
+                 gint    n_old_crtcs)
+{
+  gint i;
+
+  for (i = 0; i < n_old_crtcs; i++)
+    gf_monitor_manager_clear_crtc (&old_crtcs[i]);
+
+  g_free (old_crtcs);
+}
+
+static gboolean
 gf_monitor_manager_handle_get_resources (GfDBusDisplayConfig   *skeleton,
                                          GDBusMethodInvocation *invocation)
 {
@@ -225,6 +623,8 @@ gf_monitor_manager_constructed (GObject *object)
                                       name_lost_cb,
                                       g_object_ref (manager),
                                       g_object_unref);
+
+  priv->in_init = FALSE;
 }
 
 static void
@@ -339,6 +739,11 @@ gf_monitor_manager_class_init (GfMonitorManagerClass *manager_class)
 static void
 gf_monitor_manager_init (GfMonitorManager *manager)
 {
+  GfMonitorManagerPrivate *priv;
+
+  priv = gf_monitor_manager_get_instance_private (manager);
+
+  priv->in_init = TRUE;
 }
 
 GfBackend *
@@ -351,6 +756,28 @@ gf_monitor_manager_get_backend (GfMonitorManager *manager)
   return priv->backend;
 }
 
+void
+gf_monitor_manager_rebuild_derived (GfMonitorManager *manager,
+                                    GfMonitorsConfig *config)
+{
+  GfMonitorManagerPrivate *priv;
+  GList *old_logical_monitors;
+
+  priv = gf_monitor_manager_get_instance_private (manager);
+
+  gf_monitor_manager_update_monitor_modes_derived (manager);
+
+  if (priv->in_init)
+    return;
+
+  old_logical_monitors = manager->logical_monitors;
+
+  gf_monitor_manager_update_logical_state_derived (manager, config);
+  gf_monitor_manager_notify_monitors_changed (manager);
+
+  g_list_free_full (old_logical_monitors, g_object_unref);
+}
+
 GfMonitor *
 gf_monitor_manager_get_primary_monitor (GfMonitorManager *manager)
 {
@@ -386,6 +813,59 @@ gf_monitor_manager_get_monitors (GfMonitorManager *manager)
   return manager->monitors;
 }
 
+gboolean
+gf_monitor_manager_has_hotplug_mode_update (GfMonitorManager *manager)
+{
+  guint i;
+
+  for (i = 0; i < manager->n_outputs; i++)
+    {
+      GfOutput *output = &manager->outputs[i];
+
+      if (output->hotplug_mode_update)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+void
+gf_monitor_manager_read_current_state (GfMonitorManager *manager)
+{
+  GfOutput *old_outputs;
+  GfCrtc *old_crtcs;
+  GfCrtcMode *old_modes;
+  guint n_old_outputs;
+  guint n_old_crtcs;
+  guint n_old_modes;
+
+  /* Some implementations of read_current use the existing information
+   * we have available, so don't free the old configuration until after
+   * read_current finishes.
+   */
+  old_outputs = manager->outputs;
+  n_old_outputs = manager->n_outputs;
+  old_crtcs = manager->crtcs;
+  n_old_crtcs = manager->n_crtcs;
+  old_modes = manager->modes;
+  n_old_modes = manager->n_modes;
+
+  manager->serial++;
+  GF_MONITOR_MANAGER_GET_CLASS (manager)->read_current (manager);
+
+  rebuild_monitors (manager);
+
+  free_output_array (old_outputs, n_old_outputs);
+  free_mode_array (old_modes, n_old_modes);
+  free_crtc_array (old_crtcs, n_old_crtcs);
+}
+
+void
+gf_monitor_manager_on_hotplug (GfMonitorManager *manager)
+{
+  gf_monitor_manager_ensure_configured (manager);
+}
+
 void
 gf_monitor_manager_tiled_monitor_added (GfMonitorManager *manager,
                                         GfMonitor        *monitor)
@@ -422,6 +902,145 @@ gf_monitor_manager_is_transform_handled (GfMonitorManager   *manager,
   return manager_class->is_transform_handled (manager, crtc, transform);
 }
 
+GfMonitorsConfig *
+gf_monitor_manager_ensure_configured (GfMonitorManager *manager)
+{
+  gboolean use_stored_config;
+  GfMonitorsConfigMethod method;
+  GfMonitorsConfigMethod fallback_method;
+  GfMonitorsConfig *config;
+  GError *error;
+
+  use_stored_config = should_use_stored_config (manager);
+  if (use_stored_config)
+    method = GF_MONITORS_CONFIG_METHOD_PERSISTENT;
+  else
+    method = GF_MONITORS_CONFIG_METHOD_TEMPORARY;
+
+  fallback_method = GF_MONITORS_CONFIG_METHOD_TEMPORARY;
+  config = NULL;
+  error = NULL;
+
+  if (use_stored_config)
+    {
+      config = gf_monitor_config_manager_get_stored (manager->config_manager);
+
+      if (config)
+        {
+          if (!gf_monitor_manager_apply_monitors_config (manager, config,
+                                                         method, &error))
+            {
+              config = NULL;
+              g_warning ("Failed to use stored monitor configuration: %s",
+                         error->message);
+              g_clear_error (&error);
+            }
+          else
+            {
+              g_object_ref (config);
+              goto done;
+            }
+        }
+    }
+
+  config = gf_monitor_config_manager_create_suggested (manager->config_manager);
+  if (config)
+    {
+      if (!gf_monitor_manager_apply_monitors_config (manager, config,
+                                                     method, &error))
+        {
+          g_clear_object (&config);
+          g_warning ("Failed to use suggested monitor configuration: %s",
+                     error->message);
+          g_clear_error (&error);
+        }
+      else
+        {
+          goto done;
+        }
+    }
+
+  config = gf_monitor_config_manager_get_previous (manager->config_manager);
+  if (config)
+    {
+      config = g_object_ref (config);
+
+      if (gf_monitor_manager_is_config_complete (manager, config))
+        {
+          if (!gf_monitor_manager_apply_monitors_config (manager, config,
+                                                         method, &error))
+            {
+              g_warning ("Failed to use suggested monitor configuration: %s",
+                         error->message);
+              g_clear_error (&error);
+            }
+          else
+            {
+              goto done;
+            }
+        }
+
+      g_clear_object (&config);
+    }
+
+  config = gf_monitor_config_manager_create_linear (manager->config_manager);
+  if (config)
+    {
+      if (!gf_monitor_manager_apply_monitors_config (manager, config,
+                                                     method, &error))
+        {
+          g_clear_object (&config);
+          g_warning ("Failed to use linear monitor configuration: %s",
+                     error->message);
+          g_clear_error (&error);
+        }
+      else
+        {
+          goto done;
+        }
+    }
+
+  config = gf_monitor_config_manager_create_fallback (manager->config_manager);
+  if (config)
+    {
+      if (!gf_monitor_manager_apply_monitors_config (manager, config,
+                                                     fallback_method,
+                                                     &error))
+        {
+          g_clear_object (&config);
+          g_warning ("Failed to use fallback monitor configuration: %s",
+                 error->message);
+          g_clear_error (&error);
+        }
+      else
+        {
+          goto done;
+        }
+    }
+
+done:
+  if (!config)
+    {
+      gf_monitor_manager_apply_monitors_config (manager, NULL,
+                                                fallback_method,
+                                                &error);
+      return NULL;
+    }
+
+  g_object_unref (config);
+
+  return config;
+}
+
+void
+gf_monitor_manager_update_logical_state_derived (GfMonitorManager *manager,
+                                                 GfMonitorsConfig *config)
+{
+  manager->layout_mode = GF_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL;
+
+  gf_monitor_manager_rebuild_logical_monitors_derived (manager, config);
+}
+
 gboolean
 gf_monitor_manager_is_lid_closed (GfMonitorManager *manager)
 {
@@ -440,6 +1059,50 @@ gf_monitor_manager_calculate_monitor_mode_scale (GfMonitorManager *manager,
   return manager_class->calculate_monitor_mode_scale (manager, monitor, monitor_mode);
 }
 
+gfloat *
+gf_monitor_manager_calculate_supported_scales (GfMonitorManager           *manager,
+                                               GfLogicalMonitorLayoutMode  layout_mode,
+                                               GfMonitor                  *monitor,
+                                               GfMonitorMode              *monitor_mode,
+                                               gint                       *n_supported_scales)
+{
+  GfMonitorManagerClass *manager_class;
+
+  manager_class = GF_MONITOR_MANAGER_GET_CLASS (manager);
+
+  return manager_class->calculate_supported_scales (manager, layout_mode,
+                                                    monitor, monitor_mode,
+                                                    n_supported_scales);
+}
+
+gboolean
+gf_monitor_manager_is_scale_supported (GfMonitorManager           *manager,
+                                       GfLogicalMonitorLayoutMode  layout_mode,
+                                       GfMonitor                  *monitor,
+                                       GfMonitorMode              *monitor_mode,
+                                       gfloat                      scale)
+{
+  gfloat *supported_scales;
+  gint n_supported_scales;
+  gint i;
+
+  supported_scales = gf_monitor_manager_calculate_supported_scales (manager, layout_mode,
+                                                                    monitor, monitor_mode,
+                                                                    &n_supported_scales);
+
+  for (i = 0; i < n_supported_scales; i++)
+    {
+      if (supported_scales[i] == scale)
+        {
+          g_free (supported_scales);
+          return TRUE;
+        }
+    }
+
+  g_free (supported_scales);
+  return FALSE;
+}
+
 GfMonitorManagerCapability
 gf_monitor_manager_get_capabilities (GfMonitorManager *manager)
 {
@@ -460,6 +1123,49 @@ gf_monitor_manager_get_default_layout_mode (GfMonitorManager *manager)
   return manager_class->get_default_layout_mode (manager);
 }
 
+GfMonitorConfigManager *
+gf_monitor_manager_get_config_manager (GfMonitorManager *manager)
+{
+  return manager->config_manager;
+}
+
+void
+gf_monitor_manager_clear_output (GfOutput *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);
+
+  if (output->driver_notify)
+    output->driver_notify (output);
+
+  memset (output, 0, sizeof (*output));
+}
+
+void
+gf_monitor_manager_clear_mode (GfCrtcMode *mode)
+{
+  g_free (mode->name);
+
+  if (mode->driver_notify)
+    mode->driver_notify (mode);
+
+  memset (mode, 0, sizeof (*mode));
+}
+
+void
+gf_monitor_manager_clear_crtc (GfCrtc *crtc)
+{
+  if (crtc->driver_notify)
+    crtc->driver_notify (crtc);
+
+  memset (crtc, 0, sizeof (*crtc));
+}
+
 gboolean
 gf_monitor_manager_get_is_builtin_display_on (GfMonitorManager *manager)
 {
diff --git a/configure.ac b/configure.ac
index 896021c..b0ca8a0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -105,7 +105,9 @@ PKG_CHECK_MODULES([BACKENDS], [
   glib-2.0 >= $GLIB_REQUIRED
   gnome-desktop-3.0 >= $LIBGNOME_DESKTOP_REQUIRED
   upower-glib >= $UPOWER_GLIB_REQUIRED
+  xcb-randr
   xrandr >= $XRANDR_REQUIRED
+  x11-xcb
   x11
 ])
 


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