[gnome-flashback] backends: add GfMonitorsConfig



commit b68ab446d64973f01bbd55ac340cdf552cc482fc
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Wed Sep 20 18:31:08 2017 +0300

    backends: add GfMonitorsConfig

 backends/Makefile.am                  |    4 +
 backends/gf-monitor-manager-private.h |   18 +-
 backends/gf-monitor-manager.c         |   10 +
 backends/gf-monitors-config-private.h |   73 ++++++++
 backends/gf-monitors-config.c         |  294 +++++++++++++++++++++++++++++++++
 backends/gf-rectangle-private.h       |   37 ++++
 backends/gf-rectangle.c               |   77 +++++++++
 7 files changed, 505 insertions(+), 8 deletions(-)
---
diff --git a/backends/Makefile.am b/backends/Makefile.am
index 1eeb37b..1c3bea5 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -59,10 +59,14 @@ libbackends_la_SOURCES = \
        gf-monitor-tiled-private.h \
        gf-monitor-tiled.c \
        gf-monitor.c \
+       gf-monitors-config-private.h \
+       gf-monitors-config.c \
        gf-orientation-manager-private.h \
        gf-orientation-manager.c \
        gf-output-private.h \
        gf-output.c \
+       gf-rectangle-private.h \
+       gf-rectangle.c \
        gf-rectangle.h \
        gf-settings-private.h \
        gf-settings.c \
diff --git a/backends/gf-monitor-manager-private.h b/backends/gf-monitor-manager-private.h
index d56db1d..7228286 100644
--- a/backends/gf-monitor-manager-private.h
+++ b/backends/gf-monitor-manager-private.h
@@ -155,18 +155,20 @@ typedef struct
   GfLogicalMonitorLayoutMode   (* get_default_layout_mode)      (GfMonitorManager            *manager);
 } GfMonitorManagerClass;
 
-GType      gf_monitor_manager_get_type              (void);
+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_monitor_from_spec (GfMonitorManager *manager,
-                                                     GfMonitorSpec    *monitor_spec);
+GfMonitor                  *gf_monitor_manager_get_monitor_from_spec (GfMonitorManager *manager,
+                                                                      GfMonitorSpec    *monitor_spec);
 
-void       gf_monitor_manager_tiled_monitor_added   (GfMonitorManager *manager,
-                                                     GfMonitor        *monitor);
+void                        gf_monitor_manager_tiled_monitor_added   (GfMonitorManager *manager,
+                                                                      GfMonitor        *monitor);
 
-void       gf_monitor_manager_tiled_monitor_removed (GfMonitorManager *manager,
-                                                     GfMonitor        *monitor);
+void                        gf_monitor_manager_tiled_monitor_removed (GfMonitorManager *manager,
+                                                                      GfMonitor        *monitor);
+
+GfMonitorManagerCapability  gf_monitor_manager_get_capabilities      (GfMonitorManager *manager);
 
 static inline gboolean
 gf_monitor_transform_is_rotated (GfMonitorTransform transform)
diff --git a/backends/gf-monitor-manager.c b/backends/gf-monitor-manager.c
index 902a982..ba7b808 100644
--- a/backends/gf-monitor-manager.c
+++ b/backends/gf-monitor-manager.c
@@ -370,3 +370,13 @@ gf_monitor_manager_tiled_monitor_removed (GfMonitorManager *manager,
   if (manager_class->tiled_monitor_removed)
     manager_class->tiled_monitor_removed (manager, monitor);
 }
