c80064
From 9cf4d7b6d8a279a0d2a047f52d9fa5b0586b1ab9 Mon Sep 17 00:00:00 2001
c80064
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
c80064
Date: Fri, 14 Jan 2022 22:11:17 +0100
c80064
Subject: [PATCH 1/9] test/utils: Add helper to set custom monitors config
c80064
c80064
Make the existing implementation a wrapper to avoid changing monitor
c80064
config tests.
c80064
c80064
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2237>
c80064
c80064
Cherry-picked from 57d1d82ead6392a104a9e9d6c7f1f4f14ad54e48
c80064
---
c80064
 src/tests/monitor-test-utils.c | 18 +-----------------
c80064
 src/tests/test-utils.c         | 23 +++++++++++++++++++++++
c80064
 src/tests/test-utils.h         |  3 +++
c80064
 3 files changed, 27 insertions(+), 17 deletions(-)
c80064
c80064
diff --git a/src/tests/monitor-test-utils.c b/src/tests/monitor-test-utils.c
c80064
index 54881569102e..d2dc3b2658c6 100644
c80064
--- a/src/tests/monitor-test-utils.c
c80064
+++ b/src/tests/monitor-test-utils.c
c80064
@@ -28,23 +28,7 @@
c80064
 void
c80064
 set_custom_monitor_config (const char *filename)
c80064
 {
c80064
-  MetaBackend *backend = meta_get_backend ();
c80064
-  MetaMonitorManager *monitor_manager =
c80064
-    meta_backend_get_monitor_manager (backend);
c80064
-  MetaMonitorConfigManager *config_manager = monitor_manager->config_manager;
c80064
-  MetaMonitorConfigStore *config_store;
c80064
-  GError *error = NULL;
c80064
-  const char *path;
c80064
-
c80064
-  g_assert_nonnull (config_manager);
c80064
-
c80064
-  config_store = meta_monitor_config_manager_get_store (config_manager);
c80064
-
c80064
-  path = g_test_get_filename (G_TEST_DIST, "tests", "monitor-configs",
c80064
-                              filename, NULL);
c80064
-  if (!meta_monitor_config_store_set_custom (config_store, path, NULL,
c80064
-                                             &error))
c80064
-    g_error ("Failed to set custom config: %s", error->message);
c80064
+  meta_set_custom_monitor_config (meta_get_backend (), filename);
c80064
 }
c80064
 
c80064
 char *
c80064
diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c
c80064
index 980b20acdcb0..dd6230dc15ec 100644
c80064
--- a/src/tests/test-utils.c
c80064
+++ b/src/tests/test-utils.c
c80064
@@ -24,6 +24,7 @@
c80064
 #include <gio/gio.h>
c80064
 #include <string.h>
c80064
 
c80064
+#include "backends/meta-monitor-config-store.h"
c80064
 #include "core/display-private.h"
c80064
 #include "core/window-private.h"
c80064
 #include "wayland/meta-wayland.h"
c80064
@@ -483,3 +484,25 @@ test_get_plugin_name (void)
c80064
   else
c80064
     return "libdefault";
c80064
 }
c80064
+
c80064
+void
c80064
+meta_set_custom_monitor_config (MetaBackend *backend,
c80064
+                                const char  *filename)
c80064
+{
c80064
+  MetaMonitorManager *monitor_manager =
c80064
+    meta_backend_get_monitor_manager (backend);
c80064
+  MetaMonitorConfigManager *config_manager = monitor_manager->config_manager;
c80064
+  MetaMonitorConfigStore *config_store;
c80064
+  GError *error = NULL;
c80064
+  const char *path;
c80064
+
c80064
+  g_assert_nonnull (config_manager);
c80064
+
c80064
+  config_store = meta_monitor_config_manager_get_store (config_manager);
c80064
+
c80064
+  path = g_test_get_filename (G_TEST_DIST, "tests", "monitor-configs",
c80064
+                              filename, NULL);
c80064
+  if (!meta_monitor_config_store_set_custom (config_store, path, NULL,
c80064
+                                             &error))
c80064
+    g_error ("Failed to set custom config: %s", error->message);
c80064
+}
c80064
diff --git a/src/tests/test-utils.h b/src/tests/test-utils.h
c80064
index e11f25353a5b..5092f766fdf6 100644
c80064
--- a/src/tests/test-utils.h
c80064
+++ b/src/tests/test-utils.h
c80064
@@ -81,4 +81,7 @@ void test_client_destroy (TestClient *client);
c80064
 
c80064
 const char * test_get_plugin_name (void);
c80064
 
c80064
+void meta_set_custom_monitor_config (MetaBackend *backend,
c80064
+                                     const char  *filename);
c80064
+
c80064
 #endif /* TEST_UTILS_H */
c80064
-- 
c80064
2.33.1
c80064
c80064
c80064
From 8133ff828e46e19d4afa277c1e013a846b443b9f Mon Sep 17 00:00:00 2001
c80064
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
c80064
Date: Fri, 14 Jan 2022 22:12:36 +0100
c80064
Subject: [PATCH 2/9] tests/utils: Add meta_wait_for_paint() helper
c80064
c80064
This function queues a full stage redraw, then waits for every view to
c80064
receive the "presented" signal before returning.
c80064
c80064
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2237>
c80064
(cherry picked from commit d84f7971e476a1e2d727310d9a33ac4080137f58)
c80064
---
c80064
 src/tests/test-utils.c | 27 +++++++++++++++++++++++++++
c80064
 src/tests/test-utils.h |  2 ++
c80064
 2 files changed, 29 insertions(+)
c80064
c80064
diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c
c80064
index dd6230dc15ec..f1a688478f67 100644
c80064
--- a/src/tests/test-utils.c
c80064
+++ b/src/tests/test-utils.c
c80064
@@ -506,3 +506,30 @@ meta_set_custom_monitor_config (MetaBackend *backend,
c80064
                                              &error))
c80064
     g_error ("Failed to set custom config: %s", error->message);
c80064
 }
c80064
+
c80064
+static void
c80064
+on_view_presented (ClutterStage      *stage,
c80064
+                   ClutterStageView  *view,
c80064
+                   ClutterFrameInfo  *frame_info,
c80064
+                   GList            **presented_views)
c80064
+{
c80064
+  *presented_views = g_list_remove (*presented_views, view);
c80064
+}
c80064
+
c80064
+void
c80064
+meta_wait_for_paint (MetaBackend *backend)
c80064
+{
c80064
+  ClutterActor *stage = meta_backend_get_stage (backend);
c80064
+  MetaRenderer *renderer = meta_backend_get_renderer (backend);
c80064
+  GList *views;
c80064
+  gulong handler_id;
c80064
+
c80064
+  clutter_actor_queue_redraw (stage);
c80064
+
c80064
+  views = g_list_copy (meta_renderer_get_views (renderer));
c80064
+  handler_id = g_signal_connect (stage, "presented",
c80064
+                                 G_CALLBACK (on_view_presented), &views);
c80064
+  while (views)
c80064
+    g_main_context_iteration (NULL, TRUE);
c80064
+  g_signal_handler_disconnect (stage, handler_id);
c80064
+}
c80064
diff --git a/src/tests/test-utils.h b/src/tests/test-utils.h
c80064
index 5092f766fdf6..6d6cdb924d01 100644
c80064
--- a/src/tests/test-utils.h
c80064
+++ b/src/tests/test-utils.h
c80064
@@ -84,4 +84,6 @@ const char * test_get_plugin_name (void);
c80064
 void meta_set_custom_monitor_config (MetaBackend *backend,
c80064
                                      const char  *filename);
c80064
 
c80064
+void meta_wait_for_paint (MetaBackend *backend);
c80064
+
c80064
 #endif /* TEST_UTILS_H */
c80064
-- 
c80064
2.33.1
c80064
c80064
c80064
From 61e384f2b652e5ac774a0cb46daaba3e89ddefdc Mon Sep 17 00:00:00 2001
c80064
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
c80064
Date: Thu, 30 Sep 2021 08:59:03 +0200
c80064
Subject: [PATCH 3/9] monitor-config-store: Make parsing a bit more forgiving
c80064
c80064
Allow unknown XML elements inside <monitors>. This makes extending in
c80064
the future easier.
c80064
c80064
(cherry picked from commit 3cd666c657fa716f06dee69df59356b53b6c5d72)
c80064
---
c80064
 src/backends/meta-monitor-config-store.c      | 54 +++++++++++++++---
c80064
 .../monitor-configs/unknown-elements.xml      | 31 ++++++++++
c80064
 src/tests/monitor-store-unit-tests.c          | 56 +++++++++++++++++++
c80064
 3 files changed, 133 insertions(+), 8 deletions(-)
c80064
 create mode 100644 src/tests/monitor-configs/unknown-elements.xml
