Blame SOURCES/monitor-config-policy.patch

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