gnome-kiosk/0002-compositor-Add-a-window-configuration-file.patch
2025-04-03 12:15:00 +02:00

518 lines
19 KiB
Diff

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