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