From d7b2de24e77757ba5113a4532ae390862bb3e8ce Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Tue, 29 Mar 2022 09:30:31 -0400 Subject: [PATCH] import mutter-3.32.2-63.el8 --- ...ck-for-present-window-out-of-the-act.patch | 150 ++ ...ace-Propagate-commit-to-parent-class.patch | 38 + SOURCES/eglstream-overview-fixes.patch | 333 +++ SOURCES/monitor-config-policy.patch | 2356 +++++++++++++++++ SPECS/mutter.spec | 27 +- 5 files changed, 2903 insertions(+), 1 deletion(-) create mode 100644 SOURCES/0001-wayland-Move-check-for-present-window-out-of-the-act.patch create mode 100644 SOURCES/0002-wayland-dnd-surface-Propagate-commit-to-parent-class.patch create mode 100644 SOURCES/eglstream-overview-fixes.patch create mode 100644 SOURCES/monitor-config-policy.patch diff --git a/SOURCES/0001-wayland-Move-check-for-present-window-out-of-the-act.patch b/SOURCES/0001-wayland-Move-check-for-present-window-out-of-the-act.patch new file mode 100644 index 0000000..940bc76 --- /dev/null +++ b/SOURCES/0001-wayland-Move-check-for-present-window-out-of-the-act.patch @@ -0,0 +1,150 @@ +From 9269b09028ae51c7bb74e9cc9aefafd8eaa882d6 Mon Sep 17 00:00:00 2001 +From: Robert Mader +Date: Tue, 16 Apr 2019 23:35:28 +0200 +Subject: [PATCH 1/2] wayland: Move check for present window out of the + actor-surface class + +All child classes of `MetaWaylandShellSurface` as well as +`MetaWaylandSurfaceRoleXWayland` should only sync their actor if +their toplevel surface has a window. Currently this check is done +in the actor-surface class, but not all surface classes have a +toplevel window, e.g. dnd-surfaces. +Move the check to the right places. + +For subsurfaces this assumes that the subsurface is not the child of +a window-less surface (like, as stated above, e.g. a dnd-surface). +If we want to support subsurfaces of window-less surfaces in the future +we have to extend the check here. +But as this is not a regression, ignore this case for now. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/537 +(cherry picked from commit 7e2a0ede16bed5671fe55d3d81ccc9f82eebd94b) +--- + src/wayland/meta-wayland-actor-surface.c | 7 ------- + src/wayland/meta-wayland-shell-surface.c | 20 ++++++++++++++++++++ + src/wayland/meta-wayland-subsurface.c | 5 ++++- + src/wayland/meta-xwayland.c | 18 ++++++++++++++++++ + 4 files changed, 42 insertions(+), 8 deletions(-) + +diff --git a/src/wayland/meta-wayland-actor-surface.c b/src/wayland/meta-wayland-actor-surface.c +index 037dd901ab..e2143e51f1 100644 +--- a/src/wayland/meta-wayland-actor-surface.c ++++ b/src/wayland/meta-wayland-actor-surface.c +@@ -295,9 +295,6 @@ meta_wayland_actor_surface_commit (MetaWaylandSurfaceRole *surface_role, + META_WAYLAND_ACTOR_SURFACE (surface_role); + MetaWaylandActorSurfacePrivate *priv = + meta_wayland_actor_surface_get_instance_private (actor_surface); +- MetaWaylandSurface *surface = +- meta_wayland_surface_role_get_surface (surface_role); +- MetaWaylandSurface *toplevel_surface; + + if (!wl_list_empty (&pending->frame_callback_list) && + priv->actor && +@@ -311,10 +308,6 @@ meta_wayland_actor_surface_commit (MetaWaylandSurfaceRole *surface_role, + + meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending); + +- toplevel_surface = meta_wayland_surface_get_toplevel (surface); +- if (!toplevel_surface || !toplevel_surface->window) +- return; +- + meta_wayland_actor_surface_sync_actor_state (actor_surface); + } + +diff --git a/src/wayland/meta-wayland-shell-surface.c b/src/wayland/meta-wayland-shell-surface.c +index 04f2aaeea8..f8354ab7c5 100644 +--- a/src/wayland/meta-wayland-shell-surface.c ++++ b/src/wayland/meta-wayland-shell-surface.c +@@ -175,6 +175,22 @@ meta_wayland_shell_surface_surface_commit (MetaWaylandSurfaceRole *surface_role + window->buffer_rect.height = cogl_texture_get_height (texture) * scale; + } + ++static void ++meta_wayland_shell_surface_sync_actor_state (MetaWaylandActorSurface *actor_surface) ++{ ++ MetaWaylandSurfaceRole *surface_role = ++ META_WAYLAND_SURFACE_ROLE (actor_surface); ++ MetaWaylandSurface *surface = ++ meta_wayland_surface_role_get_surface (surface_role); ++ MetaWaylandActorSurfaceClass *actor_surface_class = ++ META_WAYLAND_ACTOR_SURFACE_CLASS (meta_wayland_shell_surface_parent_class); ++ MetaWaylandSurface *toplevel_surface; ++ ++ toplevel_surface = meta_wayland_surface_get_toplevel (surface); ++ if (toplevel_surface && toplevel_surface->window) ++ actor_surface_class->sync_actor_state (actor_surface); ++} ++ + static void + meta_wayland_shell_surface_init (MetaWaylandShellSurface *role) + { +@@ -185,6 +201,10 @@ meta_wayland_shell_surface_class_init (MetaWaylandShellSurfaceClass *klass) + { + MetaWaylandSurfaceRoleClass *surface_role_class = + META_WAYLAND_SURFACE_ROLE_CLASS (klass); ++ MetaWaylandActorSurfaceClass *actor_surface_class = ++ META_WAYLAND_ACTOR_SURFACE_CLASS (klass); + + surface_role_class->commit = meta_wayland_shell_surface_surface_commit; ++ actor_surface_class->sync_actor_state = ++ meta_wayland_shell_surface_sync_actor_state; + } +diff --git a/src/wayland/meta-wayland-subsurface.c b/src/wayland/meta-wayland-subsurface.c +index c7059b99a2..9a7ff3ec12 100644 +--- a/src/wayland/meta-wayland-subsurface.c ++++ b/src/wayland/meta-wayland-subsurface.c +@@ -199,8 +199,11 @@ meta_wayland_subsurface_sync_actor_state (MetaWaylandActorSurface *actor_surface + meta_wayland_surface_role_get_surface (surface_role); + MetaWaylandActorSurfaceClass *actor_surface_class = + META_WAYLAND_ACTOR_SURFACE_CLASS (meta_wayland_subsurface_parent_class); ++ MetaWaylandSurface *toplevel_surface; + +- actor_surface_class->sync_actor_state (actor_surface); ++ toplevel_surface = meta_wayland_surface_get_toplevel (surface); ++ if (toplevel_surface && toplevel_surface->window) ++ actor_surface_class->sync_actor_state (actor_surface); + + sync_actor_subsurface_state (surface); + } +diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c +index 6e4b9a8ffd..b71c638d93 100644 +--- a/src/wayland/meta-xwayland.c ++++ b/src/wayland/meta-xwayland.c +@@ -794,6 +794,20 @@ xwayland_surface_get_toplevel (MetaWaylandSurfaceRole *surface_role) + return meta_wayland_surface_role_get_surface (surface_role); + } + ++static void ++xwayland_surface_sync_actor_state (MetaWaylandActorSurface *actor_surface) ++{ ++ MetaWaylandSurfaceRole *surface_role = ++ META_WAYLAND_SURFACE_ROLE (actor_surface); ++ MetaWaylandSurface *surface = ++ meta_wayland_surface_role_get_surface (surface_role); ++ MetaWaylandActorSurfaceClass *actor_surface_class = ++ META_WAYLAND_ACTOR_SURFACE_CLASS (meta_wayland_surface_role_xwayland_parent_class); ++ ++ if (surface->window) ++ actor_surface_class->sync_actor_state (actor_surface); ++} ++ + static void + meta_wayland_surface_role_xwayland_init (MetaWaylandSurfaceRoleXWayland *role) + { +@@ -804,9 +818,13 @@ meta_wayland_surface_role_xwayland_class_init (MetaWaylandSurfaceRoleXWaylandCla + { + MetaWaylandSurfaceRoleClass *surface_role_class = + META_WAYLAND_SURFACE_ROLE_CLASS (klass); ++ MetaWaylandActorSurfaceClass *actor_surface_class = ++ META_WAYLAND_ACTOR_SURFACE_CLASS (klass); + + surface_role_class->get_toplevel = xwayland_surface_get_toplevel; + ++ actor_surface_class->sync_actor_state = xwayland_surface_sync_actor_state; ++ + xwayland_surface_signals[XWAYLAND_SURFACE_WINDOW_ASSOCIATED] = + g_signal_new ("window-associated", + G_TYPE_FROM_CLASS (klass), +-- +2.31.1 + diff --git a/SOURCES/0002-wayland-dnd-surface-Propagate-commit-to-parent-class.patch b/SOURCES/0002-wayland-dnd-surface-Propagate-commit-to-parent-class.patch new file mode 100644 index 0000000..2192f91 --- /dev/null +++ b/SOURCES/0002-wayland-dnd-surface-Propagate-commit-to-parent-class.patch @@ -0,0 +1,38 @@ +From f37ef55525777f742051cb988341fa1bab403666 Mon Sep 17 00:00:00 2001 +From: Robert Mader +Date: Mon, 15 Apr 2019 02:02:10 +0200 +Subject: [PATCH 2/2] wayland/dnd-surface: Propagate commit to parent class + +We need to call the underlying actor-surface so the actor +state is synced, otherwise surface state like the scale factor +does not get applied. + +Fixes https://gitlab.gnome.org/GNOME/mutter/issues/550 + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/537 +(cherry picked from commit 01d0316fd703872a2470a351f906ffa4605a647e) +--- + src/wayland/meta-wayland-dnd-surface.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/wayland/meta-wayland-dnd-surface.c b/src/wayland/meta-wayland-dnd-surface.c +index 8ddeb2a7bd..7aa7e3be2f 100644 +--- a/src/wayland/meta-wayland-dnd-surface.c ++++ b/src/wayland/meta-wayland-dnd-surface.c +@@ -51,9 +51,13 @@ dnd_surface_commit (MetaWaylandSurfaceRole *surface_role, + { + MetaWaylandSurface *surface = + meta_wayland_surface_role_get_surface (surface_role); ++ MetaWaylandSurfaceRoleClass *surface_role_class = ++ META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_surface_role_dnd_parent_class); + + meta_wayland_compositor_add_frame_callback_surface (surface->compositor, + surface); ++ ++ surface_role_class->commit (surface_role, pending); + } + + static void +-- +2.31.1 + 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?= +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?= +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?= +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: + +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 + #include + ++#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?= +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: +(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?= +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 . 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 @@ ++ ++ ++ text ++ ++ ++ ++ text ++ ++ ++ ++ text ++ ++ 0 ++ 0 ++ yes ++ ++ ++ DP-1 ++ MetaProduct's Inc. ++ MetaMonitor ++ 0x123456 ++ ++ ++ 1920 ++ 1080 ++ 60.000495910644531 ++ ++ ++ ++ ++ +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 " ++ "under , ignoring"); ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, ++ "Unknown element " ++ "under , ignoring"); ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, ++ "Unknown element " ++ "under , 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?= +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 ), 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?= +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: + + + + + system + user + + + + ... + + + +Only allow system level configurations: + + + + + system + + + + ... + + + +(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 @@ ++ ++ ++ ++ system ++ ++ ++ ++ ++ 0 ++ 0 ++ yes ++ ++ ++ DP-1 ++ MetaProduct's Inc. ++ MetaMonitor ++ 0x123456 ++ ++ ++ 1920 ++ 1080 ++ 60 ++ ++ ++ ++ ++ +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 + #include + ++#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?= +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 @@ ++ ++ ++ ++ user ++ user ++ ++ ++ +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 @@ ++ ++ ++ ++ ++ ++ ++ +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 @@ ++ ++ ++ ++ user ++ not-a-store ++ ++ ++ +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 @@ ++ ++ ++ ++ user ++ system ++ ++ ++ system ++ user ++ ++ ++ +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?= +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 + + + + system + + + +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 @@ ++ ++ ++ ++ system ++ ++ ++ ++ ++ 0 ++ 0 ++ yes ++ ++ ++ DP-1 ++ MetaProduct's Inc. ++ MetaMonitor ++ 0x123456 ++ ++ ++ 640 ++ 480 ++ 60 ++ ++ ++ ++ ++ +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 @@ ++ ++ ++ ++ 0 ++ 0 ++ yes ++ ++ ++ DP-1 ++ MetaProduct's Inc. ++ MetaMonitor ++ 0x123456 ++ ++ ++ 800 ++ 600 ++ 60 ++ ++ ++ ++ ++ +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?= +Date: Thu, 30 Sep 2021 21:06:38 +0200 +Subject: [PATCH 8/9] monitor-config-store: Allow changing D-Bus configuration + policy + +Adding a 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 + + + + no + + + +(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 @@ + --> + + ++ ++ ++ +