From d25120239c4202025ade06815579b979ef5a8da5 Mon Sep 17 00:00:00 2001
From: Peridot Bot <rockyautomation@rockylinux.org>
Date: Thu, 3 Apr 2025 21:26:38 +0000
Subject: [PATCH] import gnome-kiosk-47.0-10.el10

---
 ...se-the-meta-window-API-for-set-above.patch |  49 ++
 ...itor-Add-a-window-configuration-file.patch | 517 ++++++++++++++++++
 ...sitor-Apply-the-window-configuration.patch | 124 +++++
 ...an-API-to-retrieve-a-boolean-setting.patch |  78 +++
 ...mpositor-Make-set-above-configurable.patch |  81 +++
 SPECS/gnome-kiosk.spec                        |  16 +-
 6 files changed, 862 insertions(+), 3 deletions(-)
 create mode 100644 SOURCES/0001-compositor-Use-the-meta-window-API-for-set-above.patch
 create mode 100644 SOURCES/0002-compositor-Add-a-window-configuration-file.patch
 create mode 100644 SOURCES/0003-compositor-Apply-the-window-configuration.patch
 create mode 100644 SOURCES/0004-compositor-Add-an-API-to-retrieve-a-boolean-setting.patch
 create mode 100644 SOURCES/0005-compositor-Make-set-above-configurable.patch