c80064
c80064
diff --git a/src/backends/meta-monitor-config-store.c b/src/backends/meta-monitor-config-store.c
c80064
index 770bef734e6f..9a3daf561a69 100644
c80064
--- a/src/backends/meta-monitor-config-store.c
c80064
+++ b/src/backends/meta-monitor-config-store.c
c80064
@@ -136,6 +136,7 @@ G_DEFINE_QUARK (meta-monitor-config-store-error-quark,
c80064
 typedef enum
c80064
 {
c80064
   STATE_INITIAL,
c80064
+  STATE_UNKNOWN,
c80064
   STATE_MONITORS,
c80064
   STATE_CONFIGURATION,
c80064
   STATE_MIGRATED,
c80064
@@ -180,12 +181,28 @@ typedef struct
c80064
   MetaLogicalMonitorConfig *current_logical_monitor_config;
c80064
   GList *current_disabled_monitor_specs;
c80064
 
c80064
+  ParserState unknown_state_root;
c80064
+  int unknown_level;
c80064
+
c80064
   MetaMonitorsConfigFlag extra_config_flags;
c80064
 } ConfigParser;
c80064
 
c80064
 G_DEFINE_TYPE (MetaMonitorConfigStore, meta_monitor_config_store,
c80064
                G_TYPE_OBJECT)
c80064
 
c80064
+static void
c80064
+enter_unknown_element (ConfigParser *parser,
c80064
+                       const char   *element_name,
c80064
+                       const char   *root_element_name,
c80064
+                       ParserState   root_state)
c80064
+{
c80064
+  parser->state = STATE_UNKNOWN;
c80064
+  parser->unknown_level = 1;
c80064
+  parser->unknown_state_root = root_state;
c80064
+  g_warning ("Unknown element <%s> under <%s>, ignoring",
c80064
+             element_name, root_element_name);
c80064
+}
c80064
+
c80064
 static void
c80064
 handle_start_element (GMarkupParseContext  *context,
c80064
                       const char           *element_name,
c80064
@@ -242,8 +259,8 @@ handle_start_element (GMarkupParseContext  *context,
c80064
       {
c80064
         if (!g_str_equal (element_name, "configuration"))
c80064
           {
c80064
-            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
c80064
-                         "Invalid toplevel element '%s'", element_name);
c80064
+            enter_unknown_element (parser, element_name,
c80064
+                                   "monitors", STATE_MONITORS);
c80064
             return;
c80064
           }
c80064
 
c80064
@@ -253,6 +270,13 @@ handle_start_element (GMarkupParseContext  *context,
c80064
         return;
c80064
       }
c80064
 
c80064
+    case STATE_UNKNOWN:
c80064
+      {
c80064
+        parser->unknown_level++;
c80064
+
c80064
+        return;
c80064
+      }
c80064
+
c80064
     case STATE_CONFIGURATION:
c80064
       {
c80064
         if (g_str_equal (element_name, "logicalmonitor"))
c80064
@@ -274,9 +298,8 @@ handle_start_element (GMarkupParseContext  *context,
c80064
           }
c80064
         else
c80064
           {
c80064
-            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
c80064
-                         "Invalid configuration element '%s'", element_name);
c80064
-            return;
c80064
+            enter_unknown_element (parser, element_name,
c80064
+                                   "configuration", STATE_CONFIGURATION);
c80064
           }
c80064
 
c80064
         return;
c80064
@@ -323,9 +346,8 @@ handle_start_element (GMarkupParseContext  *context,
c80064
           }
c80064
         else
c80064
           {
c80064
-            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
c80064
-                         "Invalid monitor logicalmonitor element '%s'", element_name);
c80064
-            return;
c80064
+            enter_unknown_element (parser, element_name,
c80064
+                                   "logicalmonitor", STATE_LOGICAL_MONITOR);
c80064
           }
c80064
 
c80064
         return;
c80064
@@ -793,6 +815,18 @@ handle_end_element (GMarkupParseContext  *context,
c80064
         return;
c80064
       }
c80064
 
c80064
+    case STATE_UNKNOWN:
c80064
+      {
c80064
+        parser->unknown_level--;
c80064
+        if (parser->unknown_level == 0)
c80064
+          {
c80064
+            g_assert (parser->unknown_state_root >= 0);
c80064
+            parser->state = parser->unknown_state_root;
c80064
+            parser->unknown_state_root = -1;
c80064
+          }
c80064
+        return;
c80064
+      }
c80064
+
c80064
     case STATE_MONITORS:
c80064
       {
c80064
         g_assert (g_str_equal (element_name, "monitors"));
c80064
@@ -912,6 +946,9 @@ handle_text (GMarkupParseContext *context,
c80064
 
c80064
   switch (parser->state)
c80064
     {
c80064
+    case STATE_UNKNOWN:
c80064
+      return;
c80064
+
c80064
     case STATE_INITIAL:
c80064
     case STATE_MONITORS:
c80064
     case STATE_CONFIGURATION:
c80064
@@ -1099,6 +1136,7 @@ read_config_file (MetaMonitorConfigStore  *config_store,
c80064
     .state = STATE_INITIAL,
c80064
     .config_store = config_store,
c80064
     .extra_config_flags = extra_config_flags,
c80064
+    .unknown_state_root = -1,
c80064
   };
c80064
 
c80064
   parse_context = g_markup_parse_context_new (&config_parser,
c80064
diff --git a/src/tests/monitor-configs/unknown-elements.xml b/src/tests/monitor-configs/unknown-elements.xml
c80064
new file mode 100644
c80064
index 000000000000..f81be95dd9df
c80064
--- /dev/null
c80064
+++ b/src/tests/monitor-configs/unknown-elements.xml
c80064
@@ -0,0 +1,31 @@
c80064
+<monitors version="2">
c80064
+  <unknownundermonitors>
c80064
+    <anotherlevel>text</anotherlevel>
c80064
+  </unknownundermonitors>
c80064
+  <configuration>
c80064
+    <unknownunderconfiguration>
c80064
+      <anotherlevel>text</anotherlevel>
c80064
+    </unknownunderconfiguration>
c80064
+    <logicalmonitor>
c80064
+      <unknownunderlogicalmonitor>
c80064
+	<anotherlevel>text</anotherlevel>
c80064
+      </unknownunderlogicalmonitor>
c80064
+      <x>0</x>
c80064
+      <y>0</y>
c80064
+      <primary>yes</primary>
c80064
+      <monitor>
c80064
+	<monitorspec>
c80064
+	  <connector>DP-1</connector>
c80064
+	  <vendor>MetaProduct's Inc.</vendor>
c80064
+	  <product>MetaMonitor</product>
c80064
+	  <serial>0x123456</serial>
c80064
+	</monitorspec>
c80064
+	<mode>
c80064
+	  <width>1920</width>
c80064
+	  <height>1080</height>
c80064
+	  <rate>60.000495910644531</rate>
c80064
+	</mode>
c80064
+      </monitor>
c80064
+    </logicalmonitor>
c80064
+  </configuration>
c80064
+</monitors>
c80064
diff --git a/src/tests/monitor-store-unit-tests.c b/src/tests/monitor-store-unit-tests.c
c80064
index 4a73d89db4c6..4ff0e396dda7 100644
c80064
--- a/src/tests/monitor-store-unit-tests.c
c80064
+++ b/src/tests/monitor-store-unit-tests.c
c80064
@@ -836,6 +836,60 @@ meta_test_monitor_store_interlaced (void)
c80064
   check_monitor_configurations (&expect);
c80064
 }
c80064
 
c80064
+static void
c80064
+meta_test_monitor_store_unknown_elements (void)
c80064
+{
c80064
+  MonitorStoreTestExpect expect = {
c80064
+    .configurations = {
c80064
+      {
c80064
+        .logical_monitors = {
c80064
+          {
c80064
+            .layout = {
c80064
+              .x = 0,
c80064
+              .y = 0,
c80064
+              .width = 1920,
c80064
+              .height = 1080
c80064
+            },
c80064
+            .scale = 1,
c80064
+            .is_primary = TRUE,
c80064
+            .is_presentation = FALSE,
c80064
+            .monitors = {
c80064
+              {
c80064
+                .connector = "DP-1",
c80064
+                .vendor = "MetaProduct's Inc.",
c80064
+                .product = "MetaMonitor",
c80064
+                .serial = "0x123456",
c80064
+                .mode = {
c80064
+                  .width = 1920,
c80064
+                  .height = 1080,
c80064
+                  .refresh_rate = 60.000495910644531
c80064
+                }
c80064
+              }
c80064
+            },
c80064
+            .n_monitors = 1,
c80064
+          }
c80064
+        },
c80064
+        .n_logical_monitors = 1
c80064
+      }
c80064
+    },
c80064
+    .n_configurations = 1
c80064
+  };
c80064
+
c80064
+  g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
c80064
+                         "Unknown element <unknownundermonitors> "
c80064
+                         "under <monitors>, ignoring");
c80064
+  g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
c80064
+                         "Unknown element <unknownunderconfiguration> "
c80064
+                         "under <configuration>, ignoring");
c80064
+  g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
c80064
+                         "Unknown element <unknownunderlogicalmonitor> "
c80064
+                         "under <logicalmonitor>, ignoring");
c80064
+  set_custom_monitor_config ("unknown-elements.xml");
c80064
+  g_test_assert_expected_messages ();
c80064
+
c80064
+  check_monitor_configurations (&expect);
c80064
+}
c80064
+
c80064
 void
c80064
 init_monitor_store_tests (void)
c80064
 {
c80064
@@ -861,4 +915,6 @@ init_monitor_store_tests (void)
c80064
                    meta_test_monitor_store_second_rotated);
c80064
   g_test_add_func ("/backends/monitor-store/interlaced",
c80064
                    meta_test_monitor_store_interlaced);
c80064
+  g_test_add_func ("/backends/monitor-store/unknown-elements",
c80064
+                   meta_test_monitor_store_unknown_elements);
c80064
 }
c80064
-- 
c80064
2.33.1
c80064
c80064
c80064
From cb3f23d6bdc01f16a0c9236873057faede0a4b32 Mon Sep 17 00:00:00 2001
c80064
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
c80064
Date: Fri, 24 Sep 2021 16:29:47 +0200
c80064
Subject: [PATCH 4/9] monitor-config-store: Fix incorrect string comparison
c80064
 with empty string
c80064
c80064
strncmp() always return 0 if the passed length is 0. What this means is
c80064
that whatever the first string check happens to be, if the parsed XML
c80064
cdata was empty (e.g. if we got <element></element>), the first
c80064
condition would evaluate to true, which is rather unexpected.
c80064
c80064
Fix this by making sure the string length is correct first. Also move it
c80064
into a helper so we don't need to repeat the same strlen() check every
c80064
time.
c80064
c80064
(cherry picked from commit f798e49502313dd3e7dd67143513a7a6a91b49f8)
c80064
---
c80064
 src/backends/meta-monitor-config-store.c | 25 +++++++++++++++++-------
c80064
 1 file changed, 18 insertions(+), 7 deletions(-)
c80064
c80064
diff --git a/src/backends/meta-monitor-config-store.c b/src/backends/meta-monitor-config-store.c
c80064
index 9a3daf561a69..f6b5256aa798 100644
c80064
--- a/src/backends/meta-monitor-config-store.c
c80064
+++ b/src/backends/meta-monitor-config-store.c
c80064
@@ -190,6 +190,17 @@ typedef struct
c80064
 G_DEFINE_TYPE (MetaMonitorConfigStore, meta_monitor_config_store,
c80064
                G_TYPE_OBJECT)
c80064
 
c80064
+static gboolean
c80064
+text_equals (const char *text,
c80064
+             int         len,
c80064
+             const char *expect)
c80064
+{
c80064
+  if (strlen (expect) != len)
c80064
+    return FALSE;
c80064
+
c80064
+  return strncmp (text, expect, len) == 0;
c80064
+}
c80064
+
c80064
 static void
c80064
 enter_unknown_element (ConfigParser *parser,
c80064
                        const char   *element_name,
c80064
@@ -904,12 +915,12 @@ read_bool (const char  *text,
c80064
            gboolean    *out_value,
c80064
            GError     **error)
c80064
 {
c80064
-  if (strncmp (text, "no", text_len) == 0)
c80064
+  if (text_equals (text, text_len, "no"))
c80064
     {
c80064
       *out_value = FALSE;
c80064
       return TRUE;
c80064
     }
c80064
-  else if (strncmp (text, "yes", text_len) == 0)
c80064
+  else if (text_equals (text, text_len, "yes"))
c80064
     {
c80064
       *out_value = TRUE;
c80064
       return TRUE;
c80064
@@ -1039,13 +1050,13 @@ handle_text (GMarkupParseContext *context,
c80064
 
c80064
     case STATE_TRANSFORM_ROTATION:
c80064
       {
c80064
-        if (strncmp (text, "normal", text_len) == 0)
c80064
+        if (text_equals (text, text_len, "normal"))
c80064
           parser->current_transform = META_MONITOR_TRANSFORM_NORMAL;
c80064
-        else if (strncmp (text, "left", text_len) == 0)
c80064
+        else if (text_equals (text, text_len, "left"))
c80064
           parser->current_transform = META_MONITOR_TRANSFORM_90;
c80064
-        else if (strncmp (text, "upside_down", text_len) == 0)
c80064
+        else if (text_equals (text, text_len, "upside_down"))
c80064
           parser->current_transform = META_MONITOR_TRANSFORM_180;
c80064
-        else if (strncmp (text, "right", text_len) == 0)
c80064
+        else if (text_equals (text, text_len, "right"))
c80064
           parser->current_transform = META_MONITOR_TRANSFORM_270;
c80064
         else
c80064
           g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
c80064
@@ -1088,7 +1099,7 @@ handle_text (GMarkupParseContext *context,
c80064
 
c80064
     case STATE_MONITOR_MODE_FLAG:
c80064
       {
c80064
-        if (strncmp (text, "interlace", text_len) == 0)
c80064
+        if (text_equals (text, text_len, "interlace"))
c80064
           {
c80064
             parser->current_monitor_mode_spec->flags |=
c80064
               META_CRTC_MODE_FLAG_INTERLACE;
c80064
-- 
c80064
2.33.1
c80064
c80064
c80064
From 73ae3806396840213eb72376a954262d7132a097 Mon Sep 17 00:00:00 2001
c80064
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
c80064
Date: Mon, 17 Jan 2022 11:45:53 +0100
c80064
Subject: [PATCH 5/9] monitor-config-store: Add way to define config store
c80064
 loading policy
c80064
c80064
This adds a way to define a way, at the system level, to define a policy
c80064
of how monitor configuration files are loaded.
c80064
c80064
The intended use case is to e.g. either prefer system level monitor
c80064
configurations before user levels, or only allow system level
c80064
configurations.
c80064
c80064
Examples:
c80064
c80064
Prefer system over user level configurations:
c80064
c80064
    <monitors version="2">
c80064
      <policy>
c80064
        <stores>
c80064
          <store>system</store>
c80064
          <store>user</store>
c80064
        </stores>
c80064
      </policy>
c80064
      <configuration>
c80064
        ...
c80064
      </configuration>
c80064
    </monitors>
c80064
c80064
Only allow system level configurations:
c80064
c80064
    <monitors version="2">
c80064
      <policy>
c80064
        <stores>
c80064
          <store>system</store>
c80064
        </stores>
c80064
      </policy>
c80064
      <configuration>
c80064
        ...
c80064
      </configuration>
c80064
    </monitors>
c80064
c80064
(cherry picked from commit b747884c1eaf309bb2d9395a655c85c968bd1829)
c80064
---
c80064
 src/backends/meta-backend-types.h             |   2 +
c80064
 src/backends/meta-monitor-config-manager.h    |   4 +-
c80064
 src/backends/meta-monitor-config-store.c      | 421 ++++++++++++++----
c80064
 src/backends/meta-monitor-config-store.h      |  21 +-
c80064
 .../monitor-config-migration-unit-tests.c     |   1 +
c80064
 src/tests/monitor-configs/policy.xml          |  27 ++
c80064
 src/tests/monitor-store-unit-tests.c          |  33 ++
c80064
 src/tests/monitor-test-utils.c                |  16 +-
c80064
 src/tests/monitor-test-utils.h                |   2 +
c80064
 src/tests/monitor-unit-tests.c                |   3 +
c80064
 src/tests/test-utils.c                        |   8 +-
c80064
 src/tests/test-utils.h                        |   6 +-
c80064
 12 files changed, 450 insertions(+), 94 deletions(-)
c80064
 create mode 100644 src/tests/monitor-configs/policy.xml
c80064
c80064
diff --git a/src/backends/meta-backend-types.h b/src/backends/meta-backend-types.h
c80064
index 98cac8b9e2a9..15184f513289 100644
c80064
--- a/src/backends/meta-backend-types.h
c80064
+++ b/src/backends/meta-backend-types.h
c80064
@@ -27,6 +27,8 @@ typedef struct _MetaMonitorConfigManager MetaMonitorConfigManager;
c80064
 typedef struct _MetaMonitorConfigStore MetaMonitorConfigStore;
c80064
 typedef struct _MetaMonitorsConfig MetaMonitorsConfig;
c80064
 
c80064
+typedef enum _MetaMonitorsConfigFlag MetaMonitorsConfigFlag;
c80064
+
c80064
 typedef struct _MetaMonitor MetaMonitor;
c80064
 typedef struct _MetaMonitorNormal MetaMonitorNormal;
c80064
 typedef struct _MetaMonitorTiled MetaMonitorTiled;
c80064
diff --git a/src/backends/meta-monitor-config-manager.h b/src/backends/meta-monitor-config-manager.h
c80064
index bb847b96e314..a3047b8c6639 100644
c80064
--- a/src/backends/meta-monitor-config-manager.h
c80064
+++ b/src/backends/meta-monitor-config-manager.h
c80064
@@ -51,12 +51,12 @@ typedef struct _MetaMonitorsConfigKey
c80064
   GList *monitor_specs;
c80064
 } MetaMonitorsConfigKey;
c80064
 
c80064
-typedef enum _MetaMonitorsConfigFlag
c80064
+enum _MetaMonitorsConfigFlag
c80064
 {
c80064
   META_MONITORS_CONFIG_FLAG_NONE = 0,
c80064
   META_MONITORS_CONFIG_FLAG_MIGRATED = (1 << 0),
c80064
   META_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG = (1 << 1),
c80064
-} MetaMonitorsConfigFlag;
c80064
+};
c80064
 
c80064
 struct _MetaMonitorsConfig
c80064
 {
c80064
diff --git a/src/backends/meta-monitor-config-store.c b/src/backends/meta-monitor-config-store.c
c80064
index f6b5256aa798..b320746a4418 100644
c80064
--- a/src/backends/meta-monitor-config-store.c
c80064
+++ b/src/backends/meta-monitor-config-store.c
c80064
@@ -120,6 +120,9 @@ struct _MetaMonitorConfigStore
c80064
   GFile *user_file;
c80064
   GFile *custom_read_file;
c80064
   GFile *custom_write_file;
c80064
+
c80064
+  gboolean has_stores_policy;
c80064
+  GList *stores_policy;
c80064
 };
c80064
 
c80064
 #define META_MONITOR_CONFIG_STORE_ERROR (meta_monitor_config_store_error_quark ())
c80064
@@ -162,12 +165,18 @@ typedef enum
c80064
   STATE_MONITOR_MODE_FLAG,
c80064
   STATE_MONITOR_UNDERSCANNING,
c80064
   STATE_DISABLED,
c80064
+  STATE_POLICY,
c80064
+  STATE_STORES,
c80064
+  STATE_STORE,
c80064
 } ParserState;
c80064
 
c80064
 typedef struct
c80064
 {
c80064
   ParserState state;
c80064
   MetaMonitorConfigStore *config_store;
c80064
+  GFile *file;
c80064
+
c80064
+  GHashTable *pending_configs;
c80064
 
c80064
   ParserState monitor_spec_parent_state;
c80064
 
c80064
@@ -180,6 +189,10 @@ typedef struct
c80064
   MetaMonitorConfig *current_monitor_config;
c80064
   MetaLogicalMonitorConfig *current_logical_monitor_config;
c80064
   GList *current_disabled_monitor_specs;
c80064
+  gboolean seen_policy;
c80064
+  gboolean seen_stores;
c80064
+  MetaConfigStore pending_store;
c80064
+  GList *stores;
c80064
 
c80064
   ParserState unknown_state_root;
c80064
   int unknown_level;
c80064
@@ -268,16 +281,31 @@ handle_start_element (GMarkupParseContext  *context,
c80064
 
c80064
     case STATE_MONITORS:
c80064
       {
c80064
-        if (!g_str_equal (element_name, "configuration"))
c80064
+        if (g_str_equal (element_name, "configuration"))
c80064
+          {
c80064
+            parser->state = STATE_CONFIGURATION;
c80064
+            parser->current_was_migrated = FALSE;
c80064
+          }
c80064
+        else if (g_str_equal (element_name, "policy"))
c80064
+          {
c80064
+            if (parser->seen_policy)
c80064
+              {
c80064
+                g_set_error (error,
c80064
+                             G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
c80064
+                             "Multiple policy definitions");
c80064
+                return;
c80064
+              }
c80064
+
c80064
+            parser->seen_policy = TRUE;
c80064
+            parser->state = STATE_POLICY;
c80064
+          }
c80064
+        else
c80064
           {
c80064
             enter_unknown_element (parser, element_name,
c80064
                                    "monitors", STATE_MONITORS);
c80064
             return;
c80064
           }
c80064
 
c80064
-        parser->state = STATE_CONFIGURATION;
c80064
-        parser->current_was_migrated = FALSE;
c80064
-
c80064
         return;
c80064
       }
c80064
 
c80064
@@ -523,6 +551,59 @@ handle_start_element (GMarkupParseContext  *context,
c80064
 
c80064
         return;
c80064
       }
c80064
+
c80064
+    case STATE_POLICY:
c80064
+      {
c80064
+        if (!(parser->extra_config_flags &
c80064
+              META_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG))
c80064
+          {
c80064
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
c80064
+                         "Policy can only be defined in system level configurations");
c80064
+            return;
c80064
+          }
c80064
+
c80064
+        if (g_str_equal (element_name, "stores"))
c80064
+          {
c80064
+            if (parser->seen_stores)
c80064
+              {
c80064
+                g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
c80064
+                             "Multiple stores elements under policy");
c80064
+                return;
c80064
+              }
c80064
+
c80064
+            parser->seen_stores = TRUE;
c80064
+            parser->state = STATE_STORES;
c80064
+          }
c80064
+        else
c80064
+          {
c80064
+            enter_unknown_element (parser, element_name,
c80064
+                                   "policy", STATE_POLICY);
c80064
+          }
c80064
+
c80064
+        return;
c80064
+      }
c80064
+
c80064
+    case STATE_STORES:
c80064
+      {
c80064
+        if (g_str_equal (element_name, "store"))
c80064
+          {
c80064
+            parser->state = STATE_STORE;
c80064
+          }
c80064
+        else
c80064
+          {
c80064
+            enter_unknown_element (parser, element_name,
c80064
+                                   "stores", STATE_STORES);
c80064
+          }
c80064
+
c80064
+        return;
c80064
+      }
c80064
+
c80064
+    case STATE_STORE:
c80064
+      {
c80064
+        g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
c80064
+                     "Invalid store sub element '%s'", element_name);
c80064
+        return;
c80064
+      }
c80064
     }
c80064
 }
c80064
 
c80064
@@ -819,13 +900,65 @@ handle_end_element (GMarkupParseContext  *context,
c80064
             return;
c80064
           }
c80064
 
c80064
-        g_hash_table_replace (parser->config_store->configs,
c80064
+        g_hash_table_replace (parser->pending_configs,
c80064
                               config->key, config);
c80064
 
c80064
         parser->state = STATE_MONITORS;
c80064
         return;
c80064
       }
c80064
 
c80064
+    case STATE_STORE:
c80064
+        g_assert (g_str_equal (element_name, "store"));
c80064
+
c80064
+        if (parser->pending_store == -1)
c80064
+          {
c80064
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
c80064
+                         "Got an empty store");
c80064
+            return;
c80064
+          }
c80064
+
c80064
+        if (g_list_find (parser->stores,
c80064
+                         GINT_TO_POINTER (parser->pending_store)))
c80064
+          {
c80064
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
c80064
+                         "Multiple identical stores in policy");
c80064
+            return;
c80064
+          }
c80064
+
c80064
+        parser->stores =
c80064
+          g_list_append (parser->stores,
c80064
+                         GINT_TO_POINTER (parser->pending_store));
c80064
+        parser->pending_store = -1;
c80064
+
c80064
+        parser->state = STATE_STORES;
c80064
+        return;
c80064
+
c80064
+    case STATE_STORES:
c80064
+        g_assert (g_str_equal (element_name, "stores"));
c80064
+
c80064
+        if (parser->config_store->has_stores_policy)
c80064
+          {
c80064
+            g_warning ("Ignoring stores policy from '%s', "
c80064
+                       "it has already been configured",
c80064
+                       g_file_peek_path (parser->file));
c80064
+            g_clear_pointer (&parser->stores, g_list_free);
c80064
+          }
c80064
+        else
c80064
+          {
c80064
+            parser->config_store->stores_policy =
c80064
+              g_steal_pointer (&parser->stores);
c80064
+            parser->config_store->has_stores_policy = TRUE;
c80064
+          }
c80064
+
c80064
+        parser->state = STATE_POLICY;
c80064
+        return;
c80064
+
c80064
+    case STATE_POLICY:
c80064
+        g_assert (g_str_equal (element_name, "policy"));
c80064
+
c80064
+        parser->state = STATE_MONITORS;
c80064
+        return;
c80064
+
c80064
     case STATE_UNKNOWN:
c80064
       {
c80064
         parser->unknown_level--;
c80064
@@ -970,6 +1103,8 @@ handle_text (GMarkupParseContext *context,
c80064
     case STATE_MONITOR_MODE:
c80064
     case STATE_TRANSFORM:
c80064
     case STATE_DISABLED:
c80064
+    case STATE_POLICY:
c80064
+    case STATE_STORES:
c80064
       {
c80064
         if (!is_all_whitespace (text, text_len))
c80064
           g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
c80064
@@ -1120,6 +1255,36 @@ handle_text (GMarkupParseContext *context,
c80064
                    error);
c80064
         return;
c80064
       }
c80064
+
c80064
+    case STATE_STORE:
c80064
+      {
c80064
+        MetaConfigStore store;
c80064
+
c80064
+        if (parser->pending_store != -1)
c80064
+          {
c80064
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
c80064
+                         "Multiple store strings");
c80064
+            return;
c80064
+          }
c80064
+
c80064
+        if (text_equals (text, text_len, "system"))
c80064
+          {
c80064
+            store = META_CONFIG_STORE_SYSTEM;
c80064
+          }
c80064
+        else if (text_equals (text, text_len, "user"))
c80064
+          {
c80064
+            store = META_CONFIG_STORE_USER;
c80064
+          }
c80064
+        else
c80064
+          {
c80064
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
c80064
+                         "Invalid store %.*s", (int) text_len, text);
c80064
+            return;
c80064
+          }
c80064
+
c80064
+        parser->pending_store = store;
c80064
+        return;
c80064
+      }
c80064
     }
