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