diff --git a/SOURCES/0001-compositor-Use-the-meta-window-API-for-set-above.patch b/SOURCES/0001-compositor-Use-the-meta-window-API-for-set-above.patch
new file mode 100644
index 0000000..c2cf142
--- /dev/null
+++ b/SOURCES/0001-compositor-Use-the-meta-window-API-for-set-above.patch
@@ -0,0 +1,49 @@
+From 231001c019b379f573baa0e869811fa8c429775b Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Wed, 13 Nov 2024 11:36:25 +0100
+Subject: [PATCH 1/5] compositor: Use the meta window API for set-above
+
+Currently, GNOME Kiosk would place windows that need to be above in a
+special window group on top.
+
+Unfortunately, that means windows in that group will remain there, and
+there is no way to send these back to the notmal layer using the
+existing mutter API.
+
+To avoid the problem, use the meta_window_make_above() API.
+
+See-also: https://gitlab.gnome.org/GNOME/gnome-kiosk/-/issues/26
+(cherry picked from commit 1c2994e5eada4286655d007f91c22452c27f8ead)
+---
+ compositor/kiosk-compositor.c | 14 +++-----------
+ 1 file changed, 3 insertions(+), 11 deletions(-)
+
+diff --git a/compositor/kiosk-compositor.c b/compositor/kiosk-compositor.c
+index 8432a24..37588da 100644
+--- a/compositor/kiosk-compositor.c
++++ b/compositor/kiosk-compositor.c
+@@ -377,18 +377,10 @@ kiosk_compositor_map (MetaPlugin      *plugin,
+                 meta_window_make_fullscreen (window);
+                 easing_duration = 3000;
+         } else {
+-                ClutterActor *window_group;
+-
+                 g_debug ("KioskCompositor: Mapping window that does not need to be fullscreened");
+-                window_group = meta_get_top_window_group_for_display (self->display);
+-
+-                if (kiosk_compositor_wants_window_above (self, window)) {
+-                        g_object_ref (G_OBJECT (actor));
+-                        clutter_actor_remove_child (clutter_actor_get_parent (CLUTTER_ACTOR (actor)), CLUTTER_ACTOR (actor));
+-                        clutter_actor_add_child (window_group, CLUTTER_ACTOR (actor));
+-                        clutter_actor_set_child_above_sibling (window_group, CLUTTER_ACTOR (actor), NULL);
+-                        g_object_unref (G_OBJECT (actor));
+-                }
++
++                if (kiosk_compositor_wants_window_above (self, window))
++                        meta_window_make_above (window);
+ 
+                 easing_duration = 500;
+         }
+-- 
+2.49.0
+
diff --git a/SOURCES/0002-compositor-Add-a-window-configuration-file.patch b/SOURCES/0002-compositor-Add-a-window-configuration-file.patch
new file mode 100644
index 0000000..bc1cc7b
--- /dev/null
+++ b/SOURCES/0002-compositor-Add-a-window-configuration-file.patch
@@ -0,0 +1,517 @@
+From f30b19d09bf521f3b731e08551428c5a7afb26c6 Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Fri, 11 Oct 2024 14:36:02 +0200
+Subject: [PATCH 2/5] compositor: Add a window configuration file
+
+This adds a configuration file to specify the windows configuration at
+start-up.
+
+The file is an "ini" style file with sections and keys/values.
+
+There can be as many sections as desired, each section gets evaluated.
+
+There are two categories of keys, the "match" keys and the "set" keys.
+
+The "match" keys are used to filter the windows before applying the
+values from the "set" keys.
+
+The "match" keys can take wildcards and patterns.
+
+Currently the following "match" keys as supported:
+
+ * match-title (string) - Matches the window title
+ * match-class (string) - Matches the window class
+ * match-sandboxed-app-id (string) - Matches the sandboxed application id
+
+The following "set" keys are supported:
+
+ * set-fullscreen (boolean) - Whether the window should be fullscreen
+ * set-x (integer) - the X position
+ * set-y (integer) - the Y position
+ * set-width (integer) - the width
+ * set-height (integer) - the height
+
+E.g.:
+
+  # Place all windows at (0,0) by default
+  [all]
+  set-x=0
+  set-y=0
+
+  # Make all Mozilla windows fullscreen
+  [mozilla]
+  match-class=org.mozilla.*
+  set-fullscreen=true
+
+  # All other windows will be set fullscreen automatically using the
+  # existing GNOME Kiosk heuristic, as before.
+
+see-also: https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4076
+(cherry picked from commit 9cb4f91724e489ab8b849ffb83f5a6c01e0ac552)
+(cherry picked from commit 047bd8c83f4a6e950b0ce608417cba178df19f10)
+---
+ CONFIG.md                        |  62 ++++++
+ compositor/kiosk-window-config.c | 315 +++++++++++++++++++++++++++++++
+ compositor/kiosk-window-config.h |  21 +++
+ meson.build                      |   1 +
+ window-config.ini                |  17 ++
+ 5 files changed, 416 insertions(+)
+ create mode 100644 CONFIG.md
+ create mode 100644 compositor/kiosk-window-config.c
+ create mode 100644 compositor/kiosk-window-config.h
+ create mode 100644 window-config.ini
+
+diff --git a/CONFIG.md b/CONFIG.md
+new file mode 100644
+index 0000000..b0045ee
+--- /dev/null
++++ b/CONFIG.md
+@@ -0,0 +1,62 @@
++# Configuration file
++
++GNOME Kiosk takes a configuration file to specify the windows configuration at start-up.
++
++The configuration file called `window-config.ini` is searched in multiple places on the
++system. The first instance of the file found is used.
++
++ * The base directory in which user-specific application configuration is stored
++   `$XDG_CONFIG_HOME/gnome-kiosk/window-config.ini` (usually `$HOME/.config/gnome-kiosk/window-config.ini`)
++ * The system-wide list of directories in which system-wide application data is stored `$XDG_DATA_DIRS`
++   This list usually includes:
++    - `/var/lib/flatpak/exports/share/gnome-kiosk/window-config.ini`
++    - `/usr/local/share/gnome-kiosk/window-config.ini`
++    - `/usr/share/gnome-kiosk/window-config.ini`
++
++# Syntax
++
++The configuration file is an "ini" style file with sections and keys/values.
++
++There can be as many sections as desired.
++
++The name of the sections does not matter, there is no special name of section,
++each section gets evaluated.
++
++There are two categories of keys, the "*match*" keys and the "*set*" keys.
++
++The "*match*" keys are used to filter the windows before applying the
++values from the "*set*" keys.
++
++The "*match*" keys can take wildcards and patterns.
++
++The following "*match*" keys as supported:
++
++ * `match-title` (string) - Matches the window title
++ * `match-class` (string) - Matches the window class
++ * `match-sandboxed-app-id` (string) - Matches the sandboxed application id
++
++The following "*set*" keys are supported:
++
++ * `set-fullscreen` (boolean) - Whether the window should be fullscreen
++ * `set-x` (integer) - the X position
++ * `set-y` (integer) - the Y position
++ * `set-width` (integer) - the width
++ * `set-height` (integer) - the height
++
++# Example
++
++```
++  # Place all windows at (0,0) by default, not fullscreen
++  [all]
++  set-x=0
++  set-y=0
++  set-fullscreen=false
++
++  # Make all Mozilla windows fullscreen
++  [mozilla]
++  match-class=org.mozilla.*
++  set-fullscreen=true
++
++  # All other windows will be set fullscreen automatically using the
++  # existing GNOME Kiosk heuristic, as before.
++```
+diff --git a/compositor/kiosk-window-config.c b/compositor/kiosk-window-config.c
+new file mode 100644
+index 0000000..5e6c830
+--- /dev/null
++++ b/compositor/kiosk-window-config.c
+@@ -0,0 +1,315 @@
++#include "config.h"
++
++#include <stdlib.h>
++#include <string.h>
++
++#include "kiosk-window-config.h"
++
++#define KIOSK_WINDOW_CONFIG_DIR      "gnome-kiosk"
++#define KIOSK_WINDOW_CONFIG_FILENAME "window-config.ini"
++#define KIOSK_WINDOW_CONFIG_GET_KEY_VALUE(f) ((KioskWindowConfigGetKeyValue) (f))
++
++typedef gpointer (*KioskWindowConfigGetKeyValue) (GKeyFile   *key_file,
++                                                  const char *section_name,
++                                                  const char *key_name,
++                                                  GError    **error);
++
++struct _KioskWindowConfig
++{
++        GObject   parent;
++
++        GKeyFile *config_key_file;
++};
++
++G_DEFINE_TYPE (KioskWindowConfig, kiosk_window_config, G_TYPE_OBJECT)
++
++static gboolean
++kiosk_window_config_try_load_file (KioskWindowConfig *kiosk_window_config,
++                                   char              *filename)
++{
++        g_autoptr (GError) error = NULL;
++
++        if (!g_key_file_load_from_file (kiosk_window_config->config_key_file,
++                                        filename,
++                                        G_KEY_FILE_NONE,
++                                        &error)) {
++                g_debug ("KioskWindowConfig: Error loading key file %s: %s",
++                         filename, error->message);
++
++                return FALSE;
++        }
++
++        return TRUE;
++}
++
++static gboolean
++kiosk_window_config_load (KioskWindowConfig *kiosk_window_config)
++{
++        const char * const *xdg_data_dirs;
++        g_autofree gchar *filename = NULL;
++        int i;
++
++        /* Try user config first */
++        filename = g_build_filename (g_get_user_config_dir (),
++                                     KIOSK_WINDOW_CONFIG_DIR,
++                                     KIOSK_WINDOW_CONFIG_FILENAME, NULL);
++
++        if (kiosk_window_config_try_load_file (kiosk_window_config, filename))
++                goto out;
++
++        /* Then system config */
++        xdg_data_dirs = g_get_system_data_dirs ();
++        for (i = 0; xdg_data_dirs[i]; i++) {
++                filename = g_build_filename (xdg_data_dirs[i],
++                                             KIOSK_WINDOW_CONFIG_DIR,
++                                             KIOSK_WINDOW_CONFIG_FILENAME, NULL);
++
++                if (kiosk_window_config_try_load_file (kiosk_window_config, filename))
++                        goto out;
++        }
++
++        g_debug ("KioskWindowConfig: No configuration file found");
++
++        return FALSE;
++out:
++        g_debug ("KioskWindowConfig: Loading key file %s", filename);
++
++        return TRUE;
++}
++
++static void
++kiosk_window_config_init (KioskWindowConfig *self)
++{
++        self->config_key_file = g_key_file_new ();
++        kiosk_window_config_load (self);
++}
++
++static void
++kiosk_window_config_dispose (GObject *object)
++{
++        KioskWindowConfig *self = KIOSK_WINDOW_CONFIG (object);
++
++        g_clear_pointer (&self->config_key_file, g_key_file_free);
++
++        G_OBJECT_CLASS (kiosk_window_config_parent_class)->dispose (object);
++}
++
++static void
++kiosk_window_config_class_init (KioskWindowConfigClass *klass)
++{
++        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
++
++        gobject_class->dispose = kiosk_window_config_dispose;
++}
++
++#define KIOSK_WINDOW_CONFIG_CHECK_FUNC_VALUE(self, section, key, func, value) \
++        G_STMT_START { \
++                g_autoptr (GError) error = NULL; \
++                if (!g_key_file_has_key (self->config_key_file, section, key, NULL)) { \
++                        g_debug ("KioskWindowConfig: No key '%s' in section [%s]", \
++                                 key, section); \
++                        return FALSE; \
++                } \
++                *value = func (self->config_key_file, section, key, &error); \
++                if (error) { \
++                        g_debug ("KioskWindowConfig: Error with key '%s' in section [%s]: %s", \
++                                 key, section, error->message); \
++                        return FALSE; \
++                } \
++                return TRUE; \
++        } G_STMT_END
++
++static gboolean
++kiosk_window_config_check_for_string_value (KioskWindowConfig *kiosk_window_config,
++                                            const char        *section_name,
++                                            const char        *key_name,
++                                            char             **value)
++{
++        KIOSK_WINDOW_CONFIG_CHECK_FUNC_VALUE (kiosk_window_config,
++                                              section_name,
++                                              key_name,
++                                              g_key_file_get_string,
++                                              value);
++}
++
++static gboolean
++kiosk_window_config_check_for_integer_value (KioskWindowConfig *kiosk_window_config,
++                                             const char        *section_name,
++                                             const char        *key_name,
++                                             int               *value)
++{
++        KIOSK_WINDOW_CONFIG_CHECK_FUNC_VALUE (kiosk_window_config,
++                                              section_name,
++                                              key_name,
++                                              g_key_file_get_integer,
++                                              value);
++}
++
++static gboolean
++kiosk_window_config_check_for_boolean_value (KioskWindowConfig *kiosk_window_config,
++                                             const char        *section_name,
++                                             const char        *key_name,
++                                             gboolean          *value)
++{
++        KIOSK_WINDOW_CONFIG_CHECK_FUNC_VALUE (kiosk_window_config,
++                                              section_name,
++                                              key_name,
++                                              g_key_file_get_boolean,
++                                              value);
++}
++
++static void
++kiosk_window_config_apply_config (KioskWindowConfig *kiosk_window_config,
++                                  MetaWindowConfig  *window_config,
++                                  const char        *section_name)
++{
++        MtkRectangle rect;
++        int new_x, new_y, new_width, new_height;
++        gboolean fullscreen;
++
++        if (kiosk_window_config_check_for_boolean_value (kiosk_window_config,
++                                                         section_name,
++                                                         "set-fullscreen",
++                                                         &fullscreen)) {
++                g_debug ("KioskWindowConfig: Using 'set-fullscreen=%s' from section [%s]",
++                         fullscreen ? "TRUE" : "FALSE", section_name);
++                meta_window_config_set_is_fullscreen (window_config, fullscreen);
++        }
++
++        rect = meta_window_config_get_rect (window_config);
++
++        if (kiosk_window_config_check_for_integer_value (kiosk_window_config,
++                                                         section_name,
++                                                         "set-x",
++                                                         &new_x)) {
++                g_debug ("KioskWindowConfig: Using 'set-x=%i' from section [%s]",
++                         new_x, section_name);
++                rect.x = new_x;
++        }
++
++        if (kiosk_window_config_check_for_integer_value (kiosk_window_config,
++                                                         section_name,
++                                                         "set-y",
++                                                         &new_y)) {
++                g_debug ("KioskWindowConfig: Using 'set-y=%i' from section [%s]",
++                         new_y, section_name);
++                rect.y = new_y;
++        }
++
++        if (kiosk_window_config_check_for_integer_value (kiosk_window_config,
++                                                         section_name,
++                                                         "set-width",
++                                                         &new_width)) {
++                g_debug ("KioskWindowConfig: Using 'set-width=%i' from section [%s]",
++                         new_width, section_name);
++                rect.width = new_width;
++        }
++
++        if (kiosk_window_config_check_for_integer_value (kiosk_window_config,
++                                                         section_name,
++                                                         "set-height",
++                                                         &new_height)) {
++                g_debug ("KioskWindowConfig: Using 'set-height=%i' from section [%s]",
++                         new_height, section_name);
++                rect.height = new_height;
++        }
++
++        meta_window_config_set_rect (window_config, rect);
++}
++
++static gboolean
++kiosk_window_config_match_string_key (KioskWindowConfig *kiosk_window_config,
++                                      const char        *section_name,
++                                      const char        *key_name,
++                                      const char        *value)
++{
++        g_autofree gchar *key_value = NULL;
++        g_autoptr (GError) error = NULL;
++        gboolean is_a_match = TRUE;
++
++        /* Keys are used to filter out, no key means we have a match */
++        if (!kiosk_window_config_check_for_string_value (kiosk_window_config,
++                                                         section_name,
++                                                         key_name,
++                                                         &key_value))
++                return TRUE;
++
++        is_a_match = g_pattern_match_simple (key_value, value);
++        g_debug ("KioskWindowConfig: Value '%s' %s key '%s=%s' from section [%s]",
++                 value,
++                 is_a_match ? "matches" : "does not match",
++                 key_name,
++                 key_value,
++                 section_name);
++
++        return is_a_match;
++}
++
++static gboolean
++kiosk_window_config_match_window (KioskWindowConfig *kiosk_window_config,
++                                  MetaWindow        *window,
++                                  const char        *section_name)
++{
++        const char *match_value;
++
++        g_debug ("KioskWindowConfig: Checking section [%s]", section_name);
++
++        match_value = meta_window_get_title (window);
++        if (match_value &&
++            !kiosk_window_config_match_string_key (kiosk_window_config,
++                                                   section_name,
++                                                   "match-title",
++                                                   match_value))
++                return FALSE;
++
++        match_value = meta_window_get_wm_class (window);
++        if (match_value &&
++            !kiosk_window_config_match_string_key (kiosk_window_config,
++                                                   section_name,
++                                                   "match-class",
++                                                   match_value))
++                return FALSE;
++
++        match_value = meta_window_get_sandboxed_app_id (window);
++        if (match_value &&
++            !kiosk_window_config_match_string_key (kiosk_window_config,
++                                                   section_name,
++                                                   "match-sandboxed-app-id",
++                                                   match_value))
++                return FALSE;
++
++        return TRUE;
++}
++
++void
++kiosk_window_config_update_window (KioskWindowConfig *kiosk_window_config,
++                                   MetaWindow        *window,
++                                   MetaWindowConfig  *window_config)
++{
++        g_auto (GStrv) sections;
++        gsize length;
++        int i;
++
++        sections = g_key_file_get_groups (kiosk_window_config->config_key_file, &length);
++        for (i = 0; i < length; i++) {
++                if (!kiosk_window_config_match_window (kiosk_window_config,
++                                                       window,
++                                                       sections[i]))
++                        continue;
++
++                kiosk_window_config_apply_config (kiosk_window_config,
++                                                  window_config,
++                                                  sections[i]);
++        }
++}
++
++KioskWindowConfig *
++kiosk_window_config_new (void)
++{
++        KioskWindowConfig *kiosk_window_config;
++
++        kiosk_window_config = g_object_new (KIOSK_TYPE_WINDOW_CONFIG,
++                                            NULL);
++
++        return kiosk_window_config;
++}
+diff --git a/compositor/kiosk-window-config.h b/compositor/kiosk-window-config.h
+new file mode 100644
+index 0000000..1c7e8ea
+--- /dev/null
++++ b/compositor/kiosk-window-config.h
+@@ -0,0 +1,21 @@
++#pragma once
++
++#include <glib-object.h>
++#include <glib.h>
++
++#include <meta/window.h>
++#include <meta/meta-window-config.h>
++
++G_BEGIN_DECLS
++
++#define KIOSK_TYPE_WINDOW_CONFIG (kiosk_window_config_get_type ())
++G_DECLARE_FINAL_TYPE (KioskWindowConfig, kiosk_window_config,
++                      KIOSK, WINDOW_CONFIG, GObject)
++
++KioskWindowConfig *kiosk_window_config_new (void);
++
++void kiosk_window_config_update_window (KioskWindowConfig *kiosk_window_config,
++                                        MetaWindow        *window,
++                                        MetaWindowConfig  *window_config);
++
++G_END_DECLS
+diff --git a/meson.build b/meson.build
+index 23aaff7..c3eb74c 100644
+--- a/meson.build
++++ b/meson.build
+@@ -166,6 +166,7 @@ compositor_sources += 'compositor/kiosk-service.c'
+ compositor_sources += 'compositor/kiosk-shell-service.c'
+ compositor_sources += 'compositor/kiosk-shell-introspect-service.c'
+ compositor_sources += 'compositor/kiosk-shell-screenshot-service.c'
++compositor_sources += 'compositor/kiosk-window-config.c'
+ compositor_sources += 'compositor/kiosk-screenshot.c'
+ 
+ if mutter_have_x11
+diff --git a/window-config.ini b/window-config.ini
+new file mode 100644
+index 0000000..c4c825e
+--- /dev/null
++++ b/window-config.ini
+@@ -0,0 +1,17 @@
++# This is just an example for a window configuration file.
++# Copy this file to $HOME/.config/gnome-kiosk/window-config.ini
++
++# The section names are free and all sections get evaluated.
++
++# Place all windows at (0,0) by default
++[all]
++set-x=0
++set-y=0
++
++# Make all Mozilla windows fullscreen
++[browser]
++match-class=org.mozilla*
++set-fullscreen=true
++
++# All other windows will be set fullscreen automatically using the
++# existing GNOME Kiosk heuristic, as before.
+-- 
+2.49.0
+
diff --git a/SOURCES/0003-compositor-Apply-the-window-configuration.patch b/SOURCES/0003-compositor-Apply-the-window-configuration.patch
new file mode 100644
index 0000000..3f01dd3
--- /dev/null
+++ b/SOURCES/0003-compositor-Apply-the-window-configuration.patch
@@ -0,0 +1,124 @@
+From bf79fe519d55a5fc6d31775164e0a4c5831a9020 Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Fri, 25 Oct 2024 14:04:17 +0200
+Subject: [PATCH 3/5] compositor: Apply the window configuration
+
+Use the "MetaWindow::configure" signal in mutter-16 to apply the GNOME
+Kiosk window configuration.
+
+(cherry picked from commit 30d0c4233f84234bd6d470fc42715593360e115a)
+---
+ compositor/kiosk-compositor.c | 47 +++++++++++++++++++++++++++++++++--
+ 1 file changed, 45 insertions(+), 2 deletions(-)
+
+diff --git a/compositor/kiosk-compositor.c b/compositor/kiosk-compositor.c
+index 37588da..650ac50 100644
+--- a/compositor/kiosk-compositor.c
++++ b/compositor/kiosk-compositor.c
+@@ -12,6 +12,7 @@
+ #include <meta/keybindings.h>
+ #include <meta/meta-context.h>
+ #include <meta/util.h>
++#include <meta/meta-window-config.h>
+ #include <meta/meta-window-group.h>
+ 
+ #include <systemd/sd-daemon.h>
+@@ -25,6 +26,7 @@
+ #include "kiosk-window-tracker.h"
+ #include "kiosk-shell-introspect-service.h"
+ #include "kiosk-shell-screenshot-service.h"
++#include "kiosk-window-config.h"
+ 
+ #include "org.gnome.DisplayManager.Manager.h"
+ 
+@@ -48,6 +50,7 @@ struct _KioskCompositor
+         KioskWindowTracker          *tracker;
+         KioskShellIntrospectService *introspect_service;
+         KioskShellScreenshotService *screenshot_service;
++        KioskWindowConfig           *kiosk_window_config;
+ };
+ 
+ enum
+@@ -61,6 +64,8 @@ static guint signals[NUMBER_OF_SIGNALS] = { 0, };
+ G_DEFINE_TYPE (KioskCompositor, kiosk_compositor, META_TYPE_PLUGIN)
+ 
+ static void kiosk_compositor_dispose (GObject *object);
++static gboolean kiosk_compositor_wants_window_fullscreen (KioskCompositor *self,
++                                                          MetaWindow      *window);
+ 
+ static void
+ kiosk_compositor_dispose (GObject *object)
+@@ -220,6 +225,39 @@ neuter_builtin_keybindings (KioskCompositor *self)
+         }
+ }
+ 
++static void
++kiosk_compositor_on_window_configure (MetaWindow       *window,
++                                      MetaWindowConfig *window_config,
++                                      gpointer          user_data)
++{
++        KioskCompositor *self = KIOSK_COMPOSITOR (user_data);
++        gboolean fullscreen;
++
++        if (!meta_window_config_get_is_initial (window_config)) {
++                g_debug ("KioskCompositor: Ignoring configure for window: %s",
++                         meta_window_get_description (window));
++                return;
++        }
++
++        g_debug ("KioskCompositor: configure window: %s", meta_window_get_description (window));
++
++        fullscreen = kiosk_compositor_wants_window_fullscreen (self, window);
++        meta_window_config_set_is_fullscreen (window_config, fullscreen);
++        kiosk_window_config_update_window (self->kiosk_window_config,
++                                           window,
++                                           window_config);
++}
++
++static void
++kiosk_compositor_on_window_created (MetaDisplay *display,
++                                    MetaWindow  *window,
++                                    gpointer     user_data)
++{
++        g_signal_connect (window, "configure",
++                          G_CALLBACK (kiosk_compositor_on_window_configure),
++                          user_data);
++}
++
+ static void
+ kiosk_compositor_start (MetaPlugin *plugin)
+ {
+@@ -250,6 +288,7 @@ kiosk_compositor_start (MetaPlugin *plugin)
+         self->input_sources_manager = kiosk_input_sources_manager_new (self);
+         self->app_system = kiosk_app_system_new (self);
+         self->tracker = kiosk_window_tracker_new (self, self->app_system);
++        self->kiosk_window_config = kiosk_window_config_new ();
+         self->introspect_service = kiosk_shell_introspect_service_new (self);
+         kiosk_shell_introspect_service_start (self->introspect_service, &error);
+         self->screenshot_service = kiosk_shell_screenshot_service_new (self);
+@@ -265,6 +304,11 @@ kiosk_compositor_start (MetaPlugin *plugin)
+                                                       self->cancellable,
+                                                       KIOSK_OBJECT_CALLBACK (register_session),
+                                                       NULL);
++
++        g_signal_connect_object (self->display, "window-created",
++                                 G_CALLBACK (kiosk_compositor_on_window_created),
++                                 self,
++                                 G_CONNECT_DEFAULT);
+ }
+ 
+ static void
+@@ -372,9 +416,8 @@ kiosk_compositor_map (MetaPlugin      *plugin,
+ 
+         window = meta_window_actor_get_meta_window (actor);
+ 
+-        if (kiosk_compositor_wants_window_fullscreen (self, window)) {
++        if (meta_window_is_fullscreen (window)) {
+                 g_debug ("KioskCompositor: Mapping window that does need to be fullscreened");
+-                meta_window_make_fullscreen (window);
+                 easing_duration = 3000;
+         } else {
+                 g_debug ("KioskCompositor: Mapping window that does not need to be fullscreened");
+-- 
+2.49.0
+
diff --git a/SOURCES/0004-compositor-Add-an-API-to-retrieve-a-boolean-setting.patch b/SOURCES/0004-compositor-Add-an-API-to-retrieve-a-boolean-setting.patch
new file mode 100644
index 0000000..017786d
--- /dev/null
+++ b/SOURCES/0004-compositor-Add-an-API-to-retrieve-a-boolean-setting.patch
@@ -0,0 +1,78 @@
+From fed3a11e0f9c21f2361b3fea564c51957cd24fa5 Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Tue, 12 Nov 2024 10:38:19 +0100
+Subject: [PATCH 4/5] compositor: Add an API to retrieve a boolean setting
+
+This adds a direct search API to the window configuration to retrieve
+the boolean value for a matching window.
+
+This is preparation work for the following commit.
+
+(cherry picked from commit 62a7053d14b8432ef8dbc3ad78cd043ea95cf043)
+---
+ compositor/kiosk-window-config.c | 32 ++++++++++++++++++++++++++++++++
+ compositor/kiosk-window-config.h |  5 +++++
+ 2 files changed, 37 insertions(+)
+
+diff --git a/compositor/kiosk-window-config.c b/compositor/kiosk-window-config.c
+index 5e6c830..a07798b 100644
+--- a/compositor/kiosk-window-config.c
++++ b/compositor/kiosk-window-config.c
+@@ -281,6 +281,38 @@ kiosk_window_config_match_window (KioskWindowConfig *kiosk_window_config,
+         return TRUE;
+ }
+ 
++gboolean
++kiosk_window_config_get_boolean_for_window (KioskWindowConfig *kiosk_window_config,
++                                            MetaWindow        *window,
++                                            const char        *key_name,
++                                            gboolean          *value)
++{
++        g_auto (GStrv) sections;
++        gsize length;
++        gboolean key_found = FALSE;
++        int i;
++
++        sections = g_key_file_get_groups (kiosk_window_config->config_key_file, &length);
++        for (i = 0; i < length; i++) {
++                if (!kiosk_window_config_match_window (kiosk_window_config,
++                                                       window,
++                                                       sections[i]))
++                        continue;
++
++                if (kiosk_window_config_check_for_boolean_value (kiosk_window_config,
++                                                                 sections[i],
++                                                                 key_name,
++                                                                 value)) {
++                        g_debug ("KioskWindowConfig: Using '%s=%s' from section [%s]",
++                                 key_name, *value ? "TRUE" : "FALSE", sections[i]);
++
++                        key_found = TRUE;
++                }
++        }
++
++        return key_found;
++}
++
+ void
+ kiosk_window_config_update_window (KioskWindowConfig *kiosk_window_config,
+                                    MetaWindow        *window,
+diff --git a/compositor/kiosk-window-config.h b/compositor/kiosk-window-config.h
+index 1c7e8ea..3482c43 100644
+--- a/compositor/kiosk-window-config.h
++++ b/compositor/kiosk-window-config.h
+@@ -14,6 +14,11 @@ G_DECLARE_FINAL_TYPE (KioskWindowConfig, kiosk_window_config,
+ 
+ KioskWindowConfig *kiosk_window_config_new (void);
+ 
++gboolean kiosk_window_config_get_boolean_for_window (KioskWindowConfig *kiosk_window_config,
++                                                     MetaWindow        *window,
++                                                     const char        *key_name,
++                                                     gboolean          *value);
++
+ void kiosk_window_config_update_window (KioskWindowConfig *kiosk_window_config,
+                                         MetaWindow        *window,
+                                         MetaWindowConfig  *window_config);
+-- 
+2.49.0
+
diff --git a/SOURCES/0005-compositor-Make-set-above-configurable.patch b/SOURCES/0005-compositor-Make-set-above-configurable.patch
new file mode 100644
index 0000000..48a83d4
--- /dev/null
+++ b/SOURCES/0005-compositor-Make-set-above-configurable.patch
@@ -0,0 +1,81 @@
+From 8a9121bb11f0423aefe5a51252986765e10214b0 Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan@redhat.com>
+Date: Fri, 8 Nov 2024 18:18:32 +0100
+Subject: [PATCH 5/5] compositor: Make set-above configurable
+
+GNOME Kiosk has heuristics to place windows on the top group.
+
+However, in some cases, it may desirable to avoid that behavior as that
+prevents the windows stacking from being changed by the user.
+
+Add a new option "set-above" to the configuration file to control that
+behaviour.
+
+If not specified, the existing heuristics will be applied, not breaking
+possible existing setups.
+
+Closes: https://gitlab.gnome.org/GNOME/gnome-kiosk/-/issues/26
+(cherry picked from commit b50c25fe739c00f4a1984919ee93cd351f58dfc1)
+---
+ CONFIG.md                     | 3 +++
+ compositor/kiosk-compositor.c | 9 +++++++++
+ window-config.ini             | 2 ++
+ 3 files changed, 14 insertions(+)
+
+diff --git a/CONFIG.md b/CONFIG.md
+index b0045ee..652fb71 100644
+--- a/CONFIG.md
++++ b/CONFIG.md
+@@ -42,6 +42,7 @@ The following "*set*" keys are supported:
+  * `set-y` (integer) - the Y position
+  * `set-width` (integer) - the width
+  * `set-height` (integer) - the height
++ * `set-above` (boolean) - Whether the window should be placed on a layer above
+ 
+ # Example
+ 
+@@ -51,6 +52,8 @@ The following "*set*" keys are supported:
+   set-x=0
+   set-y=0
+   set-fullscreen=false
++  # The following will place all windows on the same layer
++  set-above=false
+ 
+   # Make all Mozilla windows fullscreen
+   [mozilla]
+diff --git a/compositor/kiosk-compositor.c b/compositor/kiosk-compositor.c
+index 650ac50..aa5603d 100644
+--- a/compositor/kiosk-compositor.c
++++ b/compositor/kiosk-compositor.c
+@@ -385,6 +385,15 @@ static gboolean
+ kiosk_compositor_wants_window_above (KioskCompositor *self,
+                                      MetaWindow      *window)
+ {
++        gboolean set_above;
++
++        if (kiosk_window_config_get_boolean_for_window (self->kiosk_window_config,
++                                                        window,
++                                                        "set-above",
++                                                        &set_above))
++                return set_above;
++
++        /* If not specified in the config, use the heuristics */
+         if (meta_window_is_screen_sized (window)) {
+                 return FALSE;
+         }
+diff --git a/window-config.ini b/window-config.ini
+index c4c825e..f4f37b3 100644
+--- a/window-config.ini
++++ b/window-config.ini
+@@ -7,6 +7,8 @@
+ [all]
+ set-x=0
+ set-y=0
++# Uncomment the following to have all windows on the same layer
++# set-above=false
+ 
+ # Make all Mozilla windows fullscreen
+ [browser]
+-- 
+2.49.0
+
diff --git a/SPECS/gnome-kiosk.spec b/SPECS/gnome-kiosk.spec
index b43e423..9a71380 100644
--- a/SPECS/gnome-kiosk.spec
+++ b/SPECS/gnome-kiosk.spec
@@ -2,7 +2,7 @@
 ## (rpmautospec version 0.7.3)
 ## RPMAUTOSPEC: autorelease, autochangelog
 %define autorelease(e:s:pb:n) %{?-p:0.}%{lua:
-    release_number = 9;
+    release_number = 10;
     base_release_number = tonumber(rpm.expand("%{?-b*}%{!?-b:1}"));
     print(release_number + base_release_number - 1);
 }%{?-e:.%{-e*}}%{?-s:.%{-s*}}%{!?-n:%{?dist}}
@@ -15,7 +15,7 @@
 %global gnome_desktop_version                   44.0
 %global glib2_version                           2.68.0
 %global gtk4_version                            3.24.27
-%global mutter_version                          47~alpha
+%global mutter_version                          47.5-9
 %global gsettings_desktop_schemas_version       40~rc
 %global ibus_version                            1.5.24
 %global gnome_settings_daemon_version           40~rc
@@ -50,6 +50,7 @@ BuildRequires:  pkgconfig(gdk-pixbuf-2.0)
 
 Requires:       gnome-settings-daemon%{?_isa} >= %{gnome_settings_daemon_version}
 Requires:       gsettings-desktop-schemas%{?_isa} >= %{gsettings_desktop_schemas_version}
+Requires:       mutter%{?_isa} >= %{mutter_version}
 
 Patch: 0001-kiosk-app-Do-not-add-the-window-in-kiosk_app_new_for.patch
 # https://issues.redhat.com/browse/RHEL-71757
@@ -65,6 +66,12 @@ Patch: 0002-search-app-Update-desktop-file-definition.patch
 Patch: 0001-search-app-Use-firefox-from-flatpak.patch
 # https://issues.redhat.com/browse/RHEL-84829
 Patch: 0001-input-sources-manager-Do-not-crash-if-there-is-no-X1.patch
+# https://issues.redhat.com/browse/RHEL-84697
+Patch: 0001-compositor-Use-the-meta-window-API-for-set-above.patch
+Patch: 0002-compositor-Add-a-window-configuration-file.patch
+Patch: 0003-compositor-Apply-the-window-configuration.patch
+Patch: 0004-compositor-Add-an-API-to-retrieve-a-boolean-setting.patch
+Patch: 0005-compositor-Make-set-above-configurable.patch
 
 %description
 GNOME Kiosk provides a desktop enviroment suitable for fixed purpose, or
@@ -107,7 +114,7 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/org.gnome.Kiosk.Searc
 
 %files
 %license COPYING
-%doc NEWS README.md
+%doc NEWS README.md CONFIG.md
 %{_bindir}/gnome-kiosk
 %{_datadir}/applications/org.gnome.Kiosk.desktop
 %{_datadir}/dconf/profile/gnomekiosk
@@ -135,6 +142,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/org.gnome.Kiosk.Searc
 
 %changelog
 ## START: Generated by rpmautospec
+* Thu Apr 03 2025 Olivier Fourdan <ofourdan@redhat.com> - 47.0-10
+- Add window configurability
+
 * Wed Mar 26 2025 Olivier Fourdan <ofourdan@redhat.com> - 47.0-9
 - Fix crash on missing systemd files
 
-- 
GitLab