c80064
 }
c80064
 
c80064
@@ -1133,6 +1298,7 @@ static gboolean
c80064
 read_config_file (MetaMonitorConfigStore  *config_store,
c80064
                   GFile                   *file,
c80064
                   MetaMonitorsConfigFlag   extra_config_flags,
c80064
+                  GHashTable             **out_configs,
c80064
                   GError                 **error)
c80064
 {
c80064
   char *buffer;
c80064
@@ -1145,9 +1311,15 @@ read_config_file (MetaMonitorConfigStore  *config_store,
c80064
 
c80064
   parser = (ConfigParser) {
c80064
     .state = STATE_INITIAL,
c80064
+    .file = file,
c80064
     .config_store = config_store,
c80064
+    .pending_configs = g_hash_table_new_full (meta_monitors_config_key_hash,
c80064
+                                              meta_monitors_config_key_equal,
c80064
+                                              NULL,
c80064
+                                              g_object_unref),
c80064
     .extra_config_flags = extra_config_flags,
c80064
     .unknown_state_root = -1,
c80064
+    .pending_store = -1,
c80064
   };
c80064
 
c80064
   parse_context = g_markup_parse_context_new (&config_parser,
c80064
@@ -1165,9 +1337,13 @@ read_config_file (MetaMonitorConfigStore  *config_store,
c80064
                       meta_monitor_config_free);
c80064
       g_clear_pointer (&parser.current_logical_monitor_config,
c80064
                        meta_logical_monitor_config_free);
c80064
+      g_list_free (parser.stores);
c80064
+      g_hash_table_unref (parser.pending_configs);
c80064
       return FALSE;
c80064
     }
c80064
 
c80064
+  *out_configs = g_steal_pointer (&parser.pending_configs);
c80064
+
c80064
   g_markup_parse_context_free (parse_context);
c80064
   g_free (buffer);
c80064
 
c80064
@@ -1511,23 +1687,34 @@ meta_monitor_config_store_remove (MetaMonitorConfigStore *config_store,
c80064
 }
c80064
 
c80064
 gboolean
c80064
-meta_monitor_config_store_set_custom (MetaMonitorConfigStore *config_store,
c80064
-                                      const char             *read_path,
c80064
-                                      const char             *write_path,
c80064
-                                      GError                **error)
c80064
+meta_monitor_config_store_set_custom (MetaMonitorConfigStore  *config_store,
c80064
+                                      const char              *read_path,
c80064
+                                      const char              *write_path,
c80064
+                                      MetaMonitorsConfigFlag   config_flags,
c80064
+                                      GError                 **error)
c80064
 {
c80064
+  GHashTable *new_configs = NULL;
c80064
+
c80064
   g_clear_object (&config_store->custom_read_file);
c80064
   g_clear_object (&config_store->custom_write_file);
c80064
-  g_hash_table_remove_all (config_store->configs);
c80064
 
c80064
   config_store->custom_read_file = g_file_new_for_path (read_path);
c80064
   if (write_path)
c80064
     config_store->custom_write_file = g_file_new_for_path (write_path);
c80064
 
c80064
-  return read_config_file (config_store,
c80064
-                           config_store->custom_read_file,
c80064
-                           META_MONITORS_CONFIG_FLAG_NONE,
c80064
-                           error);
c80064
+  g_clear_pointer (&config_store->stores_policy, g_list_free);
c80064
+  config_store->has_stores_policy = FALSE;
c80064
+
c80064
+  if (!read_config_file (config_store,
c80064
+                         config_store->custom_read_file,
c80064
+                         config_flags,
c80064
+                         &new_configs,
c80064
+                         error))
c80064
+    return FALSE;
c80064
+
c80064
+  g_clear_pointer (&config_store->configs, g_hash_table_unref);
c80064
+  config_store->configs = g_steal_pointer (&new_configs);
c80064
+  return TRUE;
c80064
 }
c80064
 
c80064
 int
c80064
@@ -1536,6 +1723,12 @@ meta_monitor_config_store_get_config_count (MetaMonitorConfigStore *config_store
c80064
   return (int) g_hash_table_size (config_store->configs);
c80064
 }
c80064
 
c80064
+GList *
c80064
+meta_monitor_config_store_get_stores_policy (MetaMonitorConfigStore *config_store)
c80064
+{
c80064
+  return config_store->stores_policy;
c80064
+}
c80064
+
c80064
 MetaMonitorManager *
c80064
 meta_monitor_config_store_get_monitor_manager (MetaMonitorConfigStore *config_store)
c80064
 {
c80064
@@ -1554,75 +1747,8 @@ static void
c80064
 meta_monitor_config_store_constructed (GObject *object)
c80064
 {
c80064
   MetaMonitorConfigStore *config_store = META_MONITOR_CONFIG_STORE (object);
c80064
-  const char * const *system_dirs;
c80064
-  char *user_file_path;
c80064
-  GError *error = NULL;
c80064
-
c80064
-  for (system_dirs = g_get_system_config_dirs ();
c80064
-       system_dirs && *system_dirs;
c80064
-       system_dirs++)
c80064
-    {
c80064
-      g_autofree char *system_file_path = NULL;
c80064
-
c80064
-      system_file_path = g_build_filename (*system_dirs, "monitors.xml", NULL);
c80064
-      if (g_file_test (system_file_path, G_FILE_TEST_EXISTS))
c80064
-        {
c80064
-          g_autoptr (GFile) system_file = NULL;
c80064
-
c80064
-          system_file = g_file_new_for_path (system_file_path);
c80064
-          if (!read_config_file (config_store,
c80064
-                                 system_file,
c80064
-                                 META_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG,
c80064
-                                 &error))
c80064
-            {
c80064
-              if (g_error_matches (error,
c80064
-                                   META_MONITOR_CONFIG_STORE_ERROR,
c80064
-                                   META_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION))
c80064
-                g_warning ("System monitor configuration file (%s) is "
c80064
-                           "incompatible; ask your administrator to migrate "
c80064
-                           "the system monitor configuation.",
c80064
-                           system_file_path);
c80064
-              else
c80064
-                g_warning ("Failed to read monitors config file '%s': %s",
c80064
-                           system_file_path, error->message);
c80064
-              g_clear_error (&error);
c80064
-            }
c80064
-        }
c80064
-    }
c80064
 
c80064
-  user_file_path = g_build_filename (g_get_user_config_dir (),
c80064
-                                     "monitors.xml",
c80064
-                                     NULL);
c80064
-  config_store->user_file = g_file_new_for_path (user_file_path);
c80064
-
c80064
-  if (g_file_test (user_file_path, G_FILE_TEST_EXISTS))
c80064
-    {
c80064
-      if (!read_config_file (config_store,
c80064
-                             config_store->user_file,
c80064
-                             META_MONITORS_CONFIG_FLAG_NONE,
c80064
-                             &error))
c80064
-        {
c80064
-          if (error->domain == META_MONITOR_CONFIG_STORE_ERROR &&
c80064
-              error->code == META_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION)
c80064
-            {
c80064
-              g_clear_error (&error);
c80064
-              if (!meta_migrate_old_user_monitors_config (config_store, &error))
c80064
-                {
c80064
-                  g_warning ("Failed to migrate old monitors config file: %s",
c80064
-                             error->message);
c80064
-                  g_error_free (error);
c80064
-                }
c80064
-            }
c80064
-          else
c80064
-            {
c80064
-              g_warning ("Failed to read monitors config file '%s': %s",
c80064
-                         user_file_path, error->message);
c80064
-              g_error_free (error);
c80064
-            }
c80064
-        }
c80064
-    }
c80064
-
c80064
-  g_free (user_file_path);
c80064
+  meta_monitor_config_store_reset (config_store);
c80064
 
c80064
   G_OBJECT_CLASS (meta_monitor_config_store_parent_class)->constructed (object);
c80064
 }
c80064
@@ -1645,6 +1771,7 @@ meta_monitor_config_store_dispose (GObject *object)
c80064
   g_clear_object (&config_store->user_file);
c80064
   g_clear_object (&config_store->custom_read_file);
c80064
   g_clear_object (&config_store->custom_write_file);
c80064
+  g_clear_pointer (&config_store->stores_policy, g_list_free);
c80064
 
c80064
   G_OBJECT_CLASS (meta_monitor_config_store_parent_class)->dispose (object);
c80064
 }
c80064
@@ -1715,3 +1842,133 @@ meta_monitor_config_store_class_init (MetaMonitorConfigStoreClass *klass)
c80064
 
c80064
   g_object_class_install_properties (object_class, PROP_LAST, obj_props);
c80064
 }
c80064
+
c80064
+static void
c80064
+replace_configs (MetaMonitorConfigStore *config_store,
c80064
+                 GHashTable             *configs)
c80064
+{
c80064
+  GHashTableIter iter;
c80064
+  MetaMonitorsConfigKey *key;
c80064
+  MetaMonitorsConfig *config;
c80064
+
c80064
+  g_hash_table_iter_init (&iter, configs);
c80064
+  while (g_hash_table_iter_next (&iter,
c80064
+                                 (gpointer *) &key,
c80064
+                                 (gpointer *) &config))
c80064
+    {
c80064
+      g_hash_table_iter_steal (&iter);
c80064
+      g_hash_table_replace (config_store->configs, key, config);
c80064
+    }
c80064
+}
c80064
+
c80064
+void
c80064
+meta_monitor_config_store_reset (MetaMonitorConfigStore *config_store)
c80064
+{
c80064
+  g_autoptr (GHashTable) system_configs = NULL;
c80064
+  g_autoptr (GHashTable) user_configs = NULL;
c80064
+  const char * const *system_dirs;
c80064
+  char *user_file_path;
c80064
+  GError *error = NULL;
c80064
+
c80064
+  g_clear_object (&config_store->user_file);
c80064
+  g_clear_object (&config_store->custom_read_file);
c80064
+  g_clear_object (&config_store->custom_write_file);
c80064
+  g_hash_table_remove_all (config_store->configs);
c80064
+
c80064
+  for (system_dirs = g_get_system_config_dirs ();
c80064
+       system_dirs && *system_dirs;
c80064
+       system_dirs++)
c80064
+    {
c80064
+      g_autofree char *system_file_path = NULL;
c80064
+
c80064
+      system_file_path = g_build_filename (*system_dirs, "monitors.xml", NULL);
c80064
+      if (g_file_test (system_file_path, G_FILE_TEST_EXISTS))
c80064
+        {
c80064
+          g_autoptr (GFile) system_file = NULL;
c80064
+
c80064
+          system_file = g_file_new_for_path (system_file_path);
c80064
+          if (!read_config_file (config_store,
c80064
+                                 system_file,
c80064
+                                 META_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG,
c80064
+                                 &system_configs,
c80064
+                                 &error))
c80064
+            {
c80064
+              if (g_error_matches (error,
c80064
+                                   META_MONITOR_CONFIG_STORE_ERROR,
c80064
+                                   META_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION))
c80064
+                g_warning ("System monitor configuration file (%s) is "
c80064
+                           "incompatible; ask your administrator to migrate "
c80064
+                           "the system monitor configuration.",
c80064
+                           system_file_path);
c80064
+              else
c80064
+                g_warning ("Failed to read monitors config file '%s': %s",
c80064
+                           system_file_path, error->message);
c80064
+              g_clear_error (&error);
c80064
+            }
c80064
+        }
c80064
+    }
c80064
+
c80064
+  user_file_path = g_build_filename (g_get_user_config_dir (),
c80064
+                                     "monitors.xml",
c80064
+                                     NULL);
c80064
+  config_store->user_file = g_file_new_for_path (user_file_path);
c80064
+
c80064
+  if (g_file_test (user_file_path, G_FILE_TEST_EXISTS))
c80064
+    {
c80064
+      if (!read_config_file (config_store,
c80064
+                             config_store->user_file,
c80064
+                             META_MONITORS_CONFIG_FLAG_NONE,
c80064
+                             &user_configs,
c80064
+                             &error))
c80064
+        {
c80064
+          if (error->domain == META_MONITOR_CONFIG_STORE_ERROR &&
c80064
+              error->code == META_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION)
c80064
+            {
c80064
+              g_clear_error (&error);
c80064
+              if (!meta_migrate_old_user_monitors_config (config_store, &error))
c80064
+                {
c80064
+                  g_warning ("Failed to migrate old monitors config file: %s",
c80064
+                             error->message);
c80064
+                  g_error_free (error);
c80064
+                }
c80064
+            }
c80064
+          else
c80064
+            {
c80064
+              g_warning ("Failed to read monitors config file '%s': %s",
c80064
+                         user_file_path, error->message);
c80064
+              g_error_free (error);
c80064
+            }
c80064
+        }
c80064
+    }
c80064
+
c80064
+  if (config_store->has_stores_policy)
c80064
+    {
c80064
+      GList *l;
c80064
+
c80064
+      for (l = g_list_last (config_store->stores_policy); l; l = l->prev)
c80064
+        {
c80064
+          MetaConfigStore store = GPOINTER_TO_INT (l->data);
c80064
+
c80064
+          switch (store)
c80064
+            {
c80064
+            case META_CONFIG_STORE_SYSTEM:
c80064
+              if (system_configs)
c80064
+                replace_configs (config_store, system_configs);
c80064
+              break;
c80064
+            case META_CONFIG_STORE_USER:
c80064
+              if (user_configs)
c80064
+                replace_configs (config_store, user_configs);
c80064
+            }
c80064
+        }
c80064
+    }
c80064
+  else
c80064
+    {
c80064
+      if (system_configs)
c80064
+        replace_configs (config_store, system_configs);
c80064
+      if (user_configs)
c80064
+        replace_configs (config_store, user_configs);
c80064
+    }
c80064
+
c80064
+
c80064
+  g_free (user_file_path);
c80064
+}
c80064
diff --git a/src/backends/meta-monitor-config-store.h b/src/backends/meta-monitor-config-store.h
c80064
index 92c24ecaa8b6..cb6759dca00f 100644
c80064
--- a/src/backends/meta-monitor-config-store.h
c80064
+++ b/src/backends/meta-monitor-config-store.h
c80064
@@ -26,6 +26,12 @@
c80064
 
c80064
 #include "backends/meta-monitor-config-manager.h"
c80064
 
c80064
+typedef enum _MetaConfigStore
c80064
+{
c80064
+  META_CONFIG_STORE_SYSTEM,
c80064
+  META_CONFIG_STORE_USER,
c80064
+} MetaConfigStore;
c80064
+
c80064
 #define META_TYPE_MONITOR_CONFIG_STORE (meta_monitor_config_store_get_type ())
c80064
 G_DECLARE_FINAL_TYPE (MetaMonitorConfigStore, meta_monitor_config_store,
c80064
                       META, MONITOR_CONFIG_STORE, GObject)
c80064
@@ -46,10 +52,14 @@ void meta_monitor_config_store_remove (MetaMonitorConfigStore *config_store,
c80064
                                        MetaMonitorsConfig     *config);
c80064
 
c80064
 META_EXPORT_TEST
c80064
-gboolean meta_monitor_config_store_set_custom (MetaMonitorConfigStore *config_store,
c80064
-                                               const char             *read_path,
c80064
-                                               const char             *write_path,
c80064
-                                               GError                **error);
c80064
+gboolean meta_monitor_config_store_set_custom (MetaMonitorConfigStore  *config_store,
c80064
+                                               const char              *read_path,
c80064
+                                               const char              *write_path,
c80064
+                                               MetaMonitorsConfigFlag   flags,
c80064
+                                               GError                 **error);
c80064
+
c80064
+META_EXPORT_TEST
c80064
+GList * meta_monitor_config_store_get_stores_policy (MetaMonitorConfigStore *config_store);
c80064
 
c80064
 META_EXPORT_TEST
c80064
 int meta_monitor_config_store_get_config_count (MetaMonitorConfigStore *config_store);
c80064
@@ -57,4 +67,7 @@ int meta_monitor_config_store_get_config_count (MetaMonitorConfigStore *config_s
c80064
 META_EXPORT_TEST
c80064
 MetaMonitorManager * meta_monitor_config_store_get_monitor_manager (MetaMonitorConfigStore *config_store);
c80064
 
c80064
+META_EXPORT_TEST
c80064
+void meta_monitor_config_store_reset (MetaMonitorConfigStore *config_store);
c80064
+
c80064
 #endif /* META_MONITOR_CONFIG_STORE_H */
c80064
diff --git a/src/tests/monitor-config-migration-unit-tests.c b/src/tests/monitor-config-migration-unit-tests.c
c80064
index 461035d6304a..096869c84c2a 100644
c80064
--- a/src/tests/monitor-config-migration-unit-tests.c
c80064
+++ b/src/tests/monitor-config-migration-unit-tests.c
c80064
@@ -55,6 +55,7 @@ test_migration (const char *old_config,
c80064
                                     NULL);
c80064
   if (!meta_monitor_config_store_set_custom (config_store, "/dev/null",
c80064
                                              migrated_path,
c80064
+                                             META_MONITORS_CONFIG_FLAG_NONE,
c80064
                                              &error))
c80064
     g_error ("Failed to set custom config store: %s", error->message);
c80064
 
c80064
diff --git a/src/tests/monitor-configs/policy.xml b/src/tests/monitor-configs/policy.xml
c80064
new file mode 100644
c80064
index 000000000000..760046513e6e
c80064
--- /dev/null
c80064
+++ b/src/tests/monitor-configs/policy.xml
c80064
@@ -0,0 +1,27 @@
c80064
+<monitors version="2">
c80064
+  <policy>
c80064
+    <stores>
c80064
+      <store>system</store>
c80064
+    </stores>
c80064
+  </policy>
c80064
+  <configuration>
c80064
+    <logicalmonitor>
c80064
+      <x>0</x>
c80064
+      <y>0</y>
c80064
+      <primary>yes</primary>
c80064
+      <monitor>
c80064
+	<monitorspec>
c80064
+	  <connector>DP-1</connector>
c80064
+	  <vendor>MetaProduct's Inc.</vendor>
c80064
+	  <product>MetaMonitor</product>
c80064
+	  <serial>0x123456</serial>
c80064
+	</monitorspec>
c80064
+	<mode>
c80064
+	  <width>1920</width>
c80064
+	  <height>1080</height>
c80064
+	  <rate>60</rate>
c80064
+	</mode>
c80064
+      </monitor>
c80064
+    </logicalmonitor>
c80064
+  </configuration>
c80064
+</monitors>
c80064
diff --git a/src/tests/monitor-store-unit-tests.c b/src/tests/monitor-store-unit-tests.c
c80064
index 4ff0e396dda7..478193742ce8 100644
c80064
--- a/src/tests/monitor-store-unit-tests.c
c80064
+++ b/src/tests/monitor-store-unit-tests.c
c80064
@@ -890,6 +890,35 @@ meta_test_monitor_store_unknown_elements (void)
c80064
   check_monitor_configurations (&expect);
c80064
 }
c80064
 
c80064
+static void
c80064
+meta_test_monitor_store_policy_not_allowed (void)
c80064
+{
c80064
+  g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
c80064
+                         "*Policy can only be defined in system level "
c80064
+                         "configurations*");
c80064
+  set_custom_monitor_config ("policy.xml");
c80064
+  g_test_assert_expected_messages ();
c80064
+}
c80064
+
c80064
+static void
c80064
+meta_test_monitor_store_policy (void)
c80064
+{
c80064
+  MetaBackend *backend = meta_get_backend ();
c80064
+  MetaMonitorManager *monitor_manager =
c80064
+    meta_backend_get_monitor_manager (backend);
c80064
+  MetaMonitorConfigManager *config_manager = monitor_manager->config_manager;
c80064
+  MetaMonitorConfigStore *config_store =
c80064
+    meta_monitor_config_manager_get_store (config_manager);
c80064
+  GList *stores_policy;
c80064
+
c80064
+  set_custom_monitor_system_config ("policy.xml");
c80064
+  stores_policy = meta_monitor_config_store_get_stores_policy (config_store);
c80064
+  g_assert_cmpuint (g_list_length (stores_policy), ==, 1);
c80064
+  g_assert_cmpint (GPOINTER_TO_INT (stores_policy->data),
c80064
+                   ==,
c80064
+                   META_CONFIG_STORE_SYSTEM);
c80064
+}
c80064
+
c80064
 void
