518 lines
19 KiB
Diff
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
|
|
|