+
+GfMonitorManagerCapability
+gf_monitor_manager_get_capabilities (GfMonitorManager *manager)
+{
+  GfMonitorManagerClass *manager_class;
+
+  manager_class = GF_MONITOR_MANAGER_GET_CLASS (manager);
+
+  return manager_class->get_capabilities (manager);
+}
diff --git a/backends/gf-monitors-config-private.h b/backends/gf-monitors-config-private.h
new file mode 100644
index 0000000..065378d
--- /dev/null
+++ b/backends/gf-monitors-config-private.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 Red Hat
+ * Copyright (C) 2017 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Adapted from mutter:
+ * - src/backends/meta-monitor-config-manager.h
+ */
+
+#ifndef GF_MONITORS_CONFIG_PRIVATE_H
+#define GF_MONITORS_CONFIG_PRIVATE_H
+
+#include "gf-monitor-manager-private.h"
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+  GList *monitor_specs;
+} GfMonitorsConfigKey;
+
+typedef enum
+{
+  GF_MONITORS_CONFIG_FLAG_NONE = 0,
+  GF_MONITORS_CONFIG_FLAG_MIGRATED = (1 << 0),
+} GfMonitorsConfigFlag;
+
+struct _GfMonitorsConfig
+{
+  GObject                     parent;
+
+  GfMonitorsConfigKey        *key;
+  GList                      *logical_monitor_configs;
+
+  GfMonitorsConfigFlag        flags;
+
+  GfLogicalMonitorLayoutMode  layout_mode;
+};
+
+#define GF_TYPE_MONITORS_CONFIG (gf_monitors_config_get_type ())
+G_DECLARE_FINAL_TYPE (GfMonitorsConfig, gf_monitors_config,
+                      GF, MONITORS_CONFIG, GObject)
+
+GfMonitorsConfig *gf_monitors_config_new       (GList                       *logical_monitor_configs,
+                                                GfLogicalMonitorLayoutMode   layout_mode,
+                                                GfMonitorsConfigFlag         flags);
+
+guint             gf_monitors_config_key_hash  (gconstpointer                data);
+
+gboolean          gf_monitors_config_key_equal (gconstpointer                data_a,
+                                                gconstpointer                data_b);
+
+void              gf_monitors_config_key_free  (GfMonitorsConfigKey         *config_key);
+
+gboolean          gf_verify_monitors_config    (GfMonitorsConfig            *config,
+                                                GfMonitorManager            *monitor_manager,
+                                                GError                     **error);
+
+G_END_DECLS
+
+#endif
diff --git a/backends/gf-monitors-config.c b/backends/gf-monitors-config.c
new file mode 100644
index 0000000..b88393f
--- /dev/null
+++ b/backends/gf-monitors-config.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2016 Red Hat
+ * Copyright (C) 2017 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Adapted from mutter:
+ * - src/backends/meta-monitor-config-manager.c
+ */
+
+#include "config.h"
+#include "gf-logical-monitor-config-private.h"
+#include "gf-monitor-config-private.h"
+#include "gf-monitor-spec-private.h"
+#include "gf-monitors-config-private.h"
+#include "gf-rectangle-private.h"
+
+G_DEFINE_TYPE (GfMonitorsConfig, gf_monitors_config, G_TYPE_OBJECT)
+
+static gboolean
+has_adjecent_neighbour (GfMonitorsConfig       *config,
+                        GfLogicalMonitorConfig *logical_monitor_config)
+{
+  GList *l;
+
+  if (!config->logical_monitor_configs->next)
+    {
+      g_assert (config->logical_monitor_configs->data == logical_monitor_config);
+      return TRUE;
+    }
+
+  for (l = config->logical_monitor_configs; l; l = l->next)
+    {
+      GfLogicalMonitorConfig *other_logical_monitor_config = l->data;
+
+      if (logical_monitor_config == other_logical_monitor_config)
+        continue;
+
+      if (gf_rectangle_is_adjecent_to (&logical_monitor_config->layout,
+                                       &other_logical_monitor_config->layout))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static GfMonitorsConfigKey *
+gf_monitors_config_key_new (GList *logical_monitor_configs)
+{
+  GfMonitorsConfigKey *config_key;
+  GList *monitor_specs;
+  GList *l;
+
+  monitor_specs = NULL;
+  for (l = 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)
+        {
+          GfMonitorConfig *monitor_config = k->data;
+          GfMonitorSpec *monitor_spec;
+
+          monitor_spec = gf_monitor_spec_clone (monitor_config->monitor_spec);
+          monitor_specs = g_list_prepend (monitor_specs, monitor_spec);
+        }
+    }
+
+  monitor_specs = g_list_sort (monitor_specs, (GCompareFunc) gf_monitor_spec_compare);
+
+  config_key = g_new0 (GfMonitorsConfigKey, 1);
+  config_key->monitor_specs = monitor_specs;
+
+  return config_key;
+}
+
+static void
+gf_monitors_config_finalize (GObject *object)
+{
+  GfMonitorsConfig *config;
+
+  config = GF_MONITORS_CONFIG (object);
+
+  gf_monitors_config_key_free (config->key);
+  g_list_free_full (config->logical_monitor_configs,
+                    (GDestroyNotify) gf_logical_monitor_config_free);
+
+  G_OBJECT_CLASS (gf_monitors_config_parent_class)->finalize (object);
+}
+
+static void
+gf_monitors_config_class_init (GfMonitorsConfigClass *config_class)
+{
+  GObjectClass *object_class;
+
+  object_class = G_OBJECT_CLASS (config_class);
+
+  object_class->finalize = gf_monitors_config_finalize;
+}
+
+static void
+gf_monitors_config_init (GfMonitorsConfig *config)
+{
+}
+
+GfMonitorsConfig *
+gf_monitors_config_new (GList                      *logical_monitor_configs,
+                        GfLogicalMonitorLayoutMode  layout_mode,
+                        GfMonitorsConfigFlag        flags)
+{
+  GfMonitorsConfig *config;
+
+  config = g_object_new (GF_TYPE_MONITORS_CONFIG, NULL);
+  config->logical_monitor_configs = logical_monitor_configs;
+  config->key = gf_monitors_config_key_new (logical_monitor_configs);
+  config->layout_mode = layout_mode;
+  config->flags = flags;
+
+  return config;
+}
+
+guint
+gf_monitors_config_key_hash (gconstpointer data)
+{
+  const GfMonitorsConfigKey *config_key;
+  glong hash;
+  GList *l;
+
+  config_key = data;
+  hash = 0;
+
+  for (l = config_key->monitor_specs; l; l = l->next)
+    {
+      GfMonitorSpec *monitor_spec = l->data;
+
+      hash ^= (g_str_hash (monitor_spec->connector) ^
+               g_str_hash (monitor_spec->vendor) ^
+               g_str_hash (monitor_spec->product) ^
+               g_str_hash (monitor_spec->serial));
+    }
+
+  return hash;
+}
+
+gboolean
+gf_monitors_config_key_equal (gconstpointer data_a,
+                              gconstpointer data_b)
+{
+  const GfMonitorsConfigKey *config_key_a;
+  const GfMonitorsConfigKey *config_key_b;
+  GList *l_a, *l_b;
+
+  config_key_a = data_a;
+  config_key_b = data_b;
+
+  for (l_a = config_key_a->monitor_specs, l_b = config_key_b->monitor_specs;
+       l_a && l_b;
+       l_a = l_a->next, l_b = l_b->next)
+    {
+      GfMonitorSpec *monitor_spec_a = l_a->data;
+      GfMonitorSpec *monitor_spec_b = l_b->data;
+
+      if (!gf_monitor_spec_equals (monitor_spec_a, monitor_spec_b))
+        return FALSE;
+    }
+
+  if (l_a || l_b)
+    return FALSE;
+
+  return TRUE;
+}
+
+void
+gf_monitors_config_key_free (GfMonitorsConfigKey *config_key)
+{
+  g_list_free_full (config_key->monitor_specs, (GDestroyNotify) gf_monitor_spec_free);
+  g_free (config_key);
+}
+
+gboolean
+gf_verify_monitors_config (GfMonitorsConfig  *config,
+                           GfMonitorManager  *monitor_manager,
+                           GError           **error)
+{
+  gint min_x, min_y;
+  gboolean has_primary;
+  GList *region;
+  GList *l;
+  gboolean global_scale_required;
+
+  if (!config->logical_monitor_configs)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Monitors config incomplete");
+
+      return FALSE;
+    }
+
+  global_scale_required = !!(gf_monitor_manager_get_capabilities (monitor_manager) &
+                             GF_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED);
+
+  min_x = INT_MAX;
+  min_y = INT_MAX;
+  region = NULL;
+  has_primary = FALSE;
+  for (l = config->logical_monitor_configs; l; l = l->next)
+    {
+      GfLogicalMonitorConfig *logical_monitor_config = l->data;
+
+      if (global_scale_required)
+        {
+          GfLogicalMonitorConfig *prev_logical_monitor_config;
+
+          prev_logical_monitor_config = l->prev ? l->prev->data : NULL;
+
+          if (prev_logical_monitor_config &&
+              (prev_logical_monitor_config->scale !=
+               logical_monitor_config->scale))
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Logical monitor scales must be identical");
+
+              return FALSE;
+            }
+        }
+
+      if (gf_rectangle_overlaps_with_region (region, &logical_monitor_config->layout))
+        {
+          g_list_free (region);
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Logical monitors overlap");
+
+          return FALSE;
+        }
+
+      if (has_primary && logical_monitor_config->is_primary)
+        {
+          g_list_free (region);
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Config contains multiple primary logical monitors");
+
+          return FALSE;
+        }
+      else if (logical_monitor_config->is_primary)
+        {
+          has_primary = TRUE;
+        }
+
+      if (!has_adjecent_neighbour (config, logical_monitor_config))
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Logical monitors not adjecent");
+
+          return FALSE;
+        }
+
+      min_x = MIN (logical_monitor_config->layout.x, min_x);
+      min_y = MIN (logical_monitor_config->layout.y, min_y);
+
+      region = g_list_prepend (region, &logical_monitor_config->layout);
+    }
+
+  g_list_free (region);
+
+  if (min_x != 0 || min_y != 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Logical monitors positions are offset");
+
+      return FALSE;
+    }
+
+  if (!has_primary)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Config is missing primary logical");
+
+      return FALSE;
+    }
+
+  return TRUE;
+}
diff --git a/backends/gf-rectangle-private.h b/backends/gf-rectangle-private.h
new file mode 100644
index 0000000..490261a
--- /dev/null
+++ b/backends/gf-rectangle-private.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2005, 2006 Elijah Newren
+ * Copyright (C) 2017 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Adapted from mutter:
+ * - src/core/boxes-private.h
+ */
+
+#ifndef GF_RECTANGLE_PRIVATE_H
+#define GF_RECTANGLE_PRIVATE_H
+
+#include "gf-rectangle.h"
+
+G_BEGIN_DECLS
+
+gboolean gf_rectangle_overlaps_with_region (const GList       *spanning_rects,
+                                            const GfRectangle *rect);
+
+gboolean gf_rectangle_is_adjecent_to       (const GfRectangle *rect,
+                                            const GfRectangle *other);
+
+G_END_DECLS
+
+#endif
diff --git a/backends/gf-rectangle.c b/backends/gf-rectangle.c
new file mode 100644
index 0000000..bddd481
--- /dev/null
+++ b/backends/gf-rectangle.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2005, 2006 Elijah Newren
+ * Copyright (C) 2017 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Adapted from mutter:
+ * - src/core/boxes.c
+ */
+
+#include "config.h"
+#include "gf-rectangle-private.h"
+
+static gboolean
+gf_rectangle_overlap (const GfRectangle *rect1,
+                      const GfRectangle *rect2)
+{
+  g_return_val_if_fail (rect1 != NULL, FALSE);
+  g_return_val_if_fail (rect2 != NULL, FALSE);
+
+  return !((rect1->x + rect1->width  <= rect2->x) ||
+           (rect2->x + rect2->width  <= rect1->x) ||
+           (rect1->y + rect1->height <= rect2->y) ||
+           (rect2->y + rect2->height <= rect1->y));
+}
+
+gboolean
+gf_rectangle_overlaps_with_region (const GList       *spanning_rects,
+                                   const GfRectangle *rect)
+{
+  const GList *temp;
+  gboolean overlaps;
+
+  temp = spanning_rects;
+  overlaps = FALSE;
+  while (!overlaps && temp != NULL)
+    {
+      overlaps = overlaps || gf_rectangle_overlap (temp->data, rect);
+      temp = temp->next;
+    }
+
+  return overlaps;
+}
+
+gboolean
+gf_rectangle_is_adjecent_to (const GfRectangle *rect,
+                             const GfRectangle *other)
+{
+  gint rect_x1 = rect->x;
+  gint rect_y1 = rect->y;
+  gint rect_x2 = rect->x + rect->width;
+  gint rect_y2 = rect->y + rect->height;
+  gint other_x1 = other->x;
+  gint other_y1 = other->y;
+  gint other_x2 = other->x + other->width;
+  gint other_y2 = other->y + other->height;
+
+  if ((rect_x1 == other_x2 || rect_x2 == other_x1) &&
+      !(rect_y2 <= other_y1 || rect_y1 >= other_y2))
+    return TRUE;
+  else if ((rect_y1 == other_y2 || rect_y2 == other_y1) &&
+           !(rect_x2 <= other_x1 || rect_x1 >= other_x2))
+    return TRUE;
+  else
+    return FALSE;
+}


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