c80064
 init_monitor_store_tests (void)
c80064
 {
c80064
@@ -917,4 +946,8 @@ init_monitor_store_tests (void)
c80064
                    meta_test_monitor_store_interlaced);
c80064
   g_test_add_func ("/backends/monitor-store/unknown-elements",
c80064
                    meta_test_monitor_store_unknown_elements);
c80064
+  g_test_add_func ("/backends/monitor-store/policy-not-allowed",
c80064
+                   meta_test_monitor_store_policy_not_allowed);
c80064
+  g_test_add_func ("/backends/monitor-store/policy",
c80064
+                   meta_test_monitor_store_policy);
c80064
 }
c80064
diff --git a/src/tests/monitor-test-utils.c b/src/tests/monitor-test-utils.c
c80064
index d2dc3b2658c6..c50fa910e2ad 100644
c80064
--- a/src/tests/monitor-test-utils.c
c80064
+++ b/src/tests/monitor-test-utils.c
c80064
@@ -25,10 +25,24 @@
c80064
 #include "backends/meta-monitor-config-manager.h"
c80064
 #include "backends/meta-monitor-config-store.h"
c80064
 
c80064
+static void
c80064
+set_custom_monitor_config_common (const char             *filename,
c80064
+                                  MetaMonitorsConfigFlag  configs_flags)
c80064
+{
c80064
+  meta_set_custom_monitor_config (meta_get_backend (), filename, configs_flags);
c80064
+}
c80064
+
c80064
 void
