[mutter] Introduce MetaMonitorConfigStore



commit 1ad386bc285682e64390bd6d519d3aac35f2b41b
Author: Jonas Ådahl <jadahl gmail com>
Date:   Thu Jan 12 16:13:48 2017 +0800

    Introduce MetaMonitorConfigStore
    
    MetaMonitorConfigStore provides an XML storage mechanism for
    MetaMonitorConfigManager. It stores configuration files defined in the
    same level as the MetaMonitorsConfig format, i.e. refers to high level
    "monitors" and "monitor modes" instead of connectors and CRTCs.
    
    Only reading custom files are implemented and so far unused.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=777732

 src/Makefile.am                             |    2 +
 src/backends/meta-monitor-config-manager.c  |  108 +++-
 src/backends/meta-monitor-config-manager.h  |   19 +
 src/backends/meta-monitor-config-store.c    |  957 +++++++++++++++++++++++++++
 src/backends/meta-monitor-config-store.h    |   45 ++
 src/backends/meta-monitor-manager-private.h |    1 +
 src/backends/meta-monitor.c                 |   21 +
 src/backends/meta-monitor.h                 |    3 +
 8 files changed, 1151 insertions(+), 5 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index fdccbe6..3dbe731 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -107,6 +107,8 @@ libmutter_la_SOURCES =                              \
        backends/meta-monitor-config.h          \
        backends/meta-monitor-config-manager.c  \
        backends/meta-monitor-config-manager.h  \
+       backends/meta-monitor-config-store.c    \
+       backends/meta-monitor-config-store.h    \
        backends/meta-monitor.c                 \
        backends/meta-monitor.h                 \
        backends/meta-monitor-private.h         \
diff --git a/src/backends/meta-monitor-config-manager.c b/src/backends/meta-monitor-config-manager.c
index 07a3636..4540aaa 100644
--- a/src/backends/meta-monitor-config-manager.c
+++ b/src/backends/meta-monitor-config-manager.c
@@ -23,6 +23,7 @@
 
 #include "backends/meta-monitor-config-manager.h"
 
+#include "backends/meta-monitor-config-store.h"
 #include "backends/meta-monitor-manager-private.h"
 #include "core/boxes-private.h"
 
@@ -32,6 +33,8 @@ struct _MetaMonitorConfigManager
 
   MetaMonitorManager *monitor_manager;
 
+  MetaMonitorConfigStore *config_store;
+
   MetaMonitorsConfig *current_config;
 };
 
@@ -41,9 +44,6 @@ G_DEFINE_TYPE (MetaMonitorConfigManager, meta_monitor_config_manager,
 G_DEFINE_TYPE (MetaMonitorsConfig, meta_monitors_config,
                G_TYPE_OBJECT)
 
-static void
-meta_logical_monitor_config_free (MetaLogicalMonitorConfig *logical_monitor_config);
-
 MetaMonitorConfigManager *
 meta_monitor_config_manager_new (MetaMonitorManager *monitor_manager)
 {
@@ -51,10 +51,18 @@ meta_monitor_config_manager_new (MetaMonitorManager *monitor_manager)
 
   config_manager = g_object_new (META_TYPE_MONITOR_CONFIG_MANAGER, NULL);
   config_manager->monitor_manager = monitor_manager;
+  config_manager->config_store = g_object_new (META_TYPE_MONITOR_CONFIG_STORE,
+                                               NULL);
 
   return config_manager;
 }
 
+MetaMonitorConfigStore *
+meta_monitor_config_manager_get_store (MetaMonitorConfigManager *config_manager)
+{
+  return config_manager->config_store;
+}
+
 static gboolean
 is_crtc_assigned (MetaCrtc  *crtc,
                   GPtrArray *crtc_infos)
@@ -593,7 +601,7 @@ meta_monitor_config_manager_class_init (MetaMonitorConfigManagerClass *klass)
   object_class->dispose = meta_monitor_config_manager_dispose;
 }
 
-static void
+void
 meta_monitor_config_free (MetaMonitorConfig *monitor_config)
 {
   meta_monitor_spec_free (monitor_config->monitor_spec);
@@ -601,7 +609,7 @@ meta_monitor_config_free (MetaMonitorConfig *monitor_config)
   g_free (monitor_config);
 }
 
