[gnome-control-center/wip/rtcm/new-display-config: 4/7] display: Provide an implementation for mutter's new display config API



commit e40a7721c250e35bda35c4daf3570d23019e97a4
Author: Rui Matos <tiagomatos gmail com>
Date:   Tue Mar 7 15:58:03 2017 +0100

    display: Provide an implementation for mutter's new display config API

 panels/display/Makefile.am                      |    4 +
 panels/display/cc-display-config-dbus.c         | 1527 +++++++++++++++++++++++
 panels/display/cc-display-config-dbus.h         |   43 +
 panels/display/cc-display-config-manager-dbus.c |  188 +++
 panels/display/cc-display-config-manager-dbus.h |   37 +
 panels/display/cc-display-panel.c               |    1 +
 6 files changed, 1800 insertions(+), 0 deletions(-)
---
diff --git a/panels/display/Makefile.am b/panels/display/Makefile.am
index a68bb21..9b3f522 100644
--- a/panels/display/Makefile.am
+++ b/panels/display/Makefile.am
@@ -13,10 +13,14 @@ libdisplay_la_SOURCES =             \
        cc-display-config.h     \
        cc-display-config-rr.c  \
        cc-display-config-rr.h  \
+       cc-display-config-dbus.c        \
+       cc-display-config-dbus.h        \
        cc-display-config-manager.c     \
        cc-display-config-manager.h     \
        cc-display-config-manager-rr.c  \
        cc-display-config-manager-rr.h  \
+       cc-display-config-manager-dbus.c        \
+       cc-display-config-manager-dbus.h        \
        cc-display-panel.c      \
        cc-display-panel.h      \
        cc-night-light-dialog.c \