c80064
 set_custom_monitor_config (const char *filename)
c80064
 {
c80064
-  meta_set_custom_monitor_config (meta_get_backend (), filename);
c80064
+  set_custom_monitor_config_common (filename, META_MONITORS_CONFIG_FLAG_NONE);
c80064
+}
c80064
+
c80064
+void
c80064
+set_custom_monitor_system_config (const char *filename)
c80064
+{
c80064
+  set_custom_monitor_config_common (filename,
c80064
+                                    META_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG);
c80064
 }
c80064
 
c80064
 char *
c80064
diff --git a/src/tests/monitor-test-utils.h b/src/tests/monitor-test-utils.h
c80064
index 4b9b3f0c790d..2869f271d525 100644
c80064
--- a/src/tests/monitor-test-utils.h
c80064
+++ b/src/tests/monitor-test-utils.h
c80064
@@ -26,6 +26,8 @@ gboolean is_using_monitor_config_manager (void);
c80064
 
c80064
 void set_custom_monitor_config (const char *filename);
c80064
 
c80064
+void set_custom_monitor_system_config (const char *filename);
c80064
+
c80064
 char * read_file (const char *file_path);
c80064
 
c80064
 #endif /* MONITOR_TEST_UTILS_H */
c80064
diff --git a/src/tests/monitor-unit-tests.c b/src/tests/monitor-unit-tests.c
c80064
index 725f84173e86..83066289ebe3 100644
c80064
--- a/src/tests/monitor-unit-tests.c
c80064
+++ b/src/tests/monitor-unit-tests.c
c80064
@@ -5721,6 +5721,7 @@ meta_test_monitor_migrated_rotated (void)
c80064
   if (!meta_monitor_config_store_set_custom (config_store,
c80064
                                              "/dev/null",
c80064
                                              migrated_path,
c80064
+                                             META_MONITORS_CONFIG_FLAG_NONE,
c80064
                                              &error))
c80064
     g_error ("Failed to set custom config store files: %s", error->message);
c80064
 
c80064
@@ -5861,6 +5862,7 @@ meta_test_monitor_migrated_wiggle_discard (void)
c80064
   if (!meta_monitor_config_store_set_custom (config_store,
c80064
                                              "/dev/null",
c80064
                                              migrated_path,
c80064
+                                             META_MONITORS_CONFIG_FLAG_NONE,
c80064
                                              &error))
c80064
     g_error ("Failed to set custom config store files: %s", error->message);
c80064
 
c80064
@@ -6006,6 +6008,7 @@ meta_test_monitor_migrated_wiggle (void)
c80064
   if (!meta_monitor_config_store_set_custom (config_store,
c80064
                                              "/dev/null",
c80064
                                              migrated_path,
c80064
+                                             META_MONITORS_CONFIG_FLAG_NONE,
c80064
                                              &error))
c80064
     g_error ("Failed to set custom config store files: %s", error->message);
c80064
 
c80064
diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c
c80064
index f1a688478f67..23e7d92f91d6 100644
c80064
--- a/src/tests/test-utils.c
c80064
+++ b/src/tests/test-utils.c
c80064
@@ -486,8 +486,9 @@ test_get_plugin_name (void)
c80064
 }
c80064
 
c80064
 void
c80064
-meta_set_custom_monitor_config (MetaBackend *backend,
c80064
-                                const char  *filename)
c80064
+meta_set_custom_monitor_config (MetaBackend            *backend,
c80064
+                                const char             *filename,
c80064
+                                MetaMonitorsConfigFlag  configs_flags)
c80064
 {
c80064
   MetaMonitorManager *monitor_manager =
c80064
     meta_backend_get_monitor_manager (backend);
c80064
@@ -503,8 +504,9 @@ meta_set_custom_monitor_config (MetaBackend *backend,
c80064
   path = g_test_get_filename (G_TEST_DIST, "tests", "monitor-configs",
c80064
                               filename, NULL);
c80064
   if (!meta_monitor_config_store_set_custom (config_store, path, NULL,
c80064
+                                             configs_flags,
c80064
                                              &error))
c80064
-    g_error ("Failed to set custom config: %s", error->message);
c80064
+    g_warning ("Failed to set custom config: %s", error->message);
c80064
 }
c80064
 
c80064
 static void
c80064
diff --git a/src/tests/test-utils.h b/src/tests/test-utils.h
c80064
index 6d6cdb924d01..c426661e38eb 100644
c80064
--- a/src/tests/test-utils.h
c80064
+++ b/src/tests/test-utils.h
c80064
@@ -24,6 +24,7 @@
c80064
 #include <X11/Xlib.h>
c80064
 #include <X11/extensions/sync.h>
c80064
 
c80064
+#include "backends/meta-backend-types.h"
c80064
 #include "meta/window.h"
c80064
 
c80064
 #define TEST_RUNNER_ERROR test_runner_error_quark ()
c80064
@@ -81,8 +82,9 @@ void test_client_destroy (TestClient *client);
c80064
 
c80064
 const char * test_get_plugin_name (void);
c80064
 
c80064
-void meta_set_custom_monitor_config (MetaBackend *backend,
c80064
-                                     const char  *filename);
c80064
+void meta_set_custom_monitor_config (MetaBackend            *backend,
c80064
+                                     const char             *filename,
c80064
+                                     MetaMonitorsConfigFlag  configs_flags);
c80064
 
c80064
 void meta_wait_for_paint (MetaBackend *backend);
c80064
 
c80064
-- 
c80064
2.33.1
c80064
c80064
c80064
From 5e8e431de7d6874d112d6dcee14ad1736d745a3b Mon Sep 17 00:00:00 2001
c80064
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
c80064
Date: Fri, 24 Sep 2021 19:07:32 +0200
c80064
Subject: [PATCH 6/9] tests: Add more monitor config policy parsing tests
c80064
c80064
(cherry picked from commit 48a3ff845fabf0f23568d3c798e047e9b303bffd)
c80064
---
c80064
 .../monitor-configs/policy-duplicate.xml      |  8 ++++
c80064
 src/tests/monitor-configs/policy-empty.xml    |  7 +++
c80064
 src/tests/monitor-configs/policy-invalid.xml  |  8 ++++
c80064
 src/tests/monitor-configs/policy-multiple.xml | 12 +++++
c80064
 src/tests/monitor-store-unit-tests.c          | 44 +++++++++++++++++++
c80064
 5 files changed, 79 insertions(+)
c80064
 create mode 100644 src/tests/monitor-configs/policy-duplicate.xml
c80064
 create mode 100644 src/tests/monitor-configs/policy-empty.xml
c80064
 create mode 100644 src/tests/monitor-configs/policy-invalid.xml
c80064
 create mode 100644 src/tests/monitor-configs/policy-multiple.xml
c80064
c80064
diff --git a/src/tests/monitor-configs/policy-duplicate.xml b/src/tests/monitor-configs/policy-duplicate.xml
c80064
new file mode 100644
c80064
index 000000000000..d93cc81a4906
c80064
--- /dev/null
c80064
+++ b/src/tests/monitor-configs/policy-duplicate.xml
c80064
@@ -0,0 +1,8 @@
c80064
+<monitors version="2">
c80064
+  <policy>
c80064
+    <stores>
c80064
+      <store>user</store>
c80064
+      <store>user</store>
c80064
+    </stores>
c80064
+  </policy>
c80064
+</monitors>
c80064
diff --git a/src/tests/monitor-configs/policy-empty.xml b/src/tests/monitor-configs/policy-empty.xml
c80064
new file mode 100644
c80064
index 000000000000..f56026b66846
c80064
--- /dev/null
c80064
+++ b/src/tests/monitor-configs/policy-empty.xml
c80064
@@ -0,0 +1,7 @@
c80064
+<monitors version="2">
c80064
+  <policy>
c80064
+    <stores>
c80064
+      <store></store>
c80064
+    </stores>
c80064
+  </policy>
c80064
+</monitors>
c80064
diff --git a/src/tests/monitor-configs/policy-invalid.xml b/src/tests/monitor-configs/policy-invalid.xml
c80064
new file mode 100644
c80064
index 000000000000..fc4552fbefc7
c80064
--- /dev/null
c80064
+++ b/src/tests/monitor-configs/policy-invalid.xml
c80064
@@ -0,0 +1,8 @@
c80064
+<monitors version="2">
c80064
+  <policy>
c80064
+    <stores>
c80064
+      <store>user</store>
c80064
+      <store>not-a-store</store>
c80064
+    </stores>
c80064
+  </policy>
c80064
+</monitors>
c80064
diff --git a/src/tests/monitor-configs/policy-multiple.xml b/src/tests/monitor-configs/policy-multiple.xml
c80064
new file mode 100644
c80064
index 000000000000..ffeb79aafe8a
c80064
--- /dev/null
c80064
+++ b/src/tests/monitor-configs/policy-multiple.xml
c80064
@@ -0,0 +1,12 @@
c80064
+<monitors version="2">
c80064
+  <policy>
c80064
+    <stores>
c80064
+      <store>user</store>
c80064
+      <store>system</store>
c80064
+    </stores>
c80064
+    <stores>
c80064
+      <store>system</store>
c80064
+      <store>user</store>
c80064
+    </stores>
c80064
+  </policy>
c80064
+</monitors>
c80064
diff --git a/src/tests/monitor-store-unit-tests.c b/src/tests/monitor-store-unit-tests.c
c80064
index 478193742ce8..50c220737b20 100644
c80064
--- a/src/tests/monitor-store-unit-tests.c
c80064
+++ b/src/tests/monitor-store-unit-tests.c
c80064
@@ -919,6 +919,42 @@ meta_test_monitor_store_policy (void)
c80064
                    META_CONFIG_STORE_SYSTEM);
