Blame SOURCES/monitor-config-policy.patch

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