-static void
+void
 meta_logical_monitor_config_free (MetaLogicalMonitorConfig *logical_monitor_config)
 {
   g_list_free_full (logical_monitor_config->monitor_configs,
@@ -609,6 +617,94 @@ meta_logical_monitor_config_free (MetaLogicalMonitorConfig *logical_monitor_conf
   g_free (logical_monitor_config);
 }
 
+static MetaMonitorsConfigKey *
+meta_monitors_config_key_new (GList *logical_monitor_configs)
+{
+  MetaMonitorsConfigKey *config_key;
+  GList *monitor_specs;
+  GList *l;
+
+  monitor_specs = NULL;
+  for (l = logical_monitor_configs; l; l = l->next)
+    {
+      MetaLogicalMonitorConfig *logical_monitor_config = l->data;
+      GList *k;
+
+      for (k = logical_monitor_config->monitor_configs; k; k = k->next)
+        {
+          MetaMonitorConfig *monitor_config = k->data;
+          MetaMonitorSpec *monitor_spec;
+
+          monitor_spec = meta_monitor_spec_clone (monitor_config->monitor_spec);
+          monitor_specs = g_list_prepend (monitor_specs, monitor_spec);
+        }
+    }
+
+  monitor_specs = g_list_sort (monitor_specs,
+                               (GCompareFunc) meta_monitor_spec_compare);
+
+  config_key = g_new0 (MetaMonitorsConfigKey, 1);
+  *config_key = (MetaMonitorsConfigKey) {
+    .monitor_specs = monitor_specs
+  };
+
+  return config_key;
+}
+
+void
+meta_monitors_config_key_free (MetaMonitorsConfigKey *config_key)
+{
+  g_list_free_full (config_key->monitor_specs,
+                    (GDestroyNotify) meta_monitor_spec_free);
+  g_free (config_key);
+}
+
+unsigned int
+meta_monitors_config_key_hash (gconstpointer data)
+{
+  const MetaMonitorsConfigKey *config_key = data;
+  GList *l;
+  unsigned long hash;
+
+  hash = 0;
+  for (l = config_key->monitor_specs; l; l = l->next)
+    {
+      MetaMonitorSpec *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
+meta_monitors_config_key_equal (gconstpointer data_a,
+                                gconstpointer data_b)
+{
+  const MetaMonitorsConfigKey *config_key_a = data_a;
+  const MetaMonitorsConfigKey *config_key_b = data_b;
+  GList *l_a, *l_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)
+    {
+      MetaMonitorSpec *monitor_spec_a = l_a->data;
+      MetaMonitorSpec *monitor_spec_b = l_b->data;
+
+      if (!meta_monitor_spec_equals (monitor_spec_a, monitor_spec_b))
+        return FALSE;
+    }
+
+  if (l_b || l_b)
+    return FALSE;
+
+  return TRUE;
+}
+
 MetaMonitorsConfig *
 meta_monitors_config_new (GList *logical_monitor_configs)
 {
@@ -616,6 +712,7 @@ meta_monitors_config_new (GList *logical_monitor_configs)
 
   config = g_object_new (META_TYPE_MONITORS_CONFIG, NULL);
   config->logical_monitor_configs = logical_monitor_configs;
+  config->key = meta_monitors_config_key_new (logical_monitor_configs);
 
   return config;
 }
@@ -625,6 +722,7 @@ meta_monitors_config_finalize (GObject *object)
 {
   MetaMonitorsConfig *config = META_MONITORS_CONFIG (object);
 
+  meta_monitors_config_key_free (config->key);
   g_list_free_full (config->logical_monitor_configs,
                     (GDestroyNotify) meta_logical_monitor_config_free);
 }
diff --git a/src/backends/meta-monitor-config-manager.h b/src/backends/meta-monitor-config-manager.h
index 9740636..d10cc8f 100644
--- a/src/backends/meta-monitor-config-manager.h
+++ b/src/backends/meta-monitor-config-manager.h
@@ -43,10 +43,16 @@ typedef struct _MetaLogicalMonitorConfig
   gboolean is_presentation;
 } MetaLogicalMonitorConfig;
 
+typedef struct _MetaMonitorsConfigKey
+{
+  GList *monitor_specs;
+} MetaMonitorsConfigKey;
+
 struct _MetaMonitorsConfig
 {
   GObject parent;
 
+  MetaMonitorsConfigKey *key;
   GList *logical_monitor_configs;
 };
 
@@ -56,6 +62,8 @@ G_DECLARE_FINAL_TYPE (MetaMonitorsConfig, meta_monitors_config,
 
 MetaMonitorConfigManager * meta_monitor_config_manager_new (MetaMonitorManager *monitor_manager);
 
+MetaMonitorConfigStore * meta_monitor_config_manager_get_store (MetaMonitorConfigManager *config_manager);
+
 gboolean meta_monitor_config_manager_assign (MetaMonitorManager *manager,
                                              MetaMonitorsConfig *config,
                                              GPtrArray         **crtc_infos,
@@ -75,4 +83,15 @@ MetaMonitorsConfig * meta_monitor_config_manager_get_current (MetaMonitorConfigM
 
 MetaMonitorsConfig * meta_monitors_config_new (GList *logical_monitor_configs);
 
+unsigned int meta_monitors_config_key_hash (gconstpointer config_key);
+
+gboolean meta_monitors_config_key_equal (gconstpointer config_key_a,
+                                         gconstpointer config_key_b);
+
+void meta_monitors_config_key_free (MetaMonitorsConfigKey *config_key);
+
+void meta_logical_monitor_config_free (MetaLogicalMonitorConfig *logical_monitor_config);
+
+void meta_monitor_config_free (MetaMonitorConfig *monitor_config);
+
 #endif /* META_MONITOR_CONFIG_MANAGER_H */
diff --git a/src/backends/meta-monitor-config-store.c b/src/backends/meta-monitor-config-store.c
new file mode 100644
index 0000000..eaf70f2
--- /dev/null
+++ b/src/backends/meta-monitor-config-store.c
@@ -0,0 +1,957 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2017 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "backends/meta-monitor-config-store.h"
+
+#include <gio/gio.h>
+#include <string.h>
+
+#include "backends/meta-monitor-config-manager.h"
+#include "core/boxes-private.h"
+
+/*
+ * Example configuration:
+ *
+ * <monitors version="2">
+ *   <configuration>
+ *     <logicalmonitor>
+ *       <x>0</x>
+ *       <y>0</y>
+ *       <monitor>
+ *         <monitorspec>
+ *           <connector>LVDS1</connector>
+ *           <vendor>Vendor A</vendor>
+ *           <product>Product A</product>
+ *           <serial>Serial A</serial>
+ *         </monitorspec>
+ *         <mode>
+ *           <width>1920</width>
+ *           <height>1080</height>
+ *           <rate>60.049972534179688</rate>
+ *         </mode>
+ *       </monitor>
+ *       <primary>yes</primary>
+ *       <presentation>no</presentation>
+ *     </logicalmonitor>
+ *     <logicalmonitor>
+ *       <x>1920</x>
+ *       <y>1080</y>
+ *       <monitor>
+ *         <monitorspec>
+ *           <connector>LVDS2</connector>
+ *           <vendor>Vendor B</vendor>
+ *           <product>Product B</product>
+ *           <serial>Serial B</serial>
+ *         </monitorspec>
+ *         <mode>
+ *           <width>1920</width>
+ *           <height>1080</height>
+ *           <rate>60.049972534179688</rate>
+ *         </mode>
+ *       </monitor>
+ *       <presentation>yes</presentation>
+ *     </logicalmonitor>
+ *   </configuration>
+ * </monitors>
+ *
+ */
+
+struct _MetaMonitorConfigStore
+{
+  GObject parent;
+
+  GHashTable *configs;
+};
+
+typedef enum
+{
+  STATE_INITIAL,
+  STATE_MONITORS,
+  STATE_CONFIGURATION,
+  STATE_LOGICAL_MONITOR,
+  STATE_LOGICAL_MONITOR_X,
+  STATE_LOGICAL_MONITOR_Y,
+  STATE_LOGICAL_MONITOR_PRIMARY,
+  STATE_LOGICAL_MONITOR_PRESENTATION,
+  STATE_MONITOR,
+  STATE_MONITOR_SPEC,
+  STATE_MONITOR_SPEC_CONNECTOR,
+  STATE_MONITOR_SPEC_VENDOR,
+  STATE_MONITOR_SPEC_PRODUCT,
+  STATE_MONITOR_SPEC_SERIAL,
+  STATE_MONITOR_MODE,
+  STATE_MONITOR_MODE_WIDTH,
+  STATE_MONITOR_MODE_HEIGHT,
+  STATE_MONITOR_MODE_RATE,
+} ParserState;
+
+typedef struct
+{
+  ParserState state;
+  MetaMonitorConfigStore *config_store;
+
+  GList *current_logical_monitor_configs;
+  MetaMonitorSpec *current_monitor_spec;
+  MetaMonitorModeSpec *current_monitor_mode_spec;
+  MetaMonitorConfig *current_monitor_config;
+  MetaLogicalMonitorConfig *current_logical_monitor_config;
+} ConfigParser;
+
+G_DEFINE_TYPE (MetaMonitorConfigStore, meta_monitor_config_store,
+               G_TYPE_OBJECT)
+
+static void
+handle_start_element (GMarkupParseContext  *context,
+                      const char           *element_name,
+                      const char          **attribute_names,
+                      const char          **attribute_values,
+                      gpointer              user_data,
+                      GError              **error)
+{
+  ConfigParser *parser = user_data;
+
+  switch (parser->state)
+    {
+    case STATE_INITIAL:
+      {
+        char *version;
+
+        if (!g_str_equal (element_name, "monitors"))
+          {
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                         "Invalid document element '%s'", element_name);
+            return;
+          }
+
+        if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values,
+                                          error,
+                                          G_MARKUP_COLLECT_STRING, "version", &version,
+                                          G_MARKUP_COLLECT_INVALID))
+          {
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                         "Missing config file format version");
+          }
+        
+        /* TODO: Handle converting version 1 configuration files. */
+
+        if (!g_str_equal (version, "2"))
+          {
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                         "Invalid or unsupported version '%s'", version);
+            return;
+          }
+
+        parser->state = STATE_MONITORS;
+        return;
+      }
+
+    case STATE_MONITORS:
+      {
+        if (!g_str_equal (element_name, "configuration"))
+          {
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                         "Invalid toplevel element '%s'", element_name);
+            return;
+          }
+
+        parser->state = STATE_CONFIGURATION;
+        return;
+      }
+
+    case STATE_CONFIGURATION:
+      {
+        if (!g_str_equal (element_name, "logicalmonitor"))
+          {
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                         "Invalid configuration element '%s'", element_name);
+            return;
+          }
+
+        parser->current_logical_monitor_config =
+          g_new0 (MetaLogicalMonitorConfig, 1);
+
+        parser->state = STATE_LOGICAL_MONITOR;
+        return;
+      }
+
+    case STATE_LOGICAL_MONITOR:
+      {
+        if (g_str_equal (element_name, "x"))
+          {
+            parser->state = STATE_LOGICAL_MONITOR_X;
+          }
+        else if (g_str_equal (element_name, "y"))
+          {
+            parser->state = STATE_LOGICAL_MONITOR_Y;
+          }
+        else if (g_str_equal (element_name, "primary"))
+          {
+            parser->state = STATE_LOGICAL_MONITOR_PRIMARY;
+          }
+        else if (g_str_equal (element_name, "presentation"))
+          {
+            parser->state = STATE_LOGICAL_MONITOR_PRESENTATION;
+          }
+        else if (g_str_equal (element_name, "monitor"))
+          {
+            parser->current_monitor_config = g_new0 (MetaMonitorConfig, 1);;
+
+            parser->state = STATE_MONITOR;
+          }
+        else
+          {
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                         "Invalid monitor logicalmonitor element '%s'", element_name);
+            return;
+          }
+
+        return;
+      }
+
+    case STATE_LOGICAL_MONITOR_X:
+    case STATE_LOGICAL_MONITOR_Y:
+    case STATE_LOGICAL_MONITOR_PRIMARY:
+    case STATE_LOGICAL_MONITOR_PRESENTATION:
+      {
+        g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                     "Invalid logical monitor element '%s'", element_name);
+        return;
+      }
+
+    case STATE_MONITOR:
+      {
+        if (g_str_equal (element_name, "monitorspec"))
+          {
+            parser->current_monitor_spec = g_new0 (MetaMonitorSpec, 1);
+
+            parser->state = STATE_MONITOR_SPEC;
+          }
+        else if (g_str_equal (element_name, "mode"))
+          {
+            parser->current_monitor_mode_spec = g_new0 (MetaMonitorModeSpec, 1);
+
+            parser->state = STATE_MONITOR_MODE;
+          }
+        else
+          {
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                         "Invalid monitor element '%s'", element_name);
+            return;
+          }
+
+        return;
+      }
+
+    case STATE_MONITOR_SPEC:
+      {
+        if (g_str_equal (element_name, "connector"))
+          {
+            parser->state = STATE_MONITOR_SPEC_CONNECTOR;
+          }
+        else if (g_str_equal (element_name, "vendor"))
+          {
+            parser->state = STATE_MONITOR_SPEC_VENDOR;
+          }
+        else if (g_str_equal (element_name, "product"))
+          {
+            parser->state = STATE_MONITOR_SPEC_PRODUCT;
+          }
+        else if (g_str_equal (element_name, "serial"))
+          {
+            parser->state = STATE_MONITOR_SPEC_SERIAL;
+          }
+        else
+          {
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                         "Invalid monitor spec element '%s'", element_name);
+            return;
+          }
+
+        return;
+      }
+
+    case STATE_MONITOR_SPEC_CONNECTOR:
+    case STATE_MONITOR_SPEC_VENDOR:
+    case STATE_MONITOR_SPEC_PRODUCT:
+    case STATE_MONITOR_SPEC_SERIAL:
+      {
+        g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                     "Invalid monitor spec element '%s'", element_name);
+        return;
+      }
+
+    case STATE_MONITOR_MODE:
+      {
+        if (g_str_equal (element_name, "width"))
+          {
+            parser->state = STATE_MONITOR_MODE_WIDTH;
+          }
+        else if (g_str_equal (element_name, "height"))
+          {
+            parser->state = STATE_MONITOR_MODE_HEIGHT;
+          }
+        else if (g_str_equal (element_name, "rate"))
+          {
+            parser->state = STATE_MONITOR_MODE_RATE;
+          }
+        else
+          {
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                         "Invalid mode element '%s'", element_name);
+            return;
+          }
+
+        return;
+      }
+
+    case STATE_MONITOR_MODE_WIDTH:
+    case STATE_MONITOR_MODE_HEIGHT:
+    case STATE_MONITOR_MODE_RATE:
+      {
+        g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                     "Invalid mode sub element '%s'", element_name);
+        return;
+      }
+    }
+}
+
+static gboolean
+verify_monitor_spec (MetaMonitorSpec *monitor_spec,
+                     GError         **error)
+{
+  if (monitor_spec->connector &&
+      monitor_spec->vendor &&
+      monitor_spec->product &&
+      monitor_spec->serial)
+    {
+      return TRUE;
+    }
+  else
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Monitor spec incomplete");
+      return FALSE;
+    }
+}
+
+static gboolean
+verify_monitor_mode (MetaMonitorModeSpec *monitor_mode_spec,
+                     GError             **error)
+{
+  if (monitor_mode_spec->width > 0 &&
+      monitor_mode_spec->height > 0 &&
+      monitor_mode_spec->refresh_rate > 0.0f)
+    {
+      return TRUE;
+    }
+  else
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Monitor mode invalid");
+      return FALSE;
+    }
+}
+
+static gboolean
+verify_monitor_config (MetaMonitorConfig *monitor_config,
+                       GError           **error)
+{
+  if (monitor_config->monitor_spec && monitor_config->mode_spec)
+    {
+      return TRUE;
+    }
+  else
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Monitor config incomplete");
+      return FALSE;
+    }
+}
+
+static gboolean
+verify_logical_monitor_config (MetaLogicalMonitorConfig *logical_monitor_config,
+                               GError                  **error)
+{
+  if (logical_monitor_config->layout.x < 0 ||
+      logical_monitor_config->layout.y < 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Invalid logical monitor position (%d, %d)",
+                   logical_monitor_config->layout.x,
+                   logical_monitor_config->layout.y);
+      return FALSE;
+    }
+
+  if (!logical_monitor_config->monitor_configs)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Logical monitor is empty");
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+derive_logical_monitor_layout (MetaLogicalMonitorConfig *logical_monitor_config,
+                               GError                  **error)
+{
+  MetaMonitorConfig *monitor_config;
+  int mode_width, mode_height;
+  GList *l;
+
+  monitor_config = logical_monitor_config->monitor_configs->data;
+  mode_width = monitor_config->mode_spec->width;
+  mode_height = monitor_config->mode_spec->height;
+
+  for (l = logical_monitor_config->monitor_configs->next; l; l = l->next)
+    {
+      monitor_config = l->data;
+
+      if (monitor_config->mode_spec->width != mode_width ||
+          monitor_config->mode_spec->height != mode_height)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Monitors in logical monitor incompatible");
+          return FALSE;
+        }
+    }
+
+  logical_monitor_config->layout.width = mode_width;
+  logical_monitor_config->layout.height = mode_height;
+
+  return TRUE;
+}
+
+static gboolean
+verify_config (MetaMonitorsConfig *config,
+               GError            **error)
+{
+  gboolean has_primary;
+  GList *region;
+  GList *l;
+
+  if (!config->logical_monitor_configs)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Monitors config incomplete");
+      return FALSE;
+    }
+
+  region = NULL;
+  has_primary = FALSE;
+  for (l = config->logical_monitor_configs; l; l = l->next)
+    {
+      MetaLogicalMonitorConfig *logical_monitor_config = l->data;
+
+      if (meta_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;
+        }
+
+      region = g_list_prepend (region, &logical_monitor_config->layout);
+    }
+
+  g_list_free (region);
+
+  if (!has_primary)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Config is missing primary logical");
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+handle_end_element (GMarkupParseContext  *context,
+                    const char           *element_name,
+                    gpointer              user_data,
+                    GError              **error)
+{
+  ConfigParser *parser = user_data;
+
+  switch (parser->state)
+    {
+    case STATE_LOGICAL_MONITOR_X:
+    case STATE_LOGICAL_MONITOR_Y:
+    case STATE_LOGICAL_MONITOR_PRIMARY:
+    case STATE_LOGICAL_MONITOR_PRESENTATION:
+      {
+        parser->state = STATE_LOGICAL_MONITOR;
+        return;
+      }
+
+    case STATE_MONITOR_SPEC_CONNECTOR:
+    case STATE_MONITOR_SPEC_VENDOR:
+    case STATE_MONITOR_SPEC_PRODUCT:
+    case STATE_MONITOR_SPEC_SERIAL:
+      {
+        parser->state = STATE_MONITOR_SPEC;
+        return;
+      }
+
+    case STATE_MONITOR_SPEC:
+      {
+        g_assert (g_str_equal (element_name, "monitorspec"));
+
+        if (!verify_monitor_spec (parser->current_monitor_spec, error))
+          return;
+
+        parser->current_monitor_config->monitor_spec =
+          parser->current_monitor_spec;
+        parser->current_monitor_spec = NULL;
+
+        parser->state = STATE_MONITOR;
+        return;
+      }
+
+    case STATE_MONITOR_MODE_WIDTH:
+    case STATE_MONITOR_MODE_HEIGHT:
+    case STATE_MONITOR_MODE_RATE:
+      {
+        parser->state = STATE_MONITOR_MODE;
+        return;
+      }
+
+    case STATE_MONITOR_MODE:
+      {
+        g_assert (g_str_equal (element_name, "mode"));
+
+        if (!verify_monitor_mode (parser->current_monitor_mode_spec, error))
+          return;
+
+        parser->current_monitor_config->mode_spec =
+          parser->current_monitor_mode_spec;
+        parser->current_monitor_mode_spec = NULL;
+
+        parser->state = STATE_MONITOR;
+        return;
+      }
+
+    case STATE_MONITOR:
+      {
+        MetaLogicalMonitorConfig *logical_monitor_config;
+
+        g_assert (g_str_equal (element_name, "monitor"));
+
+        if (!verify_monitor_config (parser->current_monitor_config, error))
+          return;
+
+        logical_monitor_config = parser->current_logical_monitor_config;
+
+        logical_monitor_config->monitor_configs =
+          g_list_append (logical_monitor_config->monitor_configs,
+                         parser->current_monitor_config);
+        parser->current_monitor_config = NULL;
+
+        parser->state = STATE_LOGICAL_MONITOR;
+        return;
+      }
+
+    case STATE_LOGICAL_MONITOR:
+      {
+        MetaLogicalMonitorConfig *logical_monitor_config =
+          parser->current_logical_monitor_config;
+
+        g_assert (g_str_equal (element_name, "logicalmonitor"));
+
+        if (!verify_logical_monitor_config (logical_monitor_config, error))
+          return;
+
+        if (!derive_logical_monitor_layout (logical_monitor_config, error))
+          return;
+
+        parser->current_logical_monitor_configs =
+          g_list_append (parser->current_logical_monitor_configs,
+                         logical_monitor_config);
+        parser->current_logical_monitor_config = NULL;
+
+        parser->state = STATE_CONFIGURATION;
+        return;
+      }
+
+    case STATE_CONFIGURATION:
+      {
+        MetaMonitorsConfig *config;
+
+        g_assert (g_str_equal (element_name, "configuration"));
+
+        config =
+          meta_monitors_config_new (parser->current_logical_monitor_configs);
+
+        if (!verify_config (config, error))
+          {
+            g_object_unref (config);
+            return;
+          }
+
+        g_hash_table_replace (parser->config_store->configs,
+                              config->key, config);
+
+        parser->current_logical_monitor_configs = NULL;
+
+        parser->state = STATE_MONITORS;
+        return;
+      }
+
+    case STATE_MONITORS:
+      {
+        g_assert (g_str_equal (element_name, "monitors"));
+
+        parser->state = STATE_INITIAL;
+        return;
+      }
+
+    case STATE_INITIAL:
+      {
+        g_assert_not_reached ();
+      }
+    }
+}
+
+static gboolean
+read_int (const char  *text,
+          gsize        text_len,
+          gint        *out_value,
+          GError     **error)
+{
+  char buf[64];
+  int64_t value;
+  char *end;
+
+  strncpy (buf, text, text_len);
+  buf[MIN (63, text_len)] = 0;
+
+  value = g_ascii_strtoll (buf, &end, 10);
+
+  if (*end || value < 0 || value > G_MAXINT16)
+    {
+      g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                   "Expected a number, got %s", buf);
+      return FALSE;
+    }
+  else
+    {
+      *out_value = value;
+      return TRUE;
+    }
+}
+
+static gboolean
+read_float (const char  *text,
+            gsize        text_len,
+            float       *out_value,
+            GError     **error)
+{
+  char buf[64];
+  float value;
+  char *end;
+
+  strncpy (buf, text, text_len);
+  buf[MIN (63, text_len)] = 0;
+
+  value = g_ascii_strtod (buf, &end);
+
+  if (*end)
+    {
+      g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                   "Expected a number, got %s", buf);
+      return FALSE;
+    }
+  else
+    {
+      *out_value = value;
+      return TRUE;
+    }
+}
+
+static gboolean
+read_bool (const char  *text,
+           gsize        text_len,
+           gboolean    *out_value,
+           GError     **error)
+{
+  if (strncmp (text, "no", text_len) == 0)
+    {
+      *out_value = FALSE;
+      return TRUE;
+    }
+  else if (strncmp (text, "yes", text_len) == 0)
+    {
+      *out_value = TRUE;
+      return TRUE;
+    }
+  else
+    {
+      g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                   "Invalid boolean value '%.*s'", (int) text_len, text);
+      return FALSE;
+    }
+}
+
+static gboolean
+is_all_whitespace (const char *text,
+                   gsize       text_len)
+{
+  gsize i;
+
+  for (i = 0; i < text_len; i++)
+    if (!g_ascii_isspace (text[i]))
+      return FALSE;
+
+  return TRUE;
+}
+
+static void
+handle_text (GMarkupParseContext *context,
+             const gchar         *text,
+             gsize                text_len,
+             gpointer             user_data,
+             GError             **error)
+{
+  ConfigParser *parser = user_data;
+
+  switch (parser->state)
+    {
+    case STATE_INITIAL:
+    case STATE_MONITORS:
+    case STATE_CONFIGURATION:
+    case STATE_LOGICAL_MONITOR:
+    case STATE_MONITOR:
+    case STATE_MONITOR_SPEC:
+    case STATE_MONITOR_MODE:
+      {
+        if (!is_all_whitespace (text, text_len))
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "Unexpected content at this point");
+        return;
+      }
+
+    case STATE_MONITOR_SPEC_CONNECTOR:
+      {
+        parser->current_monitor_spec->connector = g_strndup (text, text_len);
+        return;
+      }
+
+    case STATE_MONITOR_SPEC_VENDOR:
+      {
+        parser->current_monitor_spec->vendor = g_strndup (text, text_len);
+        return;
+      }
+
+    case STATE_MONITOR_SPEC_PRODUCT:
+      {
+        parser->current_monitor_spec->product = g_strndup (text, text_len);
+        return;
+      }
+
+    case STATE_MONITOR_SPEC_SERIAL:
+      {
+        parser->current_monitor_spec->serial = g_strndup (text, text_len);
+        return;
+      }
+
+    case STATE_LOGICAL_MONITOR_X:
+      {
+        read_int (text, text_len,
+                  &parser->current_logical_monitor_config->layout.x, error);
+        return;
+      }
+
+    case STATE_LOGICAL_MONITOR_Y:
+      {
+        read_int (text, text_len,
+                  &parser->current_logical_monitor_config->layout.y, error);
+        return;
+      }
+
+    case STATE_LOGICAL_MONITOR_PRIMARY:
+      {
+        read_bool (text, text_len,
+                   &parser->current_logical_monitor_config->is_primary,
+                   error);
+        return;
+      }
+
+    case STATE_LOGICAL_MONITOR_PRESENTATION:
+      {
+        read_bool (text, text_len,
+                   &parser->current_logical_monitor_config->is_presentation,
+                   error);
+        return;
+      }
+
+    case STATE_MONITOR_MODE_WIDTH:
+      {
+        read_int (text, text_len,
+                  &parser->current_monitor_mode_spec->width,
+                  error);
+        return;
+      }
+
+    case STATE_MONITOR_MODE_HEIGHT:
+      {
+        read_int (text, text_len,
+                  &parser->current_monitor_mode_spec->height,
+                  error);
+        return;
+      }
+
+    case STATE_MONITOR_MODE_RATE:
+      {
+        read_float (text, text_len,
+                    &parser->current_monitor_mode_spec->refresh_rate,
+                    error);
+        return;
+      }
+    }
+}
+
+static const GMarkupParser config_parser = {
+  .start_element = handle_start_element,
+  .end_element = handle_end_element,
+  .text = handle_text
+};
+
+static gboolean
+read_config_file (MetaMonitorConfigStore *config_store,
+                  GFile                  *file,
+                  GError                **error)
+{
+  char *buffer;
+  gsize size;
+  ConfigParser parser;
+  GMarkupParseContext *parse_context;
+
+  if (!g_file_load_contents (file, NULL, &buffer, &size, NULL, error))
+    return FALSE;
+
+  parser = (ConfigParser) {
+    .state = STATE_INITIAL,
+    .config_store = config_store
+  };
+
+  parse_context = g_markup_parse_context_new (&config_parser,
+                                              G_MARKUP_TREAT_CDATA_AS_TEXT |
+                                              G_MARKUP_PREFIX_ERROR_POSITION,
+                                              &parser, NULL);
+  if (!g_markup_parse_context_parse (parse_context, buffer, size, error))
+    {
+      g_list_free_full (parser.current_logical_monitor_configs,
+                        (GDestroyNotify) meta_logical_monitor_config_free);
+      g_clear_pointer (&parser.current_monitor_spec,
+                       meta_monitor_spec_free);
+      g_free (parser.current_monitor_mode_spec);
+      g_clear_pointer (&parser.current_monitor_config,
+                      meta_monitor_config_free);
+      g_clear_pointer (&parser.current_logical_monitor_config,
+                       meta_logical_monitor_config_free);
+      return FALSE;
+    }
+
+  g_markup_parse_context_free (parse_context);
+  g_free (buffer);
+
+  return TRUE;
+}
+
+MetaMonitorsConfig *
+meta_monitor_config_store_lookup (MetaMonitorConfigStore *config_store,
+                                  MetaMonitorsConfigKey  *key)
+{
+  return META_MONITORS_CONFIG (g_hash_table_lookup (config_store->configs,
+                                                    key));
+}
+
+void
+meta_monitor_config_store_add (MetaMonitorConfigStore *config_store,
+                               MetaMonitorsConfig     *config)
+{
+  g_hash_table_insert (config_store->configs,
+                       config->key, g_object_ref (config));
+}
+
+gboolean
+meta_monitor_config_store_set_custom (MetaMonitorConfigStore *config_store,
+                                      const char             *path,
+                                      GError                **error)
+{
+  g_autoptr (GFile) custom_file = NULL;
+
+  g_hash_table_remove_all (config_store->configs);
+
+  custom_file = g_file_new_for_path (path);
+
+  return read_config_file (config_store, custom_file, error);
+}
+
+int
+meta_monitor_config_store_get_config_count (MetaMonitorConfigStore *config_store)
+{
+  return (int) g_hash_table_size (config_store->configs);
+}
+
+static void
+meta_monitor_config_store_dispose (GObject *object)
+{
+  MetaMonitorConfigStore *config_store = META_MONITOR_CONFIG_STORE (object);
+
+  g_clear_pointer (&config_store->configs, g_hash_table_destroy);
+
+  G_OBJECT_CLASS (meta_monitor_config_store_parent_class)->dispose (object);
+}
+
+static void
+meta_monitor_config_store_init (MetaMonitorConfigStore *config_store)
+{
+  config_store->configs = g_hash_table_new_full (meta_monitors_config_key_hash,
+                                                 meta_monitors_config_key_equal,
+                                                 NULL,
+                                                 g_object_unref);
+}
+
+static void
+meta_monitor_config_store_class_init (MetaMonitorConfigStoreClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = meta_monitor_config_store_dispose;
+}
diff --git a/src/backends/meta-monitor-config-store.h b/src/backends/meta-monitor-config-store.h
new file mode 100644
index 0000000..3dc94db
--- /dev/null
+++ b/src/backends/meta-monitor-config-store.h
@@ -0,0 +1,45 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2017 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_MONITOR_CONFIG_STORE_H
+#define META_MONITOR_CONFIG_STORE_H
+
+#include <glib-object.h>
+
+#include "backends/meta-monitor-config-manager.h"
+
+#define META_TYPE_MONITOR_CONFIG_STORE (meta_monitor_config_store_get_type ())
+G_DECLARE_FINAL_TYPE (MetaMonitorConfigStore, meta_monitor_config_store,
+                      META, MONITOR_CONFIG_STORE, GObject)
+
+MetaMonitorsConfig * meta_monitor_config_store_lookup (MetaMonitorConfigStore *config_store,
+                                                       MetaMonitorsConfigKey  *key);
+
+void meta_monitor_config_store_add (MetaMonitorConfigStore *config_store,
+                                    MetaMonitorsConfig     *config);
+
+gboolean meta_monitor_config_store_set_custom (MetaMonitorConfigStore *config_store,
+                                               const char             *path,
+                                               GError                **error);
+
+int meta_monitor_config_store_get_config_count (MetaMonitorConfigStore *config_store);
+
+#endif /* META_MONITOR_CONFIG_STORE_H */
diff --git a/src/backends/meta-monitor-manager-private.h b/src/backends/meta-monitor-manager-private.h
index 0424d03..1c2bb26 100644
--- a/src/backends/meta-monitor-manager-private.h
+++ b/src/backends/meta-monitor-manager-private.h
@@ -50,6 +50,7 @@
 
 typedef struct _MetaMonitorConfig MetaMonitorConfig;
 typedef struct _MetaMonitorConfigManager MetaMonitorConfigManager;