c80064
 }
c80064
 
c80064
+static void
c80064
+meta_test_monitor_store_policy_empty (void)
c80064
+{
c80064
+  g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
c80064
+                         "*Invalid store*");
c80064
+  set_custom_monitor_system_config ("policy-empty.xml");
c80064
+  g_test_assert_expected_messages ();
c80064
+}
c80064
+
c80064
+static void
c80064
+meta_test_monitor_store_policy_duplicate (void)
c80064
+{
c80064
+  g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
c80064
+                         "*Multiple identical stores*");
c80064
+  set_custom_monitor_system_config ("policy-duplicate.xml");
c80064
+  g_test_assert_expected_messages ();
c80064
+}
c80064
+
c80064
+static void
c80064
+meta_test_monitor_store_policy_invalid (void)
c80064
+{
c80064
+  g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
c80064
+                         "*Invalid store*");
c80064
+  set_custom_monitor_system_config ("policy-invalid.xml");
c80064
+  g_test_assert_expected_messages ();
c80064
+}
c80064
+
c80064
+static void
c80064
+meta_test_monitor_store_policy_multiple (void)
c80064
+{
c80064
+  g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
c80064
+                         "*Multiple stores elements under policy*");
c80064
+  set_custom_monitor_system_config ("policy-multiple.xml");
c80064
+  g_test_assert_expected_messages ();
c80064
+}
c80064
+
c80064
 void
c80064
 init_monitor_store_tests (void)
c80064
 {
c80064
@@ -950,4 +986,12 @@ init_monitor_store_tests (void)
c80064
                    meta_test_monitor_store_policy_not_allowed);
c80064
   g_test_add_func ("/backends/monitor-store/policy",
c80064
                    meta_test_monitor_store_policy);
c80064
+  g_test_add_func ("/backends/monitor-store/policy-empty",
c80064
+                   meta_test_monitor_store_policy_empty);
c80064
+  g_test_add_func ("/backends/monitor-store/policy-duplicate",
c80064
+                   meta_test_monitor_store_policy_duplicate);
c80064
+  g_test_add_func ("/backends/monitor-store/policy-invalid",
c80064
+                   meta_test_monitor_store_policy_invalid);
c80064
+  g_test_add_func ("/backends/monitor-store/policy-multiple",
c80064
+                   meta_test_monitor_store_policy_multiple);
c80064
 }
c80064
-- 
c80064
2.33.1
c80064
c80064
c80064
From a8f7700df399481d7bbbe42d13127e38dc193143 Mon Sep 17 00:00:00 2001
c80064
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
c80064
Date: Thu, 30 Sep 2021 17:32:31 +0200
c80064
Subject: [PATCH 7/9] monitor-config-store: Add test for monitor configuration
c80064
 policy
c80064
c80064
The test aims to verify that setting the following policy
c80064
c80064
    <policy>
c80064
      <stores>
c80064
        <store>system</store>
c80064
      </stores>
c80064
    </policy>
c80064
c80064
only applies monitor configurations from the system level.
c80064
c80064
(cherry picked from commit 9969a8aa25623dbff51e120d85ab202026571bb1)
c80064
---
c80064
 src/tests/monitor-configs/system/monitors.xml |  27 ++++
c80064
 src/tests/monitor-configs/user/monitors.xml   |  22 +++
c80064
 src/tests/monitor-store-unit-tests.c          |  17 +++
c80064
 src/tests/monitor-unit-tests.c                | 132 ++++++++++++++++++
c80064
 4 files changed, 198 insertions(+)
c80064
 create mode 100644 src/tests/monitor-configs/system/monitors.xml
c80064
 create mode 100644 src/tests/monitor-configs/user/monitors.xml
c80064
c80064
diff --git a/src/tests/monitor-configs/system/monitors.xml b/src/tests/monitor-configs/system/monitors.xml
c80064
new file mode 100644
c80064
index 000000000000..4d2eafec1327
c80064
--- /dev/null
c80064
+++ b/src/tests/monitor-configs/system/monitors.xml
c80064
@@ -0,0 +1,27 @@
c80064
+<monitors version="2">
c80064
+  <policy>
c80064
+    <stores>
c80064
+      <store>system</store>
c80064
+    </stores>
c80064
+  </policy>
c80064
+  <configuration>
c80064
+    <logicalmonitor>
c80064
+      <x>0</x>
c80064
+      <y>0</y>
c80064
+      <primary>yes</primary>
c80064
+      <monitor>
c80064
+	<monitorspec>
c80064
+	  <connector>DP-1</connector>
c80064
+	  <vendor>MetaProduct's Inc.</vendor>
c80064
+	  <product>MetaMonitor</product>
c80064
+	  <serial>0x123456</serial>
c80064
+	</monitorspec>
c80064
+	<mode>
c80064
+	  <width>640</width>
c80064
+	  <height>480</height>
c80064
+	  <rate>60</rate>
c80064
+	</mode>
c80064
+      </monitor>
c80064
+    </logicalmonitor>
c80064
+  </configuration>
c80064
+</monitors>
c80064
diff --git a/src/tests/monitor-configs/user/monitors.xml b/src/tests/monitor-configs/user/monitors.xml
c80064
new file mode 100644
c80064
index 000000000000..f125972e01e7
c80064
--- /dev/null
c80064
+++ b/src/tests/monitor-configs/user/monitors.xml
c80064
@@ -0,0 +1,22 @@
c80064
+<monitors version="2">
c80064
+  <configuration>
c80064
+    <logicalmonitor>
c80064
+      <x>0</x>
c80064
+      <y>0</y>
c80064
+      <primary>yes</primary>
c80064
+      <monitor>
c80064
+	<monitorspec>
c80064
+	  <connector>DP-1</connector>
c80064
+	  <vendor>MetaProduct's Inc.</vendor>
c80064
+	  <product>MetaMonitor</product>
c80064
+	  <serial>0x123456</serial>
c80064
+	</monitorspec>
c80064
+	<mode>
c80064
+	  <width>800</width>
c80064
+	  <height>600</height>
c80064
+	  <rate>60</rate>
c80064
+	</mode>
c80064
+      </monitor>
c80064
+    </logicalmonitor>
c80064
+  </configuration>
c80064
+</monitors>
c80064
diff --git a/src/tests/monitor-store-unit-tests.c b/src/tests/monitor-store-unit-tests.c
c80064
index 50c220737b20..20860e7aa510 100644
c80064
--- a/src/tests/monitor-store-unit-tests.c
c80064
+++ b/src/tests/monitor-store-unit-tests.c
c80064
@@ -958,6 +958,23 @@ meta_test_monitor_store_policy_multiple (void)
c80064
 void
c80064
 init_monitor_store_tests (void)
c80064
 {
c80064
+  char *path;
c80064
+
c80064
+  path = g_test_build_filename (G_TEST_DIST,
c80064
+                                "tests",
c80064
+                                "monitor-configs",
c80064
+                                "system",
c80064
+                                NULL);
c80064
+  g_setenv ("XDG_CONFIG_DIRS", path, TRUE);
c80064
+  g_free (path);
c80064
+  path = g_test_build_filename (G_TEST_DIST,
c80064
+                                "tests",
c80064
+                                "monitor-configs",
c80064
+                                "user",
c80064
+                                NULL);
c80064
+  g_setenv ("XDG_CONFIG_HOME", path, TRUE);
c80064
+  g_free (path);
c80064
+
c80064
   g_test_add_func ("/backends/monitor-store/single",
c80064
                    meta_test_monitor_store_single);
c80064
   g_test_add_func ("/backends/monitor-store/vertical",
c80064
diff --git a/src/tests/monitor-unit-tests.c b/src/tests/monitor-unit-tests.c
c80064
index 83066289ebe3..5401edbc6d7c 100644
c80064
--- a/src/tests/monitor-unit-tests.c
c80064
+++ b/src/tests/monitor-unit-tests.c
c80064
@@ -6043,6 +6043,135 @@ meta_test_monitor_migrated_wiggle (void)
c80064
     g_error ("Failed to remove test data output file: %s", error->message);
c80064
 }
c80064
 
c80064
+static void
c80064
+meta_test_monitor_policy_system_only (void)
c80064
+{
c80064
+  MetaMonitorTestSetup *test_setup;
c80064
+  MonitorTestCase test_case = {
c80064
+    .setup = {
c80064
+      .modes = {
c80064
+        {
c80064
+          .width = 1024,
c80064
+          .height = 768,
c80064
+          .refresh_rate = 60.0
c80064
+        },
c80064
+        {
c80064
+          .width = 800,
c80064
+          .height = 600,
c80064
+          .refresh_rate = 60.0
c80064
+        },
c80064
+        {
c80064
+          .width = 640,
c80064
+          .height = 480,
c80064
+          .refresh_rate = 60.0
c80064
+        }
c80064
+      },
c80064
+      .n_modes = 3,
c80064
+      .outputs = {
c80064
+         {
c80064
+          .crtc = 0,
c80064
+          .modes = { 0, 1, 2 },
c80064
+          .n_modes = 3,
c80064
+          .preferred_mode = 0,
c80064
+          .possible_crtcs = { 0 },
c80064
+          .n_possible_crtcs = 1,
c80064
+          .width_mm = 222,
c80064
+          .height_mm = 125
c80064
+        },
c80064
+      },
c80064
+      .n_outputs = 1,
c80064
+      .crtcs = {
c80064
+        {
c80064
+          .current_mode = 0
c80064
+        }
c80064
+      },
c80064
+      .n_crtcs = 1
c80064
+    },
c80064
+
c80064
+    .expect = {
c80064
+      .monitors = {
c80064
+        {
c80064
+          .outputs = { 0 },
c80064
+          .n_outputs = 1,
c80064
+          .modes = {
c80064
+            {
c80064
+              .width = 1024,
c80064
+              .height = 768,
c80064
+              .refresh_rate = 60.0,
c80064
+              .crtc_modes = {
c80064
+                {
c80064
+                  .output = 0,
c80064
+                  .crtc_mode = 0
c80064
+                }
c80064
+              }
c80064
+            },
c80064
+            {
c80064
+              .width = 800,
c80064
+              .height = 600,
c80064
+              .refresh_rate = 60.0,
c80064
+              .crtc_modes = {
c80064
+                {
c80064
+                  .output = 0,
c80064
+                  .crtc_mode = 1
c80064
+                }
c80064
+              }
c80064
+            },
c80064
+            {
c80064
+              .width = 640,
c80064
+              .height = 480,
c80064
+              .refresh_rate = 60.0,
c80064
+              .crtc_modes = {
c80064
+                {
c80064
+                  .output = 0,
c80064
+                  .crtc_mode = 2
c80064
+                }
c80064
+              }
c80064
+            }
c80064
+          },
c80064
+          .n_modes = 3,
c80064
+          .current_mode = 2,
c80064
+          .width_mm = 222,
c80064
+          .height_mm = 125
c80064
+        },
c80064
+      },
c80064
+      .n_monitors = 1,
c80064
+      .logical_monitors = {
c80064
+        {
c80064
+          .monitors = { 0 },
c80064
+          .n_monitors = 1,
c80064
+          .layout = { .x = 0, .y = 0, .width = 640, .height = 480 },
c80064
+          .scale = 1
c80064
+        },
c80064
+      },
c80064
+      .n_logical_monitors = 1,
c80064
+      .primary_logical_monitor = 0,
c80064
+      .n_outputs = 1,
c80064
+      .crtcs = {
c80064
+        {
c80064
+          .current_mode = 2,
c80064
+          .x = 0,
c80064
+        }
c80064
+      },
c80064
+      .n_crtcs = 1,
c80064
+      .screen_width = 640,
c80064
+      .screen_height = 480,
c80064
+    }
c80064
+  };
c80064
+  MetaBackend *backend = meta_get_backend ();
c80064
+  MetaMonitorManager *monitor_manager =
c80064
+    meta_backend_get_monitor_manager (backend);
c80064
+  MetaMonitorConfigManager *config_manager = monitor_manager->config_manager;
c80064
+  MetaMonitorConfigStore *config_store =
c80064
+    meta_monitor_config_manager_get_store (config_manager);
c80064
+
c80064
+  test_setup = create_monitor_test_setup (&test_case.setup,
c80064
+                                          MONITOR_TEST_FLAG_NONE);
c80064
+
c80064
+  meta_monitor_config_store_reset (config_store);
c80064
+  emulate_hotplug (test_setup);
c80064
+  check_monitor_configuration (&test_case);
c80064
+}
c80064
+
c80064
 static void
c80064
 test_case_setup (void       **fixture,
c80064
                  const void   *data)
c80064
@@ -6159,6 +6288,9 @@ init_monitor_tests (void)
c80064
                     meta_test_monitor_migrated_wiggle);