diff --git a/panels/display/cc-display-config-dbus.c b/panels/display/cc-display-config-dbus.c
new file mode 100644
index 0000000..014d8d6
--- /dev/null
+++ b/panels/display/cc-display-config-dbus.c
@@ -0,0 +1,1527 @@
+/*
+ * Copyright (C) 2017  Red Hat, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <gio/gio.h>
+
+#include "cc-display-config-dbus.h"
+
+#define MODE_FORMAT "(iiddu)"
+#define MODES_FORMAT "a" MODE_FORMAT
+
+#define MONITOR_SPEC_FORMAT "(ssss)"
+#define MONITOR_FORMAT "(" MONITOR_SPEC_FORMAT MODES_FORMAT "a{sv})"
+#define MONITORS_FORMAT "a" MONITOR_FORMAT
+
+#define LOGICAL_MONITOR_FORMAT "(iiduba" MONITOR_SPEC_FORMAT "a{sv})"
+#define LOGICAL_MONITORS_FORMAT "a" LOGICAL_MONITOR_FORMAT
+
+#define CURRENT_STATE_FORMAT "(u" MONITORS_FORMAT LOGICAL_MONITORS_FORMAT "ad" "a{sv})"
+
+typedef enum _CcDisplayModeFlags
+{
+  MODE_PREFERRED = 1 << 0,
+  MODE_CURRENT = 1 << 1
+} CcDisplayModeFlags;
+
+struct _CcDisplayModeDBus
+{
+  CcDisplayMode parent_instance;
+
+  int width;
+  int height;
+  double refresh_rate;
+  double preferred_scale;
+  guint32 flags;
+};
+
+G_DEFINE_TYPE (CcDisplayModeDBus,
+               cc_display_mode_dbus,
+               CC_TYPE_DISPLAY_MODE)
+
+static gboolean
+cc_display_mode_dbus_equal (const CcDisplayModeDBus *m1,
+                            const CcDisplayModeDBus *m2)
+{
+  if (!m1 && !m2)
+    return TRUE;
+  else if (!m1 || !m2)
+    return FALSE;
+
+  return m1->width == m2->width &&
+    m1->height == m2->height &&
+    m1->refresh_rate == m2->refresh_rate;
+}
+
+static void
+cc_display_mode_dbus_get_resolution (CcDisplayMode *pself,
+                                     int *w, int *h)
+{
+  CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself);
+
+  if (w)
+    *w = self->width;
+  if (h)
+    *h = self->height;
+}
+
+static gboolean
+cc_display_mode_dbus_is_interlaced (CcDisplayMode *pself)
+{
+  /* XXX: add to the dbus api ? */
+  return FALSE;
+}
+
+static int
+cc_display_mode_dbus_get_freq (CcDisplayMode *pself)
+{
+  CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself);
+
+  return self->refresh_rate;
+}
+
+static double
+cc_display_mode_dbus_get_freq_f (CcDisplayMode *pself)
+{
+  CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself);
+
+  return self->refresh_rate;
+}
+
+static void
+cc_display_mode_dbus_init (CcDisplayModeDBus *self)
+{
+}
+
+static void
+cc_display_mode_dbus_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (cc_display_mode_dbus_parent_class)->finalize (object);
+}
+
+static void
+cc_display_mode_dbus_class_init (CcDisplayModeDBusClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  CcDisplayModeClass *parent_class = CC_DISPLAY_MODE_CLASS (klass);
+
+  gobject_class->finalize = cc_display_mode_dbus_finalize;
+
+  parent_class->get_resolution = cc_display_mode_dbus_get_resolution;
+  parent_class->is_interlaced = cc_display_mode_dbus_is_interlaced;
+  parent_class->get_freq = cc_display_mode_dbus_get_freq;
+  parent_class->get_freq_f = cc_display_mode_dbus_get_freq_f;
+}
+
+static CcDisplayModeDBus *
+cc_display_mode_dbus_new (GVariant *variant)
+{
+  CcDisplayModeDBus *self = g_object_new (CC_TYPE_DISPLAY_MODE_DBUS, NULL);
+
+  g_variant_get (variant, MODE_FORMAT,
+                 &self->width,
+                 &self->height,
+                 &self->refresh_rate,
+                 &self->preferred_scale,
+                 &self->flags);
+
+  return self;
+}
+
+
+#define CC_TYPE_DISPLAY_LOGICAL_MONITOR (cc_display_logical_monitor_get_type ())
+G_DECLARE_FINAL_TYPE (CcDisplayLogicalMonitor, cc_display_logical_monitor,
+                      CC, DISPLAY_LOGICAL_MONITOR, GObject)
+
+struct _CcDisplayLogicalMonitor
+{
+  GObject parent_instance;
+
+  int x;
+  int y;
+  double scale;
+  CcDisplayRotation rotation;
+  gboolean primary;
+
+  GHashTable *monitors;
+};
+
+G_DEFINE_TYPE (CcDisplayLogicalMonitor,
+               cc_display_logical_monitor,
+               G_TYPE_OBJECT)
+
+static gboolean
+cc_display_logical_monitor_equal (const CcDisplayLogicalMonitor *m1,
+                                  const CcDisplayLogicalMonitor *m2)
+{
+  if (!m1 && !m2)
+    return TRUE;
+  else if (!m1 || !m2)
+    return FALSE;
+
+  return m1->x == m2->x &&
+    m1->y == m2->y &&
+    m1->scale == m2->scale &&
+    m1->rotation == m2->rotation &&
+    m1->primary == m2->primary;
+}
+
+static void
+cc_display_logical_monitor_init (CcDisplayLogicalMonitor *self)
+{
+  self->scale = 1.0;
+  self->monitors = g_hash_table_new (NULL, NULL);
+}
+
+static void
+cc_display_logical_monitor_finalize (GObject *object)
+{
+  CcDisplayLogicalMonitor *self = CC_DISPLAY_LOGICAL_MONITOR (object);
+
+  g_warn_if_fail (g_hash_table_size (self->monitors) == 0);
+  g_clear_pointer (&self->monitors, g_hash_table_destroy);
+
+  G_OBJECT_CLASS (cc_display_logical_monitor_parent_class)->finalize (object);
+}
+
+static void
+cc_display_logical_monitor_class_init (CcDisplayLogicalMonitorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = cc_display_logical_monitor_finalize;
+}
+
+
+typedef enum _CcDisplayMonitorUnderscanning
+{
+  UNDERSCANNING_UNSUPPORTED = 0,
+  UNDERSCANNING_DISABLED,
+  UNDERSCANNING_ENABLED
+} CcDisplayMonitorUnderscanning;
+
+struct _CcDisplayMonitorDBus
+{
+  CcDisplayMonitor parent_instance;
+  CcDisplayConfigDBus *config;
+
+  gchar *connector_name;
+  gchar *vendor_name;
+  gchar *product_name;
+  gchar *product_serial;
+  gchar *display_name;
+
+  int width_mm;
+  int height_mm;
+  gboolean builtin;
+  CcDisplayMonitorUnderscanning underscanning;
+  int max_width;
+  int max_height;
+
+  GList *modes;
+  CcDisplayMode *current_mode;
+  CcDisplayMode *preferred_mode;
+
+  CcDisplayLogicalMonitor *logical_monitor;
+};
+
+G_DEFINE_TYPE (CcDisplayMonitorDBus,
+               cc_display_monitor_dbus,
+               CC_TYPE_DISPLAY_MONITOR)
+
+static void
+register_logical_monitor (CcDisplayConfigDBus *self,
+                          CcDisplayLogicalMonitor *logical_monitor);
+static void
+cc_display_config_dbus_set_primary (CcDisplayConfigDBus *self,
+                                    CcDisplayMonitorDBus *new_primary);
+static void
+cc_display_config_dbus_unset_primary (CcDisplayConfigDBus *self,
+                                      CcDisplayMonitorDBus *old_primary);
+static void
+cc_display_config_dbus_ensure_non_offset_coords (CcDisplayConfigDBus *self);
+static void
+cc_display_config_dbus_append_right (CcDisplayConfigDBus *self,
+                                     CcDisplayLogicalMonitor *monitor);
+static void
+cc_display_config_dbus_ensure_gapless (CcDisplayConfigDBus *self);
+static void
+cc_display_config_dbus_make_linear (CcDisplayConfigDBus *self);
+
+
+static const char *
+cc_display_monitor_dbus_get_display_name (CcDisplayMonitor *pself)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  if (self->display_name)
+    return self->display_name;
+
+  return self->connector_name;
+}
+
+static const char *
+cc_display_monitor_dbus_get_connector_name (CcDisplayMonitor *pself)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  return self->connector_name;
+}
+
+static gboolean
+cc_display_monitor_dbus_is_builtin (CcDisplayMonitor *pself)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  return self->builtin;
+}
+
+static gboolean
+cc_display_monitor_dbus_is_primary (CcDisplayMonitor *pself)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  if (self->logical_monitor)
+    return self->logical_monitor->primary;
+
+  return FALSE;
+}
+
+static void
+cc_display_monitor_dbus_set_primary (CcDisplayMonitor *pself,
+                                     gboolean primary)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  if (primary)
+    cc_display_config_dbus_set_primary (self->config, self);
+  else
+    cc_display_config_dbus_unset_primary (self->config, self);
+}
+
+static gboolean
+cc_display_monitor_dbus_is_active (CcDisplayMonitor *pself)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  return self->logical_monitor != NULL;
+}
+
+static void
+cc_display_monitor_dbus_set_logical_monitor (CcDisplayMonitorDBus *self,
+                                             CcDisplayLogicalMonitor *logical_monitor)
+{
+  gboolean was_primary = FALSE;
+
+  if (self->logical_monitor)
+    {
+      was_primary = self->logical_monitor->primary;
+      if (was_primary)
+        cc_display_config_dbus_unset_primary (self->config, self);
+      g_hash_table_remove (self->logical_monitor->monitors, self);
+      g_object_unref (self->logical_monitor);
+    }
+
+  self->logical_monitor = logical_monitor;
+
+  if (self->logical_monitor)
+    {
+      g_hash_table_add (self->logical_monitor->monitors, self);
+      g_object_ref (self->logical_monitor);
+      if (was_primary)
+        cc_display_config_dbus_set_primary (self->config, self);
+    }
+}
+
+static void
+cc_display_monitor_dbus_set_active (CcDisplayMonitor *pself,
+                                    gboolean active)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  if (!self->current_mode && active)
+    {
+      if (self->preferred_mode)
+        self->current_mode = self->preferred_mode;
+      else if (self->modes)
+        self->current_mode = (CcDisplayMode *) self->modes->data;
+      else
+        g_warning ("Couldn't find a mode to activate monitor at %s", self->connector_name);
+    }
+
+  if (!self->logical_monitor && active)
+    {
+      CcDisplayLogicalMonitor *logical_monitor;
+      logical_monitor = g_object_new (CC_TYPE_DISPLAY_LOGICAL_MONITOR, NULL);
+      cc_display_monitor_dbus_set_logical_monitor (self, logical_monitor);
+      cc_display_config_dbus_append_right (self->config, logical_monitor);
+      register_logical_monitor (self->config, logical_monitor);
+    }
+  else if (self->logical_monitor && !active)
+    {
+      cc_display_monitor_dbus_set_logical_monitor (self, NULL);
+      cc_display_config_dbus_ensure_gapless (self->config);
+    }
+}
+
+static CcDisplayRotation
+cc_display_monitor_dbus_get_rotation (CcDisplayMonitor *pself)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  if (self->logical_monitor)
+    return self->logical_monitor->rotation;
+
+  return CC_DISPLAY_ROTATION_NONE;
+}
+
+static void
+cc_display_monitor_dbus_set_rotation (CcDisplayMonitor *pself,
+                                      CcDisplayRotation rotation)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  if (self->logical_monitor)
+    self->logical_monitor->rotation = rotation;
+
+  /* See comment in ensure_gapless() for why we disregard the
+     existing layout here. */
+  cc_display_config_dbus_make_linear (self->config);
+}
+
+static gboolean
+cc_display_monitor_dbus_supports_rotation (CcDisplayMonitor *pself,
+                                           CcDisplayRotation rotation)
+{
+  return TRUE;
+}
+
+static void
+cc_display_monitor_dbus_get_physical_size (CcDisplayMonitor *pself,
+                                           int *w, int *h)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  if (w)
+    *w = self->width_mm;
+  if (h)
+    *h = self->height_mm;
+}
+
+static void
+cc_display_monitor_dbus_get_geometry (CcDisplayMonitor *pself,
+                                      int *x, int *y, int *w, int *h)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+  CcDisplayMode *mode = NULL;
+
+  /* XXX: physical or logical ? */
+
+  if (self->logical_monitor)
+    {
+      if (x)
+        *x = self->logical_monitor->x;
+      if (y)
+        *y = self->logical_monitor->y;
+    }
+  else
+    {
+      if (x)
+        *x = -1;
+      if (y)
+        *y = -1;
+    }
+
+  if (self->current_mode)
+    mode = self->current_mode;
+  else if (self->preferred_mode)
+    mode = self->preferred_mode;
+  else if (self->modes)
+    mode = CC_DISPLAY_MODE (self->modes->data);
+
+  if (mode)
+    cc_display_mode_get_resolution (mode, w, h);
+  else
+    {
+      g_warning ("Monitor at %s has no modes?", self->connector_name);
+      if (w)
+        *w = -1;
+      if (h)
+        *h = -1;
+    }
+}
+
+static CcDisplayMode *
+cc_display_monitor_dbus_get_mode (CcDisplayMonitor *pself)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  return self->current_mode;
+}
+
+static CcDisplayMode *
+cc_display_monitor_dbus_get_preferred_mode (CcDisplayMonitor *pself)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  return self->preferred_mode;
+}
+
+static guint32
+cc_display_monitor_dbus_get_id (CcDisplayMonitor *pself)
+{
+  return 0;
+}
+
+static GList *
+cc_display_monitor_dbus_get_modes (CcDisplayMonitor *pself)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  return self->modes;
+}
+
+static gboolean
+cc_display_monitor_dbus_supports_underscanning (CcDisplayMonitor *pself)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  return self->underscanning != UNDERSCANNING_UNSUPPORTED;
+}
+
+static gboolean
+cc_display_monitor_dbus_get_underscanning (CcDisplayMonitor *pself)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  return self->underscanning == UNDERSCANNING_ENABLED;
+}
+
+static void
+cc_display_monitor_dbus_set_underscanning (CcDisplayMonitor *pself,
+                                           gboolean underscanning)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  if (self->underscanning == UNDERSCANNING_UNSUPPORTED)
+    return;
+
+  if (underscanning)
+    self->underscanning = UNDERSCANNING_ENABLED;
+  else
+    self->underscanning = UNDERSCANNING_DISABLED;
+}
+
+static CcDisplayMode *
+cc_display_monitor_dbus_get_closest_mode (CcDisplayMonitorDBus *self,
+                                          CcDisplayModeDBus *mode)
+{
+  CcDisplayModeDBus *best = NULL;
+  GList *l;
+
+  for (l = self->modes; l != NULL; l = l->next)
+    {
+      CcDisplayModeDBus *similar = l->data;
+
+      if (similar->width != mode->width ||
+          similar->height != mode->height)
+        continue;
+
+      if (similar->refresh_rate == mode->refresh_rate)
+        {
+          best = similar;
+          break;
+        }
+
+      /* There might be a better heuristic. */
+      if (!best || best->refresh_rate < similar->refresh_rate)
+        {
+          best = similar;
+          continue;
+        }
+    }
+
+  return CC_DISPLAY_MODE (best);
+}
+
+static void
+cc_display_monitor_dbus_set_mode (CcDisplayMonitor *pself,
+                                  CcDisplayMode *mode)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  g_return_if_fail (mode != NULL);
+
+  self->current_mode =
+    cc_display_monitor_dbus_get_closest_mode (self, CC_DISPLAY_MODE_DBUS (mode));
+
+  /* See comment in ensure_gapless() for why we disregard the
+     existing layout here. */
+  cc_display_config_dbus_make_linear (self->config);
+}
+
+static void
+cc_display_monitor_dbus_set_position (CcDisplayMonitor *pself,
+                                      int x, int y)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
+
+  if (self->logical_monitor)
+    {
+      self->logical_monitor->x = x;
+      self->logical_monitor->y = y;
+    }
+}
+
+static void
+cc_display_monitor_dbus_init (CcDisplayMonitorDBus *self)
+{
+  self->underscanning = UNDERSCANNING_UNSUPPORTED;
+  self->max_width = G_MAXINT;
+  self->max_height = G_MAXINT;
+}
+
+static void
+cc_display_monitor_dbus_finalize (GObject *object)
+{
+  CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (object);
+
+  g_free (self->connector_name);
+  g_free (self->vendor_name);
+  g_free (self->product_name);
+  g_free (self->product_serial);
+  g_free (self->display_name);
+
+  g_list_foreach (self->modes, (GFunc) g_object_unref, NULL);
+  g_clear_pointer (&self->modes, g_list_free);
+
+  if (self->logical_monitor)
+    {
+      g_hash_table_remove (self->logical_monitor->monitors, self);
+      g_object_unref (self->logical_monitor);
+    }
+
+  G_OBJECT_CLASS (cc_display_monitor_dbus_parent_class)->finalize (object);
+}
+
+static void
+cc_display_monitor_dbus_class_init (CcDisplayMonitorDBusClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  CcDisplayMonitorClass *parent_class = CC_DISPLAY_MONITOR_CLASS (klass);
+
+  gobject_class->finalize = cc_display_monitor_dbus_finalize;
+
+  parent_class->get_display_name = cc_display_monitor_dbus_get_display_name;
+  parent_class->get_connector_name = cc_display_monitor_dbus_get_connector_name;
+  parent_class->is_builtin = cc_display_monitor_dbus_is_builtin;
+  parent_class->is_primary = cc_display_monitor_dbus_is_primary;
+  parent_class->set_primary = cc_display_monitor_dbus_set_primary;
+  parent_class->is_active = cc_display_monitor_dbus_is_active;
+  parent_class->set_active = cc_display_monitor_dbus_set_active;
+  parent_class->get_rotation = cc_display_monitor_dbus_get_rotation;
+  parent_class->set_rotation = cc_display_monitor_dbus_set_rotation;
+  parent_class->supports_rotation = cc_display_monitor_dbus_supports_rotation;
+  parent_class->get_physical_size = cc_display_monitor_dbus_get_physical_size;
+  parent_class->get_geometry = cc_display_monitor_dbus_get_geometry;
+  parent_class->get_mode = cc_display_monitor_dbus_get_mode;
+  parent_class->get_preferred_mode = cc_display_monitor_dbus_get_preferred_mode;
+  parent_class->get_id = cc_display_monitor_dbus_get_id;
+  parent_class->get_modes = cc_display_monitor_dbus_get_modes;
+  parent_class->supports_underscanning = cc_display_monitor_dbus_supports_underscanning;
+  parent_class->get_underscanning = cc_display_monitor_dbus_get_underscanning;
+  parent_class->set_underscanning = cc_display_monitor_dbus_set_underscanning;
+  parent_class->set_mode = cc_display_monitor_dbus_set_mode;
+  parent_class->set_position = cc_display_monitor_dbus_set_position;
+}
+
+static void
+construct_modes (CcDisplayMonitorDBus *self,
+                 GVariantIter *modes)
+{
+  CcDisplayModeDBus *mode;
+  GVariant *variant;
+
+  while (g_variant_iter_next (modes, "@"MODE_FORMAT, &variant))
+    {
+      mode = cc_display_mode_dbus_new (variant);
+      self->modes = g_list_prepend (self->modes, mode);
+
+      if (mode->flags & MODE_PREFERRED)
+        self->preferred_mode = CC_DISPLAY_MODE (mode);
+      if (mode->flags & MODE_CURRENT)
+        self->current_mode = CC_DISPLAY_MODE (mode);
+
+      g_variant_unref (variant);
+    }
+}
+
+static CcDisplayMonitorDBus *
+cc_display_monitor_dbus_new (GVariant *variant,
+                             CcDisplayConfigDBus *config)
+{
+  CcDisplayMonitorDBus *self = g_object_new (CC_TYPE_DISPLAY_MONITOR_DBUS, NULL);
+  gchar *s1, *s2, *s3, *s4;
+  GVariantIter *modes;
+  GVariantIter *props;
+  const char *s;
+  GVariant *v;
+
+  self->config = config;
+
+  g_variant_get (variant, MONITOR_FORMAT,
+                 &s1, &s2, &s3, &s4, &modes, &props);
+  self->connector_name = s1;
+  self->vendor_name = s2;
+  self->product_name = s3;
+  self->product_serial = s4;
+
+  construct_modes (self, modes);
+
+  while (g_variant_iter_next (props, "{&sv}", &s, &v))
+    {
+      if (g_str_equal (s, "width-mm"))
+        {
+          g_variant_get (v, "i", &self->width_mm);
+        }
+      else if (g_str_equal (s, "height-mm"))
+        {
+          g_variant_get (v, "i", &self->height_mm);
+        }
+      else if (g_str_equal (s, "is-underscanning"))
+        {
+          gboolean underscanning = FALSE;
+          g_variant_get (v, "b", &underscanning);
+          if (underscanning)
+            self->underscanning = UNDERSCANNING_ENABLED;
+          else
+            self->underscanning = UNDERSCANNING_DISABLED;
+        }
+      else if (g_str_equal (s, "max-screen-size"))
+        {
+          g_variant_get (v, "ii", &self->max_width, &self->max_height);
+        }
+      else if (g_str_equal (s, "is-builtin"))
+        {
+          g_variant_get (v, "b", &self->builtin);
+        }
+      else if (g_str_equal (s, "display-name"))
+        {
+          g_variant_get (v, "s", &self->display_name);
+        }
+
+      g_variant_unref (v);
+    }
+
+  g_variant_iter_free (modes);
+  g_variant_iter_free (props);
+
+  return self;
+}
+
+
+typedef enum _CcDisplayLayoutMode
+{
+  CC_DISPLAY_LAYOUT_MODE_LOGICAL = 1,
+  CC_DISPLAY_LAYOUT_MODE_PHYSICAL = 2
+} CcDisplayLayoutMode;
+
+typedef enum _CcDisplayConfigMethod
+{
+  CC_DISPLAY_CONFIG_METHOD_VERIFY = 0,
+  CC_DISPLAY_CONFIG_METHOD_TEMPORARY = 1,
+  CC_DISPLAY_CONFIG_METHOD_PERSISTENT = 2
+} CcDisplayConfigMethod;
+
+struct _CcDisplayConfigDBus
+{
+  CcDisplayConfig parent_instance;
+
+  GVariant *state;
+  GDBusConnection *connection;
+
+  guint32 serial;
+  gboolean supports_mirroring;
+  gboolean supports_changing_layout_mode;
+  CcDisplayLayoutMode layout_mode;
+
+  GArray *supported_scales;
+
+  GList *monitors;
+  CcDisplayMonitorDBus *primary;
+
+  GHashTable *logical_monitors;
+
+  GList *clone_modes;
+};
+
+G_DEFINE_TYPE (CcDisplayConfigDBus,
+               cc_display_config_dbus,
+               CC_TYPE_DISPLAY_CONFIG)
+
+enum
+{
+  PROP_0,
+  PROP_STATE,
+  PROP_CONNECTION,
+};
+
+static GList *
+cc_display_config_dbus_get_monitors (CcDisplayConfig *pself)
+{
+  CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
+
+  return self->monitors;
+}
+
+static GVariant *
+build_monitors_variant (GHashTable *monitors)
+{
+  GVariantBuilder builder;
+  GHashTableIter iter;
+  CcDisplayMonitorDBus *monitor;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+  g_hash_table_iter_init (&iter, monitors);
+
+  while (g_hash_table_iter_next (&iter, (void **) &monitor, NULL))
+    {
+      int w, h;
+      GVariantBuilder props_builder;
+
+      if (!monitor->current_mode)
+        continue;
+
+      g_variant_builder_init (&props_builder, G_VARIANT_TYPE ("a{sv}"));
+      g_variant_builder_add (&props_builder, "{sv}",
+                             "underscanning",
+                             g_variant_new_boolean (monitor->underscanning == UNDERSCANNING_ENABLED));
+
+      cc_display_mode_get_resolution (monitor->current_mode, &w, &h);
+      g_variant_builder_add (&builder, "(s(iid)@*)",
+                             monitor->connector_name,
+                             w, h,
+                             cc_display_mode_get_freq_f (monitor->current_mode),
+                             g_variant_builder_end (&props_builder));
+    }
+
+  return g_variant_builder_end (&builder);
+}
+
+static GVariant *
+build_logical_monitors_parameter (CcDisplayConfigDBus *self)
+{
+  GVariantBuilder builder;
+  GHashTableIter iter;
+  CcDisplayLogicalMonitor *logical_monitor;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+  g_hash_table_iter_init (&iter, self->logical_monitors);
+
+  while (g_hash_table_iter_next (&iter, (void **) &logical_monitor, NULL))
+    g_variant_builder_add (&builder, "(iidub@*)",
+                           logical_monitor->x,
+                           logical_monitor->y,
+                           logical_monitor->scale,
+                           logical_monitor->rotation,
+                           logical_monitor->primary,
+                           build_monitors_variant (logical_monitor->monitors));
+
+  return g_variant_builder_end (&builder);
+}
+
+static GVariant *
+build_apply_parameters (CcDisplayConfigDBus *self,
+                        CcDisplayConfigMethod method)
+{
+  GVariantBuilder props_builder;
+  g_variant_builder_init (&props_builder, G_VARIANT_TYPE ("a{sv}"));
+
+  if (self->supports_changing_layout_mode)
+    g_variant_builder_add (&props_builder, "{sv}",
+                           "layout-mode", g_variant_new_uint32 (self->layout_mode));
+
+  return g_variant_new ("(uu@*@*)",
+                        self->serial,
+                        method,
+                        build_logical_monitors_parameter (self),
+                        g_variant_builder_end (&props_builder));
+}
+
+static gboolean
+config_apply (CcDisplayConfigDBus *self,
+              CcDisplayConfigMethod method,
+              GError **error)
+{
+  GVariant *retval;
+
+  cc_display_config_dbus_ensure_non_offset_coords (self);
+
+  retval = g_dbus_connection_call_sync (self->connection,
+                                        "org.gnome.Mutter.DisplayConfig",
+                                        "/org/gnome/Mutter/DisplayConfig",
+                                        "org.gnome.Mutter.DisplayConfig",
+                                        "ApplyMonitorsConfig",
+                                        build_apply_parameters (self, method),
+                                        NULL,
+                                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+                                        -1,
+                                        NULL,
+                                        error);
+  if (!retval)
+    return FALSE;
+
+  g_variant_unref (retval);
+  return TRUE;
+}
+
+static gboolean
+cc_display_config_dbus_is_applicable (CcDisplayConfig *pself)
+{
+  CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
+  GError *error = NULL;
+
+  if (!config_apply (self, CC_DISPLAY_CONFIG_METHOD_VERIFY, &error))
+    {
+      g_warning ("Config not applicable: %s", error->message);
+      g_error_free (error);
+      return FALSE;
+    }
+  else
+    {
+      return TRUE;
+    }
+}
+
+static CcDisplayMonitorDBus *
+monitor_from_spec (CcDisplayConfigDBus *self,
+                   const gchar *connector,
+                   const gchar *vendor,
+                   const gchar *product,
+                   const gchar *serial)
+{
+  GList *l;
+  for (l = self->monitors; l != NULL; l = l->next)
+    {
+      CcDisplayMonitorDBus *m = l->data;
+      if (g_str_equal (m->connector_name, connector) &&
+          g_str_equal (m->vendor_name, vendor) &&
+          g_str_equal (m->product_name, product) &&
+          g_str_equal (m->product_serial, serial))
+        return m;
+    }
+  return NULL;
+}
+
+static gboolean
+cc_display_config_dbus_equal (CcDisplayConfig *pself,
+                              CcDisplayConfig *pother)
+{
+  CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
+  CcDisplayConfigDBus *other = CC_DISPLAY_CONFIG_DBUS (pother);
+  GList *l;
+
+  for (l = self->monitors; l != NULL; l = l->next)
+    {
+      CcDisplayMonitorDBus *m1 = l->data;
+      CcDisplayMonitorDBus *m2 = monitor_from_spec (other,
+                                                    m1->connector_name,
+                                                    m1->vendor_name,
+                                                    m1->product_name,
+                                                    m1->product_serial);
+      if (!m2)
+        return FALSE;
+
+      if (m1->underscanning != m2->underscanning)
+        return FALSE;
+
+      if (!cc_display_mode_dbus_equal (CC_DISPLAY_MODE_DBUS (m1->current_mode),
+                                       CC_DISPLAY_MODE_DBUS (m2->current_mode)))
+        return FALSE;
+
+      if (!cc_display_logical_monitor_equal (m1->logical_monitor, m2->logical_monitor))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+cc_display_config_dbus_set_primary (CcDisplayConfigDBus *self,
+                                    CcDisplayMonitorDBus *new_primary)
+{
+  if (self->primary == new_primary)
+    return;
+
+  if (!new_primary->logical_monitor)
+    return;
+
+  if (self->primary && self->primary->logical_monitor)
+    self->primary->logical_monitor->primary = FALSE;
+
+  self->primary = new_primary;
+  self->primary->logical_monitor->primary = TRUE;
+}
+
+static void
+cc_display_config_dbus_unset_primary (CcDisplayConfigDBus *self,
+                                      CcDisplayMonitorDBus *old_primary)
+{
+  GList *l;
+
+  if (self->primary != old_primary)
+    return;
+
+  for (l = self->monitors; l != NULL; l = l->next)
+    {
+      CcDisplayMonitorDBus *monitor = l->data;
+      if (monitor->logical_monitor &&
+          monitor != old_primary)
+        {
+          cc_display_config_dbus_set_primary (self, monitor);
+          break;
+        }
+    }
+
+  if (self->primary == old_primary)
+    self->primary = NULL;
+}
+
+static gboolean
+cc_display_config_dbus_is_cloning (CcDisplayConfig *pself)
+{
+  CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
+  guint n_active_monitors = 0;
+  GList *l;
+
+  for (l = self->monitors; l != NULL; l = l->next)
+    if (cc_display_monitor_is_active (CC_DISPLAY_MONITOR (l->data)))
+      n_active_monitors += 1;
+
+  return n_active_monitors > 1 && g_hash_table_size (self->logical_monitors) == 1;
+}
+
+static void
+cc_display_config_dbus_set_cloning (CcDisplayConfig *pself,
+                                    gboolean clone)
+{
+  CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
+  gboolean is_cloning = cc_display_config_is_cloning (pself);
+  CcDisplayLogicalMonitor *logical_monitor;
+  GList *l;
+
+  if (clone && !is_cloning)
+    {
+      logical_monitor = g_object_new (CC_TYPE_DISPLAY_LOGICAL_MONITOR, NULL);
+      for (l = self->monitors; l != NULL; l = l->next)
+        cc_display_monitor_dbus_set_logical_monitor (CC_DISPLAY_MONITOR_DBUS (l->data),
+                                                     logical_monitor);
+      register_logical_monitor (self, logical_monitor);
+    }
+  else if (!clone && is_cloning)
+    {
+      for (l = self->monitors; l != NULL; l = l->next)
+        {
+          logical_monitor = g_object_new (CC_TYPE_DISPLAY_LOGICAL_MONITOR, NULL);
+          cc_display_monitor_dbus_set_logical_monitor (CC_DISPLAY_MONITOR_DBUS (l->data),
+                                                       logical_monitor);
+          register_logical_monitor (self, logical_monitor);
+        }
+      cc_display_config_dbus_make_linear (self);
+    }
+}
+
+static GList *
+cc_display_config_dbus_get_cloning_modes (CcDisplayConfig *pself)
+{
+  CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
+
+  return self->clone_modes;
+}
+
+static gboolean
+cc_display_config_dbus_apply (CcDisplayConfig *pself,
+                              GError **error)
+{
+  CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
+
+  return config_apply (self, CC_DISPLAY_CONFIG_METHOD_PERSISTENT, error);
+}
+
+static void
+cc_display_config_dbus_init (CcDisplayConfigDBus *self)
+{
+  self->serial = 0;
+  self->supports_mirroring = TRUE;
+  self->supports_changing_layout_mode = FALSE;
+  self->layout_mode = CC_DISPLAY_LAYOUT_MODE_LOGICAL;
+  self->supported_scales = g_array_new (FALSE, FALSE, sizeof (double));
+  self->logical_monitors = g_hash_table_new (NULL, NULL);
+}
+
+static void
+gather_clone_modes (CcDisplayConfigDBus *self)
+{
+  guint n_monitors = g_list_length (self->monitors);
+  CcDisplayMonitorDBus *monitor;
+  GList *l;
+
+  if (n_monitors < 2)
+    return;
+
+  monitor = self->monitors->data;
+  for (l = monitor->modes; l != NULL; l = l->next)
+    {
+      CcDisplayModeDBus *mode = l->data;
+      gboolean valid = TRUE;
+      GList *ll;
+      for (ll = self->monitors->next; ll != NULL; ll = ll->next)
+        {
+          CcDisplayMonitorDBus *other_monitor = ll->data;
+          if (!cc_display_monitor_dbus_get_closest_mode (other_monitor, mode))
+            {
+              valid = FALSE;
+              break;
+            }
+        }
+      if (valid)
+        self->clone_modes = g_list_prepend (self->clone_modes, mode);
+    }
+}
+
+static void
+remove_logical_monitor (gpointer data,
+                        GObject *object)
+{
+  CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (data);
+
+  g_hash_table_remove (self->logical_monitors, object);
+}
+
+static void
+register_logical_monitor (CcDisplayConfigDBus *self,
+                          CcDisplayLogicalMonitor *logical_monitor)
+{
+  g_hash_table_add (self->logical_monitors, logical_monitor);
+  g_object_weak_ref (G_OBJECT (logical_monitor), remove_logical_monitor, self);
+  g_object_unref (logical_monitor);
+}
+
+static void
+construct_monitors (CcDisplayConfigDBus *self,
+                    GVariantIter *monitors,
+                    GVariantIter *logical_monitors)
+{
+  GVariant *variant;
+
+  while (g_variant_iter_next (monitors, "@"MONITOR_FORMAT, &variant))
+    {
+      CcDisplayMonitorDBus *monitor;
+
+      monitor = cc_display_monitor_dbus_new (variant, self);
+      self->monitors = g_list_prepend (self->monitors, monitor);
+
+      g_variant_unref (variant);
+    }
+
+  while (g_variant_iter_next (logical_monitors, "@"LOGICAL_MONITOR_FORMAT, &variant))
+    {
+      CcDisplayLogicalMonitor *logical_monitor;
+      GVariantIter *monitor_specs;
+      const gchar *s1, *s2, *s3, *s4;
+
+      logical_monitor = g_object_new (CC_TYPE_DISPLAY_LOGICAL_MONITOR, NULL);
+      g_variant_get (variant, LOGICAL_MONITOR_FORMAT,
+                     &logical_monitor->x,
+                     &logical_monitor->y,
+                     &logical_monitor->scale,
+                     &logical_monitor->rotation,
+                     &logical_monitor->primary,
+                     &monitor_specs,
+                     NULL);
+
+      while (g_variant_iter_next (monitor_specs, "(&s&s&s&s)", &s1, &s2, &s3, &s4))
+        {
+          CcDisplayMonitorDBus *m = monitor_from_spec (self, s1, s2, s3, s4);
+          if (!m)
+            {
+              g_warning ("Couldn't find monitor given spec: %s, %s, %s, %s",
+                         s1, s2, s3, s4);
+              continue;
+            }
+
+          cc_display_monitor_dbus_set_logical_monitor (m, logical_monitor);
+        }
+
+      if (g_hash_table_size (logical_monitor->monitors) > 0)
+        {
+          if (logical_monitor->primary)
+            {
+              GHashTableIter iter;
+              g_hash_table_iter_init (&iter, logical_monitor->monitors);
+              g_hash_table_iter_next (&iter, (void **) &self->primary, NULL);
+            }
+        }
+      else
+        {
+          g_warning ("Got an empty logical monitor, ignoring");
+        }
+
+      register_logical_monitor (self, logical_monitor);
+
+      g_variant_iter_free (monitor_specs);
+      g_variant_unref (variant);
+    }
+
+  gather_clone_modes (self);
+}
+
+static void
+cc_display_config_dbus_constructed (GObject *object)
+{
+  CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (object);
+  GVariantIter *monitors;
+  GVariantIter *logical_monitors;
+  GVariantIter *scales;
+  GVariantIter *props;
+  double d;
+  const char *s;
+  GVariant *v;
+
+  g_variant_get (self->state,
+                 CURRENT_STATE_FORMAT,
+                 &self->serial,
+                 &monitors,
+                 &logical_monitors,
+                 &scales,
+                 &props);
+
+  while (g_variant_iter_next (scales, "d", &d))
+    g_array_append_val (self->supported_scales, d);
+
+  while (g_variant_iter_next (props, "{&sv}", &s, &v))
+    {
+      if (g_str_equal (s, "supports-mirroring"))
+        {
+          g_variant_get (v, "b", &self->supports_mirroring);
+        }
+      else if (g_str_equal (s, "supports-changing-layout-mode"))
+        {
+          g_variant_get (v, "b", &self->supports_changing_layout_mode);
+        }
+      else if (g_str_equal (s, "layout-mode"))
+        {
+          guint32 u = 0;
+          g_variant_get (v, "u", &u);
+          if (u >= CC_DISPLAY_LAYOUT_MODE_LOGICAL &&
+              u <= CC_DISPLAY_LAYOUT_MODE_PHYSICAL)
+            self->layout_mode = u;
+        }
+
+      g_variant_unref (v);
+    }
+
+  construct_monitors (self, monitors, logical_monitors);
+
+  g_variant_iter_free (monitors);
+  g_variant_iter_free (logical_monitors);
+  g_variant_iter_free (scales);
+  g_variant_iter_free (props);
+
+  G_OBJECT_CLASS (cc_display_config_dbus_parent_class)->constructed (object);
+}
+
+static void
+cc_display_config_dbus_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+  CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (object);
+
+  switch (prop_id)
+    {
+    case PROP_STATE:
+      self->state = g_value_dup_variant (value);
+      break;
+    case PROP_CONNECTION:
+      self->connection = g_value_dup_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+cc_display_config_dbus_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (object);
+
+  switch (prop_id)
+    {
+    case PROP_STATE:
+      g_value_set_variant (value, self->state);
+      break;
+    case PROP_CONNECTION:
+      g_value_set_object (value, self->connection);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+cc_display_config_dbus_finalize (GObject *object)
+{
+  CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (object);
+
+  g_clear_pointer (&self->state, g_variant_unref);
+  g_clear_object (&self->connection);
+
+  g_array_free (self->supported_scales, TRUE);
+  g_list_foreach (self->monitors, (GFunc) g_object_unref, NULL);
+  g_clear_pointer (&self->monitors, g_list_free);
+  g_clear_pointer (&self->logical_monitors, g_hash_table_destroy);
+  g_clear_pointer (&self->clone_modes, g_list_free);
+
+  G_OBJECT_CLASS (cc_display_config_dbus_parent_class)->finalize (object);
+}
+
+static void
+cc_display_config_dbus_class_init (CcDisplayConfigDBusClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  CcDisplayConfigClass *parent_class = CC_DISPLAY_CONFIG_CLASS (klass);
+  GParamSpec *pspec;
+
+  gobject_class->constructed = cc_display_config_dbus_constructed;
+  gobject_class->set_property = cc_display_config_dbus_set_property;
+  gobject_class->get_property = cc_display_config_dbus_get_property;
+  gobject_class->finalize = cc_display_config_dbus_finalize;
+
+  parent_class->get_monitors = cc_display_config_dbus_get_monitors;
+  parent_class->is_applicable = cc_display_config_dbus_is_applicable;
+  parent_class->equal = cc_display_config_dbus_equal;
+  parent_class->apply = cc_display_config_dbus_apply;
+  parent_class->is_cloning = cc_display_config_dbus_is_cloning;
+  parent_class->set_cloning = cc_display_config_dbus_set_cloning;
+  parent_class->get_cloning_modes = cc_display_config_dbus_get_cloning_modes;
+
+  pspec = g_param_spec_variant ("state",
+                                "GVariant",
+                                "GVariant",
+                                G_VARIANT_TYPE (CURRENT_STATE_FORMAT),
+                                NULL,
+                                G_PARAM_READWRITE |
+                                G_PARAM_STATIC_STRINGS |
+                                G_PARAM_CONSTRUCT_ONLY);
+  g_object_class_install_property (gobject_class, PROP_STATE, pspec);
+
+  pspec = g_param_spec_object ("connection",
+                               "GDBusConnection",
+                               "GDBusConnection",
+                                G_TYPE_DBUS_CONNECTION,
+                                G_PARAM_READWRITE |
+                                G_PARAM_STATIC_STRINGS |
+                                G_PARAM_CONSTRUCT_ONLY);
+  g_object_class_install_property (gobject_class, PROP_CONNECTION, pspec);
+}
+
+static gint
+sort_x_axis (gconstpointer a, gconstpointer b)
+{
+  const CcDisplayLogicalMonitor *ma = a;
+  const CcDisplayLogicalMonitor *mb = b;
+  return ma->x - mb->x;
+}
+
+static gint
+sort_y_axis (gconstpointer a, gconstpointer b)
+{
+  const CcDisplayLogicalMonitor *ma = a;
+  const CcDisplayLogicalMonitor *mb = b;
+  return ma->y - mb->y;
+}
+
+static void
+add_x_delta (gpointer d1, gpointer d2)
+{
+  CcDisplayLogicalMonitor *m = d1;
+  int delta = GPOINTER_TO_INT (d2);
+  m->x += delta;
+}
+
+static int
+logical_monitor_width (CcDisplayLogicalMonitor *lm)
+{
+  CcDisplayMonitorDBus *monitor;
+  CcDisplayModeDBus *mode;
+  GHashTableIter iter;
+  g_hash_table_iter_init (&iter, lm->monitors);
+  g_hash_table_iter_next (&iter, (void **) &monitor, NULL);
+  mode = CC_DISPLAY_MODE_DBUS (monitor->current_mode);
+  return mode ? mode->width : 0;
+}
+
+static void
+add_y_delta (gpointer d1, gpointer d2)
+{
+  CcDisplayLogicalMonitor *m = d1;
+  int delta = GPOINTER_TO_INT (d2);
+  m->y += delta;
+}
+
+static int
+logical_monitor_height (CcDisplayLogicalMonitor *lm)
+{
+  CcDisplayMonitorDBus *monitor;
+  CcDisplayModeDBus *mode;
+  GHashTableIter iter;
+  g_hash_table_iter_init (&iter, lm->monitors);
+  g_hash_table_iter_next (&iter, (void **) &monitor, NULL);
+  mode = CC_DISPLAY_MODE_DBUS (monitor->current_mode);
+  return mode ? mode->height : 0;
+}
+
+static void
+cc_display_config_dbus_ensure_non_offset_coords (CcDisplayConfigDBus *self)
+{
+  GList *x_axis, *y_axis;
+  CcDisplayLogicalMonitor *m;
+
+  if (g_hash_table_size (self->logical_monitors) == 0)
+    return;
+
+  x_axis = g_hash_table_get_keys (self->logical_monitors);
+  x_axis = g_list_sort (x_axis, sort_x_axis);
+  y_axis = g_hash_table_get_keys (self->logical_monitors);
+  y_axis = g_list_sort (y_axis, sort_y_axis);
+
+  m = x_axis->data;
+  if (m->x != 0)
+    g_list_foreach (x_axis, add_x_delta, GINT_TO_POINTER (- m->x));
+
+  m = y_axis->data;
+  if (m->y != 0)
+    g_list_foreach (y_axis, add_y_delta, GINT_TO_POINTER (- m->y));
+
+  g_list_free (x_axis);
+  g_list_free (y_axis);
+}
+
+static void
+cc_display_config_dbus_append_right (CcDisplayConfigDBus *self,
+                                     CcDisplayLogicalMonitor *monitor)
+{
+  GList *x_axis;
+  CcDisplayLogicalMonitor *last;
+
+  if (g_hash_table_size (self->logical_monitors) == 0)
+    {
+      monitor->x = 0;
+      return;
+    }
+
+  x_axis = g_hash_table_get_keys (self->logical_monitors);
+  x_axis = g_list_sort (x_axis, sort_x_axis);
+  last = g_list_last (x_axis)->data;
+  monitor->x = last->x + logical_monitor_width (last);
+
+  g_list_free (x_axis);
+}
+
+static void
+cc_display_config_dbus_ensure_gapless (CcDisplayConfigDBus *self)
+{
+  GList *x_axis, *y_axis, *l;
+
+  if (g_hash_table_size (self->logical_monitors) == 0)
+    return;
+
+  x_axis = g_hash_table_get_keys (self->logical_monitors);
+  x_axis = g_list_sort (x_axis, sort_x_axis);
+  y_axis = g_hash_table_get_keys (self->logical_monitors);
+  y_axis = g_list_sort (y_axis, sort_y_axis);
+
+  /* This might produce overlaps which will fail validation.
+     Unfortunately, automating this to avoid gaps and overlaps with
+     arbitrary rectangles is a hard problem and we'd probably not find
+     the layout the user wants anyway. We'll need a panel re-design
+     that allows manual layout adjustments after monitor operations
+     and before applying. */
+  for (l = x_axis; l != NULL && l->next != NULL; l = l->next)
+    {
+      CcDisplayLogicalMonitor *m = l->data;
+      CcDisplayLogicalMonitor *n = l->next->data;
+      int mx2 = m->x + logical_monitor_width (m);
+
+      if (n->x > mx2)
+        n->x = mx2;
+    }
+
+  for (l = y_axis; l != NULL && l->next != NULL; l = l->next)
+    {
+      CcDisplayLogicalMonitor *m = l->data;
+      CcDisplayLogicalMonitor *n = l->next->data;
+      int my2 = m->y + logical_monitor_height (m);
+
+      if (n->y > my2)
+        n->y = my2;
+    }
+
+  g_list_free (x_axis);
+  g_list_free (y_axis);
+}
+
+static void
+cc_display_config_dbus_make_linear (CcDisplayConfigDBus *self)
+{
+  CcDisplayLogicalMonitor *primary;
+  GList *logical_monitors, *l;
+  int x;
+
+  if (self->primary && self->primary->logical_monitor)
+    {
+      primary = self->primary->logical_monitor;
+      primary->x = primary->y = 0;
+      x = logical_monitor_width (primary);
+    }
+  else
+    {
+      primary = NULL;
+      x = 0;
+    }
+
+  logical_monitors = g_hash_table_get_keys (self->logical_monitors);
+  for (l = logical_monitors; l != NULL; l = l->next)
+    {
+      CcDisplayLogicalMonitor *m = l->data;
+
+      if (m == primary)
+        continue;
+
+      m->x = x;
+      m->y = 0;
+      x += logical_monitor_width (m);
+    }
+
+  g_list_free (logical_monitors);
+}
diff --git a/panels/display/cc-display-config-dbus.h b/panels/display/cc-display-config-dbus.h
new file mode 100644
index 0000000..9809652
--- /dev/null
+++ b/panels/display/cc-display-config-dbus.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017  Red Hat, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef _CC_DISPLAY_CONFIG_DBUS_H
+#define _CC_DISPLAY_CONFIG_DBUS_H
+
+#include <glib-object.h>
+
+#include "cc-display-config.h"
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_DISPLAY_MODE_DBUS (cc_display_mode_dbus_get_type ())
+G_DECLARE_FINAL_TYPE (CcDisplayModeDBus, cc_display_mode_dbus,
+                      CC, DISPLAY_MODE_DBUS, CcDisplayMode)
+
+#define CC_TYPE_DISPLAY_MONITOR_DBUS (cc_display_monitor_dbus_get_type ())
+G_DECLARE_FINAL_TYPE (CcDisplayMonitorDBus, cc_display_monitor_dbus,
+                      CC, DISPLAY_MONITOR_DBUS, CcDisplayMonitor)
+
+#define CC_TYPE_DISPLAY_CONFIG_DBUS (cc_display_config_dbus_get_type ())
+G_DECLARE_FINAL_TYPE (CcDisplayConfigDBus, cc_display_config_dbus,
+                      CC, DISPLAY_CONFIG_DBUS, CcDisplayConfig)
+
+G_END_DECLS
+
+#endif /* _CC_DISPLAY_CONFIG_DBUS_H */
diff --git a/panels/display/cc-display-config-manager-dbus.c b/panels/display/cc-display-config-manager-dbus.c
new file mode 100644
index 0000000..3ec65aa
--- /dev/null
+++ b/panels/display/cc-display-config-manager-dbus.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2017  Red Hat, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include "cc-display-config-dbus.h"
+#include "cc-display-config-manager-dbus.h"
+
+#include <gio/gio.h>
+
+struct _CcDisplayConfigManagerDBus
+{
+  CcDisplayConfigManager parent_instance;
+
+  GCancellable *cancellable;
+  GDBusConnection *connection;
+  guint monitors_changed_id;
+
+  GVariant *current_state;
+};
+
+G_DEFINE_TYPE (CcDisplayConfigManagerDBus,
+               cc_display_config_manager_dbus,
+               CC_TYPE_DISPLAY_CONFIG_MANAGER)
+
+static CcDisplayConfig *
+cc_display_config_manager_dbus_get_current (CcDisplayConfigManager *pself)
+{
+  CcDisplayConfigManagerDBus *self = CC_DISPLAY_CONFIG_MANAGER_DBUS (pself);
+
+  if (!self->current_state)
+    return NULL;
+
+  return g_object_new (CC_TYPE_DISPLAY_CONFIG_DBUS,
+                       "state", self->current_state,
+                       "connection", self->connection, NULL);
+}
+
+static void
+got_current_state (GObject      *object,
+                   GAsyncResult *result,
+                   gpointer      data)
+{
+  CcDisplayConfigManagerDBus *self;
+  GVariant *variant;
+  GError *error = NULL;
+
+  variant = g_dbus_connection_call_finish (G_DBUS_CONNECTION (object),
+                                           result, &error);
+  if (!variant)
+    {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        {
+          _cc_display_config_manager_emit_changed (CC_DISPLAY_CONFIG_MANAGER (data));
+          g_warning ("Error calling GetCurrentState: %s", error->message);
+        }
+      g_clear_error (&error);
+      return;
+    }
+
+  self = CC_DISPLAY_CONFIG_MANAGER_DBUS (data);
+  g_clear_pointer (&self->current_state, g_variant_unref);
+  self->current_state = variant;
+
+  _cc_display_config_manager_emit_changed (CC_DISPLAY_CONFIG_MANAGER (self));
+}
+
+static void
+get_current_state (CcDisplayConfigManagerDBus *self)
+{
+  g_dbus_connection_call (self->connection,
+                          "org.gnome.Mutter.DisplayConfig",
+                          "/org/gnome/Mutter/DisplayConfig",
+                          "org.gnome.Mutter.DisplayConfig",
+                          "GetCurrentState",
+                          NULL,
+                          NULL,
+                          G_DBUS_CALL_FLAGS_NO_AUTO_START,
+                          -1,
+                          self->cancellable,
+                          got_current_state,
+                          self);
+}
+
+static void
+monitors_changed (GDBusConnection *connection,
+                  const gchar     *sender_name,
+                  const gchar     *object_path,
+                  const gchar     *interface_name,
+                  const gchar     *signal_name,
+                  GVariant        *parameters,
+                  gpointer         data)
+{
+  CcDisplayConfigManagerDBus *self = CC_DISPLAY_CONFIG_MANAGER_DBUS (data);
+  get_current_state (self);
+}
+
+static void
+bus_gotten (GObject      *object,
+            GAsyncResult *result,
+            gpointer      data)
+{
+  CcDisplayConfigManagerDBus *self;
+  GDBusConnection *connection;
+  GError *error = NULL;
+
+  connection = g_bus_get_finish (result, &error);
+  if (!connection)
+    {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        {
+          _cc_display_config_manager_emit_changed (CC_DISPLAY_CONFIG_MANAGER (data));
+          g_warning ("Error obtaining DBus connection: %s", error->message);
+        }
+      g_clear_error (&error);
+      return;
+    }
+
+  self = CC_DISPLAY_CONFIG_MANAGER_DBUS (data);
+  self->connection = connection;
+  self->monitors_changed_id =
+    g_dbus_connection_signal_subscribe (self->connection,
+                                        "org.gnome.Mutter.DisplayConfig",
+                                        "org.gnome.Mutter.DisplayConfig",
+                                        "MonitorsChanged",
+                                        "/org/gnome/Mutter/DisplayConfig",
+                                        NULL,
+                                        G_DBUS_SIGNAL_FLAGS_NONE,
+                                        monitors_changed,
+                                        self,
+                                        NULL);
+  get_current_state (self);
+}
+
+static void
+cc_display_config_manager_dbus_init (CcDisplayConfigManagerDBus *self)
+{
+  self->cancellable = g_cancellable_new ();
+  g_bus_get (G_BUS_TYPE_SESSION, self->cancellable, bus_gotten, self);
+}
+
+static void
+cc_display_config_manager_dbus_finalize (GObject *object)
+{
+  CcDisplayConfigManagerDBus *self = CC_DISPLAY_CONFIG_MANAGER_DBUS (object);
+
+  g_cancellable_cancel (self->cancellable);
+  g_clear_object (&self->cancellable);
+
+  if (self->monitors_changed_id && self->connection)
+    g_dbus_connection_signal_unsubscribe (self->connection,
+                                          self->monitors_changed_id);
+  g_clear_object (&self->connection);
+  g_clear_pointer (&self->current_state, g_variant_unref);
+
+  G_OBJECT_CLASS (cc_display_config_manager_dbus_parent_class)->finalize (object);
+}
+
+static void
+cc_display_config_manager_dbus_class_init (CcDisplayConfigManagerDBusClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  CcDisplayConfigManagerClass *parent_class = CC_DISPLAY_CONFIG_MANAGER_CLASS (klass);
+
+  gobject_class->finalize = cc_display_config_manager_dbus_finalize;
+
+  parent_class->get_current = cc_display_config_manager_dbus_get_current;
+}
+
+CcDisplayConfigManager *
+cc_display_config_manager_dbus_new (void)
+{
+  return g_object_new (CC_TYPE_DISPLAY_CONFIG_MANAGER_DBUS, NULL);
+}
diff --git a/panels/display/cc-display-config-manager-dbus.h b/panels/display/cc-display-config-manager-dbus.h
new file mode 100644
index 0000000..a9b85e6
--- /dev/null
+++ b/panels/display/cc-display-config-manager-dbus.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017  Red Hat, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef _CC_DISPLAY_CONFIG_MANAGER_DBUS_H
+#define _CC_DISPLAY_CONFIG_MANAGER_DBUS_H
+
+#include <glib-object.h>
+
+#include "cc-display-config-manager.h"
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_DISPLAY_CONFIG_MANAGER_DBUS (cc_display_config_manager_dbus_get_type ())
+G_DECLARE_FINAL_TYPE (CcDisplayConfigManagerDBus, cc_display_config_manager_dbus,
+                      CC, DISPLAY_CONFIG_MANAGER_DBUS, CcDisplayConfigManager)
+
+CcDisplayConfigManager * cc_display_config_manager_dbus_new (void);
+
+G_END_DECLS
+
+#endif /* _CC_DISPLAY_CONFIG_MANAGER_DBUS_H */
diff --git a/panels/display/cc-display-panel.c b/panels/display/cc-display-panel.c
index 3c9b26b..19d4e30 100644
--- a/panels/display/cc-display-panel.c
+++ b/panels/display/cc-display-panel.c
@@ -521,6 +521,7 @@ on_screen_changed (CcDisplayPanel *panel)
     {
       CcDisplayMonitor *output = l->data;
 
+      /* Don't expose non-primary tile monitors in _get_monitors() */
       /* if (!gnome_rr_output_info_is_primary_tile (outputs[i])) */
       /*   continue; */
 


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