+typedef struct _MetaMonitorConfigStore MetaMonitorConfigStore;
 typedef struct _MetaMonitorsConfig MetaMonitorsConfig;
 
 typedef struct _MetaMonitor MetaMonitor;
diff --git a/src/backends/meta-monitor.c b/src/backends/meta-monitor.c
index 20e524f..8238b43 100644
--- a/src/backends/meta-monitor.c
+++ b/src/backends/meta-monitor.c
@@ -103,6 +103,27 @@ meta_monitor_spec_equals (MetaMonitorSpec *monitor_spec,
           g_str_equal (monitor_spec->serial, other_monitor_spec->serial));
 }
 
+int
+meta_monitor_spec_compare (MetaMonitorSpec *monitor_spec_a,
+                           MetaMonitorSpec *monitor_spec_b)
+{
+  int ret;
+
+  ret = strcmp (monitor_spec_a->connector, monitor_spec_b->connector);
+  if (ret != 0)
+    return ret;
+
+  ret = strcmp (monitor_spec_a->vendor, monitor_spec_b->vendor);
+  if (ret != 0)
+    return ret;
+
+  ret = strcmp (monitor_spec_a->product, monitor_spec_b->product);
+  if (ret != 0)
+    return ret;
+
+  return strcmp (monitor_spec_a->serial, monitor_spec_b->serial);
+}
+
 void
 meta_monitor_spec_free (MetaMonitorSpec *monitor_spec)
 {
diff --git a/src/backends/meta-monitor.h b/src/backends/meta-monitor.h
index 8ed9563..d38b766 100644
--- a/src/backends/meta-monitor.h
+++ b/src/backends/meta-monitor.h
@@ -154,6 +154,9 @@ MetaMonitorSpec * meta_monitor_spec_clone (MetaMonitorSpec *monitor_id);
 gboolean meta_monitor_spec_equals (MetaMonitorSpec *monitor_id,
                                    MetaMonitorSpec *other_monitor_id);
 
+int meta_monitor_spec_compare (MetaMonitorSpec *monitor_spec_a,
+                               MetaMonitorSpec *monitor_spec_b);
+
 void meta_monitor_spec_free (MetaMonitorSpec *monitor_id);
 
 #endif /* META_MONITOR_H */



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