c80064
   add_monitor_test ("/backends/monitor/migrated/wiggle-discard",
c80064
                     meta_test_monitor_migrated_wiggle_discard);
c80064
+
c80064
+  add_monitor_test ("/backends/monitor/policy/system-only",
c80064
+                    meta_test_monitor_policy_system_only);
c80064
 }
c80064
 
c80064
 void
c80064
-- 
c80064
2.33.1
c80064
c80064
c80064
From e4de67a35a70cfe0886b1932c442517ccfa0ca93 Mon Sep 17 00:00:00 2001
c80064
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
c80064
Date: Thu, 30 Sep 2021 21:06:38 +0200
c80064
Subject: [PATCH 8/9] monitor-config-store: Allow changing D-Bus configuration
c80064
 policy
c80064
c80064
Adding a <dbus/> element containing a boolean (yes/no) determines
c80064
whether org.gnome.Mutter.DisplayConfig ApplyMonitorsConfig will be
c80064
callable. The state is also introspectable via the
c80064
ApplyMonitorsConfigAllowed property on the same interface.
c80064
c80064
For example
c80064
c80064
    <monitors version="2">
c80064
      <policy>
c80064
        <dbus>no</dbus>
c80064
      </policy>
c80064
    </monitors>
c80064
c80064
(cherry picked from commit f2c7ae821b7af7e732e588e7548238dd814e5f84)
c80064
---
c80064
 src/backends/meta-monitor-config-store.c      | 68 +++++++++++++++++++
c80064
 src/backends/meta-monitor-config-store.h      |  8 +++
c80064
 src/backends/meta-monitor-manager-private.h   |  1 +
c80064
 src/backends/meta-monitor-manager.c           | 24 +++++++
c80064
 src/org.gnome.Mutter.DisplayConfig.xml        |  7 ++
c80064
 .../monitor-configs/policy-dbus-invalid.xml   |  6 ++
c80064
 src/tests/monitor-configs/policy-dbus.xml     |  5 ++
c80064
 src/tests/monitor-store-unit-tests.c          | 49 +++++++++++++
c80064
 8 files changed, 168 insertions(+)
c80064
 create mode 100644 src/tests/monitor-configs/policy-dbus-invalid.xml
c80064
 create mode 100644 src/tests/monitor-configs/policy-dbus.xml
c80064
c80064
diff --git a/src/backends/meta-monitor-config-store.c b/src/backends/meta-monitor-config-store.c
c80064
index b320746a4418..cc866587c219 100644
c80064
--- a/src/backends/meta-monitor-config-store.c
c80064
+++ b/src/backends/meta-monitor-config-store.c
c80064
@@ -123,6 +123,9 @@ struct _MetaMonitorConfigStore
c80064
 
c80064
   gboolean has_stores_policy;
c80064
   GList *stores_policy;
c80064
+
c80064
+  gboolean has_dbus_policy;
c80064
+  MetaMonitorConfigPolicy policy;
c80064
 };
c80064
 
c80064
 #define META_MONITOR_CONFIG_STORE_ERROR (meta_monitor_config_store_error_quark ())
c80064
@@ -168,6 +171,7 @@ typedef enum
c80064
   STATE_POLICY,
c80064
   STATE_STORES,
c80064
   STATE_STORE,
c80064
+  STATE_DBUS,
c80064
 } ParserState;
c80064
 
c80064
 typedef struct
c80064
@@ -191,9 +195,13 @@ typedef struct
c80064
   GList *current_disabled_monitor_specs;
c80064
   gboolean seen_policy;
c80064
   gboolean seen_stores;
c80064
+  gboolean seen_dbus;
c80064
   MetaConfigStore pending_store;
c80064
   GList *stores;
c80064
 
c80064
+  gboolean enable_dbus_set;
c80064
+  gboolean enable_dbus;
c80064
+
c80064
   ParserState unknown_state_root;
c80064
   int unknown_level;
c80064
 
c80064
@@ -574,6 +582,19 @@ handle_start_element (GMarkupParseContext  *context,
c80064
             parser->seen_stores = TRUE;
c80064
             parser->state = STATE_STORES;
c80064
           }
c80064
+        else if (g_str_equal (element_name, "dbus"))
c80064
+          {
c80064
+            if (parser->seen_dbus)
c80064
+              {
c80064
+                g_set_error (error,
c80064
+                             G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
c80064
+                             "Multiple dbus elements under policy");
c80064
+                return;
c80064
+              }
c80064
+
c80064
+            parser->seen_dbus = TRUE;
c80064
+            parser->state = STATE_DBUS;
c80064
+          }
c80064
         else
c80064
           {
c80064
             enter_unknown_element (parser, element_name,
c80064
@@ -604,6 +625,13 @@ handle_start_element (GMarkupParseContext  *context,
c80064
                      "Invalid store sub element '%s'", element_name);
c80064
         return;
c80064
       }
c80064
+
c80064
+    case STATE_DBUS:
c80064
+      {
c80064
+        g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
c80064
+                     "Invalid dbus sub element '%s'", element_name);
c80064
+        return;
c80064
+      }
c80064
     }
c80064
 }
c80064
 
c80064
@@ -953,6 +981,23 @@ handle_end_element (GMarkupParseContext  *context,
c80064
         parser->state = STATE_POLICY;
c80064
         return;
c80064
 
c80064
+    case STATE_DBUS:
c80064
+        if (!parser->config_store->has_dbus_policy)
c80064
+          {
c80064
+            parser->config_store->has_dbus_policy = TRUE;
c80064
+            parser->config_store->policy.enable_dbus = parser->enable_dbus;
c80064
+            parser->enable_dbus_set = FALSE;
c80064
+          }
c80064
+        else
c80064
+          {
c80064
+            g_warning ("Policy for monitor configuration via D-Bus "
c80064
+                       "has already been set, ignoring policy from '%s'",
c80064
+                       g_file_get_path (parser->file));
c80064
+          }
c80064
+        parser->state = STATE_POLICY;
c80064
+
c80064
+        return;
c80064
+
c80064
     case STATE_POLICY:
c80064
         g_assert (g_str_equal (element_name, "policy"));
c80064
 
c80064
@@ -1285,6 +1330,15 @@ handle_text (GMarkupParseContext *context,
c80064
         parser->pending_store = store;
c80064
         return;
c80064
       }
c80064
+
c80064
+    case STATE_DBUS:
c80064
+      {
c80064
+        parser->enable_dbus_set = TRUE;
c80064
+        read_bool (text, text_len,
c80064
+                   &parser->enable_dbus,
c80064
+                   error);
c80064
+        return;
c80064
+      }
c80064
     }
c80064
 }
c80064
 
c80064
@@ -1628,6 +1682,11 @@ meta_monitor_config_store_save (MetaMonitorConfigStore *config_store)
c80064
       return;
c80064
     }
c80064
 
c80064
+  if (config_store->has_stores_policy &&
c80064
+      !g_list_find (config_store->stores_policy,
c80064
+                    GINT_TO_POINTER (META_CONFIG_STORE_USER)))
c80064
+    return;
c80064
+
c80064
   config_store->save_cancellable = g_cancellable_new ();
c80064
 
c80064
   buffer = generate_config_xml (config_store);
c80064
@@ -1704,6 +1763,8 @@ meta_monitor_config_store_set_custom (MetaMonitorConfigStore  *config_store,
c80064
 
c80064
   g_clear_pointer (&config_store->stores_policy, g_list_free);
c80064
   config_store->has_stores_policy = FALSE;
c80064
+  config_store->policy.enable_dbus = TRUE;
c80064
+  config_store->has_dbus_policy = FALSE;
c80064
 
c80064
   if (!read_config_file (config_store,
c80064
                          config_store->custom_read_file,
c80064
@@ -1819,6 +1880,7 @@ meta_monitor_config_store_init (MetaMonitorConfigStore *config_store)
c80064
                                                  meta_monitors_config_key_equal,
c80064
                                                  NULL,
c80064
                                                  g_object_unref);
c80064
+  config_store->policy.enable_dbus = TRUE;
c80064
 }
c80064
 
c80064
 static void
c80064
@@ -1972,3 +2034,9 @@ meta_monitor_config_store_reset (MetaMonitorConfigStore *config_store)
c80064
 
c80064
   g_free (user_file_path);
c80064
 }
c80064
+
c80064
+const MetaMonitorConfigPolicy *
c80064
+meta_monitor_config_store_get_policy (MetaMonitorConfigStore *config_store)
c80064
+{
c80064
+  return &config_store->policy;
c80064
+}
c80064
diff --git a/src/backends/meta-monitor-config-store.h b/src/backends/meta-monitor-config-store.h
c80064
index cb6759dca00f..a255e370baaf 100644
c80064
--- a/src/backends/meta-monitor-config-store.h
c80064
+++ b/src/backends/meta-monitor-config-store.h
c80064
@@ -32,6 +32,11 @@ typedef enum _MetaConfigStore
c80064
   META_CONFIG_STORE_USER,
c80064
 } MetaConfigStore;
c80064
 
c80064
+typedef struct _MetaMonitorConfigPolicy
c80064
+{
c80064
+  gboolean enable_dbus;
c80064
+} MetaMonitorConfigPolicy;
c80064
+
c80064
 #define META_TYPE_MONITOR_CONFIG_STORE (meta_monitor_config_store_get_type ())
c80064
 G_DECLARE_FINAL_TYPE (MetaMonitorConfigStore, meta_monitor_config_store,
c80064
                       META, MONITOR_CONFIG_STORE, GObject)
c80064
@@ -70,4 +75,7 @@ MetaMonitorManager * meta_monitor_config_store_get_monitor_manager (MetaMonitorC
c80064
 META_EXPORT_TEST
c80064
 void meta_monitor_config_store_reset (MetaMonitorConfigStore *config_store);
c80064
 
c80064
+META_EXPORT_TEST
c80064
+const MetaMonitorConfigPolicy * meta_monitor_config_store_get_policy (MetaMonitorConfigStore *config_store);
c80064
+
c80064
 #endif /* META_MONITOR_CONFIG_STORE_H */
c80064
diff --git a/src/backends/meta-monitor-manager-private.h b/src/backends/meta-monitor-manager-private.h
c80064
index 223b5dfbda31..030eaf7b9ea1 100644
c80064
--- a/src/backends/meta-monitor-manager-private.h
c80064
+++ b/src/backends/meta-monitor-manager-private.h
c80064
@@ -383,6 +383,7 @@ gboolean           meta_monitor_manager_get_max_screen_size (MetaMonitorManager
c80064
 MetaLogicalMonitorLayoutMode
c80064
                    meta_monitor_manager_get_default_layout_mode (MetaMonitorManager *manager);
c80064
 
c80064
+META_EXPORT_TEST
c80064
 MetaMonitorConfigManager *
c80064
                    meta_monitor_manager_get_config_manager (MetaMonitorManager *manager);
c80064
 
c80064
diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c
c80064
index 0adf2100db18..f00e7c2abaad 100644
c80064
--- a/src/backends/meta-monitor-manager.c
c80064
+++ b/src/backends/meta-monitor-manager.c
c80064
@@ -51,6 +51,7 @@
c80064
 #include "backends/meta-logical-monitor.h"
c80064
 #include "backends/meta-monitor.h"
c80064
 #include "backends/meta-monitor-config-manager.h"
c80064
+#include "backends/meta-monitor-config-store.h"
c80064
 #include "backends/meta-orientation-manager.h"
c80064
 #include "backends/meta-output.h"
c80064
 #include "backends/x11/meta-monitor-manager-xrandr.h"
c80064
@@ -776,9 +777,18 @@ experimental_features_changed (MetaSettings           *settings,
c80064
 void
c80064
 meta_monitor_manager_setup (MetaMonitorManager *manager)
c80064
 {
c80064
+  MetaMonitorConfigStore *config_store;
c80064
+  const MetaMonitorConfigPolicy *policy;
c80064
+
c80064
   manager->in_init = TRUE;
c80064
 
c80064
   manager->config_manager = meta_monitor_config_manager_new (manager);
c80064
+  config_store =
c80064
+    meta_monitor_config_manager_get_store (manager->config_manager);
c80064
+  policy = meta_monitor_config_store_get_policy (config_store);
c80064
+  meta_dbus_display_config_set_apply_monitors_config_allowed (manager->display_config,
c80064
+                                                              policy->enable_dbus);
c80064
+
c80064
 
c80064
   meta_monitor_manager_read_current_state (manager);
c80064
 
c80064
@@ -2033,6 +2043,8 @@ meta_monitor_manager_handle_apply_monitors_config (MetaDBusDisplayConfig *skelet
c80064
                                                    GVariant              *properties_variant,
c80064
                                                    MetaMonitorManager    *manager)
c80064
 {
c80064
+  MetaMonitorConfigStore *config_store;
c80064
+  const MetaMonitorConfigPolicy *policy;
c80064
   MetaMonitorManagerCapability capabilities;
c80064
   GVariant *layout_mode_variant = NULL;
c80064
   MetaLogicalMonitorLayoutMode layout_mode;
c80064
@@ -2049,6 +2061,18 @@ meta_monitor_manager_handle_apply_monitors_config (MetaDBusDisplayConfig *skelet
c80064
       return TRUE;
c80064
     }
c80064
 
c80064
+  config_store =
c80064
+    meta_monitor_config_manager_get_store (manager->config_manager);
c80064
+  policy = meta_monitor_config_store_get_policy (config_store);
c80064
+
c80064
+  if (!policy->enable_dbus)
c80064
+    {
c80064
+      g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
c80064
+                                             G_DBUS_ERROR_ACCESS_DENIED,
c80064
+                                             "Monitor configuration via D-Bus is disabled");
c80064
+      return TRUE;
c80064
+    }
c80064
+
c80064
   capabilities = meta_monitor_manager_get_capabilities (manager);
c80064
 
c80064
   if (properties_variant)
c80064
diff --git a/src/org.gnome.Mutter.DisplayConfig.xml b/src/org.gnome.Mutter.DisplayConfig.xml
c80064
index 3abfa15d7816..07e2e58df758 100644
c80064
--- a/src/org.gnome.Mutter.DisplayConfig.xml
c80064
+++ b/src/org.gnome.Mutter.DisplayConfig.xml
c80064
@@ -282,6 +282,13 @@
c80064
     -->
c80064
     <property name="PowerSaveMode" type="i" access="readwrite" />
c80064
 
c80064
+    
c80064
+        ApplyMonitorsConfigAllowed:
c80064
+
c80064
+        Whether calling the ApplyMonitorsConfig method is allowed.
c80064
+    -->
c80064
+    <property name="ApplyMonitorsConfigAllowed" type="b" access="read" />
c80064
+
c80064
     
c80064
         MonitorsChanged:
c80064
 
c80064
diff --git a/src/tests/monitor-configs/policy-dbus-invalid.xml b/src/tests/monitor-configs/policy-dbus-invalid.xml
c80064
new file mode 100644
c80064
index 000000000000..67fa6045ff26
c80064
--- /dev/null
c80064
+++ b/src/tests/monitor-configs/policy-dbus-invalid.xml
c80064
@@ -0,0 +1,6 @@
c80064
+<monitors version="2">
c80064
+  <policy>
c80064
+    <dbus>no</dbus>
c80064
+    <dbus>yes</dbus>
c80064
+  </policy>
c80064
+</monitors>
c80064
diff --git a/src/tests/monitor-configs/policy-dbus.xml b/src/tests/monitor-configs/policy-dbus.xml
c80064
new file mode 100644
c80064
index 000000000000..c407e1f02194
c80064
--- /dev/null
c80064
+++ b/src/tests/monitor-configs/policy-dbus.xml
c80064
@@ -0,0 +1,5 @@
c80064
+<monitors version="2">
c80064
+  <policy>
c80064
+    <dbus>no</dbus>
c80064
+  </policy>
c80064
+</monitors>
c80064
diff --git a/src/tests/monitor-store-unit-tests.c b/src/tests/monitor-store-unit-tests.c
c80064
index 20860e7aa510..1730dd9d6838 100644
c80064
--- a/src/tests/monitor-store-unit-tests.c
c80064
+++ b/src/tests/monitor-store-unit-tests.c
c80064
@@ -955,6 +955,51 @@ meta_test_monitor_store_policy_multiple (void)
c80064
   g_test_assert_expected_messages ();
c80064
 }
c80064
 
c80064
+static void
c80064
+meta_test_monitor_store_policy_dbus (void)
c80064
+{
c80064
+  MetaBackend *backend = meta_get_backend ();
c80064
+  MetaMonitorManager *monitor_manager =
c80064
+    meta_backend_get_monitor_manager (backend);
c80064
+  MetaMonitorConfigManager *config_manager =
c80064
+    meta_monitor_manager_get_config_manager (monitor_manager);
c80064
+  MetaMonitorConfigStore *config_store =
c80064
+    meta_monitor_config_manager_get_store (config_manager);
c80064
+  const MetaMonitorConfigPolicy *policy;
c80064
+
c80064
+  policy = meta_monitor_config_store_get_policy (config_store);
c80064
+  g_assert_nonnull (policy);
c80064
+  g_assert_cmpint (policy->enable_dbus, ==, TRUE);
c80064
+
c80064
+  set_custom_monitor_system_config ("policy-dbus.xml");
c80064
+
c80064
+  policy = meta_monitor_config_store_get_policy (config_store);
c80064
+  g_assert_nonnull (policy);
c80064
+  g_assert_cmpint (policy->enable_dbus, ==, FALSE);
c80064
+}
c80064
+
c80064
+static void
c80064
+meta_test_monitor_store_policy_dbus_invalid (void)
c80064
+{
c80064
+  MetaBackend *backend = meta_get_backend ();
c80064
+  MetaMonitorManager *monitor_manager =
c80064
+    meta_backend_get_monitor_manager (backend);
c80064
+  MetaMonitorConfigManager *config_manager =
c80064
+    meta_monitor_manager_get_config_manager (monitor_manager);
c80064
+  MetaMonitorConfigStore *config_store =
c80064
+    meta_monitor_config_manager_get_store (config_manager);
c80064
+  const MetaMonitorConfigPolicy *policy;
c80064
+
c80064
+  g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
c80064
+                         "*Multiple dbus elements under policy*");
c80064
+  set_custom_monitor_system_config ("policy-dbus-invalid.xml");
c80064
+  g_test_assert_expected_messages ();
c80064
+
c80064
+  policy = meta_monitor_config_store_get_policy (config_store);
c80064
+  g_assert_nonnull (policy);
c80064
+  g_assert_cmpint (policy->enable_dbus, ==, FALSE);
c80064
+}
c80064
+
c80064
 void
c80064
 init_monitor_store_tests (void)
c80064
 {
c80064
@@ -1011,4 +1056,8 @@ init_monitor_store_tests (void)
c80064
                    meta_test_monitor_store_policy_invalid);
c80064
   g_test_add_func ("/backends/monitor-store/policy-multiple",
c80064
                    meta_test_monitor_store_policy_multiple);
c80064
+  g_test_add_func ("/backends/monitor-store/dbus",
c80064
+                   meta_test_monitor_store_policy_dbus);
c80064
+  g_test_add_func ("/backends/monitor-store/dbus-invalid",
c80064
+                   meta_test_monitor_store_policy_dbus_invalid);
c80064
 }
c80064
-- 
c80064
2.33.1
c80064
c80064
c80064
From b39016a5a782f132f380591ab98c7f3eac6af861 Mon Sep 17 00:00:00 2001
c80064
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
c80064
Date: Fri, 1 Oct 2021 09:52:15 +0200
c80064
Subject: [PATCH 9/9] doc: Add monitor configuration documentation
c80064
c80064
This describes how the `monitors.xml` file work, and how to override the
c80064
newly introduced configurable policy.
c80064
c80064
(cherry picked from commit 3bbb53db3da07c0e944903ee8dede722cd082d34)
c80064
---
c80064
 doc/monitor-configuration.md | 105 +++++++++++++++++++++++++++++++++++
c80064
 1 file changed, 105 insertions(+)
c80064
 create mode 100644 doc/monitor-configuration.md
c80064
c80064
diff --git a/doc/monitor-configuration.md b/doc/monitor-configuration.md
c80064
new file mode 100644
c80064
index 000000000000..46c1078379cb
c80064
--- /dev/null
c80064
+++ b/doc/monitor-configuration.md
c80064
@@ -0,0 +1,105 @@
c80064
+Monitor configuration
c80064
+=====================
c80064
+
c80064
+File locations
c80064
+--------------
c80064
+
c80064
+Monitor configurations are stored as XML files called `monitors.xml` on the file
c80064
+system. There are two types of locations for the XML file: the system level and
c80064
+the user level.
c80064
+
c80064
+The directories for system level configuration is defined in accordance to the
c80064
+$XDG_CONFIG_DIRS environment variable defined in the XDG Base Directory
c80064
+Specification. The default is `/etc/xdg/monitors.xml`.
c80064
+
c80064
+The directory for the user level configuration is defined in accordance to the
c80064
+$XDG_CONFIG_HOME environment variable defined in the XDG Base Directory
c80064
+Specification. The default is `~/.config/monitors.xml`
c80064
+
c80064
+File contents
c80064
+-------------
c80064
+
c80064
+A configuration file consists of an XML document with the root element
c80064
+`<monitors version="2">`. In this document multiple configurations are stored as
c80064
+individual `<configuration/>` elements containing all the details of the monitor
c80064
+setup. The `version` attribute must be set to `"2"`.
c80064
+
c80064
+Each configuration corresponds to a specific hardware setup, where a given set
c80064
+of monitors are connected to the computer. There can only be one configuration
c80064
+per hardware setup.
c80064
+
c80064
+Writing configuration
c80064
+---------------------
c80064
+
c80064
+Monitor configurations are managed by Mutter via the Display panel in Settings,
c80064
+which uses a D-Bus API to communicate with Mutter. Each time a new configuration
c80064
+is applied and accepted, the user level configuration file is replaced with
c80064
+updated content.
c80064
+
c80064
+Previously defined monitor configurations for hardware state other than the
c80064
+current are left intact.
c80064
+
c80064
+Configuration policy
c80064
+--------------------
c80064
+
c80064
+The monitor configuration policy determines how Mutter configures monitors. This
c80064
+can mean for example in what order configuration files should be preferred, or
c80064
+whether configuration via Settings (i.e. D-Bus) should be allowed.
c80064
+
c80064
+The default policy is to prioritize configurations defined in the user level
c80064
+configuration file, and to allow configuring via D-Bus.
c80064
+
c80064
+Changing the policy is possible by manually adding a `<policy/>` element inside
c80064
+the `<monitors version="2"/>` element in the `monitors.xml` file. Note that
c80064
+there may only be one `<policy/>` element in each configuration file.
c80064
+
c80064
+### Changing configuration file priority policy
c80064
+
c80064
+To change the order of configuration file priority, or to disable configuration
c80064
+files completely, add a `<stores/>` element inside the `<policy/>` element
c80064
+described above.
c80064
+
c80064
+In this element, the file policy is defined by a `<stores/>` element, which
c80064
+lists stores with the order according to prioritization. Each store is specified
c80064
+using a `<store/>` element with either `system` or `user` as the content.
c80064
+
c80064
+#### Example of only reading monitor configuration from the system level file:
c80064
+
c80064
+```xml
c80064
+<monitors version="2">
c80064
+  <policy>
c80064
+    <stores>
c80064
+      <store>system</store>
c80064
+    </stores>
c80064
+  </policy>
c80064
+</monitors>
c80064
+```
c80064
+
c80064
+#### Example of reversing the priority of monitor configuration:
c80064
+
c80064
+```xml
c80064
+<monitors version="2">
c80064
+  <policy>
c80064
+    <stores>
c80064
+      <store>user</store>
c80064
+      <store>system</store>
c80064
+    </stores>
c80064
+  </policy>
c80064
+</monitors>
c80064
+```
c80064
+
c80064
+### Changing D-Bus configuration policy
c80064
+
c80064
+D-Bus configureability can be configured using a `<dbus/>` element in the
c80064
+`<policy/>` element. It's content should either be `yes` or `no` depending on
c80064
+whether monitor configuration via D-Bus should be enabled or disable.
c80064
+
c80064
+#### Example of how to disable monitor configuration via D-Bus:
c80064
+
c80064
+```xml
c80064
+<monitors version="2">
c80064
+  <policy>
c80064
+    <dbus>no</dbus>
c80064
+  </policy>
c80064
+</monitors>
c80064
+```
c80064
-- 
c80064
2.33.1
c80064