gnome-kiosk/0004-wip-Better-support-libxklavier.patch
2021-04-20 10:23:22 -04:00

1249 lines
48 KiB
Diff

From c99306e0efcf3113d592bc963b4dfac6504b724d Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Thu, 15 Apr 2021 14:43:17 -0400
Subject: [PATCH 4/4] wip! Better support libxklavier
Mutter currently doesn't allow libxklavier to change the group out from
under it.
This commit fixes that, but also makes sure that current layout state
stays in sync with X server state.
---
compositor/kiosk-input-source-group.c | 17 +
compositor/kiosk-input-source-group.h | 5 +-
compositor/kiosk-input-sources-manager.c | 142 ++++++
compositor/kiosk-x-keyboard-manager.c | 579 +++++++++++++++++++++++
compositor/kiosk-x-keyboard-manager.h | 22 +
meson.build | 1 +
6 files changed, 765 insertions(+), 1 deletion(-)
create mode 100644 compositor/kiosk-x-keyboard-manager.c
create mode 100644 compositor/kiosk-x-keyboard-manager.h
diff --git a/compositor/kiosk-input-source-group.c b/compositor/kiosk-input-source-group.c
index a0c924e..6e2712f 100644
--- a/compositor/kiosk-input-source-group.c
+++ b/compositor/kiosk-input-source-group.c
@@ -213,60 +213,66 @@ kiosk_input_source_group_set_input_engine (KioskInputSourceGroup *self,
const char *engine_name)
{
g_debug ("KioskInputSourceGroup: Setting input engine to '%s'", engine_name);
g_free (self->input_engine_name);
self->input_engine_name = g_strdup (engine_name);
g_ptr_array_set_size (self->layouts, 0);
g_ptr_array_set_size (self->variants, 0);
g_ptr_array_add (self->layouts, NULL);
g_ptr_array_add (self->variants, NULL);
return TRUE;
}
const char *
kiosk_input_source_group_get_input_engine (KioskInputSourceGroup *self)
{
return self->input_engine_name;
}
void
kiosk_input_source_group_set_options (KioskInputSourceGroup *self,
const char *options)
{
g_free (self->options);
self->options = g_strdup (options);
}
+const char *
+kiosk_input_source_group_get_options (KioskInputSourceGroup *self)
+{
+ return self->options;
+}
+
gboolean
kiosk_input_source_group_activate (KioskInputSourceGroup *self)
{
size_t number_of_layouts;
g_autofree char *layouts = NULL;
g_autofree char *variants = NULL;
g_debug ("KioskInputSourceGroup: Activating input source");
if (self->input_engine_name != NULL) {
kiosk_input_source_group_ensure_layout_for_input_engine (self);
}
number_of_layouts = kiosk_input_source_group_get_number_of_layouts (self);
if (number_of_layouts == 0) {
return FALSE;
}
layouts = g_strjoinv (",", (GStrv) self->layouts->pdata);
variants = g_strjoinv (",", (GStrv) self->variants->pdata);
if (self->input_engine_name != NULL) {
gboolean activated;
activated = kiosk_input_engine_manager_activate_engine (self->input_engine_manager, self->input_engine_name);
if (!activated) {
g_debug ("KioskInputSourceGroup: Could not activate input engine '%s'", self->input_engine_name);
return FALSE;
@@ -274,60 +280,71 @@ kiosk_input_source_group_activate (KioskInputSourceGroup *self)
} else {
kiosk_input_engine_manager_activate_engine (self->input_engine_manager, NULL);
}
g_debug ("KioskInputSourceGroup: Setting keyboard mapping to [%s] (%s) [%s]",
layouts, variants, self->options);
meta_backend_set_keymap (meta_get_backend (), layouts, variants, self->options);
meta_backend_lock_layout_group (meta_get_backend (), self->layout_index);
return TRUE;
}
static ssize_t
get_index_of_layout (KioskInputSourceGroup *self,
const char *layout_name)
{
g_auto (GStrv) layouts;
size_t i;
layouts = kiosk_input_source_group_get_layouts (self);
for (i = 0; layouts[i] != NULL; i++) {
if (g_strcmp0 (layout_name, layouts[i]) == 0) {
return (ssize_t) i;
}
}
return -1;
}
+gboolean
+kiosk_input_source_group_only_has_layouts (KioskInputSourceGroup *self,
+ const char * const *layouts_to_check)
+{
+ g_auto (GStrv) layouts;
+
+ layouts = kiosk_input_source_group_get_layouts (self);
+
+ return g_strv_equal (layouts_to_check, (const char * const *) layouts);
+}
+
gboolean
kiosk_input_source_group_switch_to_layout (KioskInputSourceGroup *self,
const char *layout_name)
{
g_autofree char *active_layout = NULL;
ssize_t layout_index;
layout_index = get_index_of_layout (self, layout_name);
if (layout_index < 0) {
return FALSE;
}
g_debug ("KioskInputSourceGroup: Switching to layout %s", layout_name);
active_layout = kiosk_input_source_group_get_selected_layout (self);
self->layout_index = layout_index;
g_debug ("KioskInputSourceGroup: Switching from layout '%s' to next layout '%s'",
active_layout, layout_name);
meta_backend_lock_layout_group (meta_get_backend (), self->layout_index);
return TRUE;
}
void
kiosk_input_source_group_switch_to_first_layout (KioskInputSourceGroup *self)
{
diff --git a/compositor/kiosk-input-source-group.h b/compositor/kiosk-input-source-group.h
index cec8b2f..d255da4 100644
--- a/compositor/kiosk-input-source-group.h
+++ b/compositor/kiosk-input-source-group.h
@@ -1,38 +1,41 @@
#pragma once
#include <glib-object.h>
typedef struct _KioskInputSourcesManager KioskInputSourcesManager;
G_BEGIN_DECLS
#define KIOSK_TYPE_INPUT_SOURCE_GROUP (kiosk_input_source_group_get_type ())
G_DECLARE_FINAL_TYPE (KioskInputSourceGroup,
kiosk_input_source_group,
KIOSK, INPUT_SOURCE_GROUP,
GObject)
KioskInputSourceGroup *kiosk_input_source_group_new (KioskInputSourcesManager *manager);
gboolean kiosk_input_source_group_add_layout (KioskInputSourceGroup *input_sources,
const char *layout,
const char *variant);
char *kiosk_input_source_group_get_selected_layout (KioskInputSourceGroup *input_sources);
char **kiosk_input_source_group_get_layouts (KioskInputSourceGroup *input_sources);
gboolean kiosk_input_source_group_set_input_engine (KioskInputSourceGroup *input_sources,
const char *engine_name);
const char *kiosk_input_source_group_get_input_engine (KioskInputSourceGroup *input_sources);
void kiosk_input_source_group_set_options (KioskInputSourceGroup *input_sources,
- const char *options);
+ const char *options);
+const char *kiosk_input_source_group_get_options (KioskInputSourceGroup *self);
gboolean kiosk_input_source_group_activate (KioskInputSourceGroup *input_sources);
+gboolean kiosk_input_source_group_only_has_layouts (KioskInputSourceGroup *self,
+ const char * const *layouts_to_check);
gboolean kiosk_input_source_group_switch_to_layout (KioskInputSourceGroup *input_sources,
const char *layout_name);
void kiosk_input_source_group_switch_to_first_layout (KioskInputSourceGroup *input_sources);
void kiosk_input_source_group_switch_to_last_layout (KioskInputSourceGroup *input_sources);
gboolean kiosk_input_source_group_switch_to_next_layout (KioskInputSourceGroup *input_sources);
gboolean kiosk_input_source_group_switch_to_previous_layout (KioskInputSourceGroup *input_sources);
G_END_DECLS
diff --git a/compositor/kiosk-input-sources-manager.c b/compositor/kiosk-input-sources-manager.c
index 4b4ef62..c6c8c91 100644
--- a/compositor/kiosk-input-sources-manager.c
+++ b/compositor/kiosk-input-sources-manager.c
@@ -1,83 +1,88 @@
#include "config.h"
#include "kiosk-input-sources-manager.h"
#include <stdlib.h>
#include <string.h>
#include <xkbcommon/xkbcommon.h>
#include <meta/display.h>
#include <meta/keybindings.h>
#include <meta/util.h>
#include <meta/meta-backend.h>
#include <meta/meta-plugin.h>
+#include <meta/meta-x11-display.h>
+
+#include <X11/Xatom.h>
#define GNOME_DESKTOP_USE_UNSTABLE_API
#include <libgnome-desktop/gnome-languages.h>
#include <libgnome-desktop/gnome-xkb-info.h>
#include "org.freedesktop.locale1.h"
#include "kiosk-compositor.h"
#include "kiosk-dbus-utils.h"
#include "kiosk-gobject-utils.h"
#include "kiosk-input-engine-manager.h"
#include "kiosk-input-source-group.h"
+#include "kiosk-x-keyboard-manager.h"
#define SD_LOCALE1_BUS_NAME "org.freedesktop.locale1"
#define SD_LOCALE1_OBJECT_PATH "/org/freedesktop/locale1"
#define KIOSK_INPUT_SOURCES_SCHEMA "org.gnome.desktop.input-sources"
#define KIOSK_INPUT_SOURCES_SETTING "sources"
#define KIOSK_INPUT_OPTIONS_SETTING "xkb-options"
#define KIOSK_INPUT_SOURCE_OBJECTS_PATH_PREFIX "/org/gnome/Kiosk/InputSources"
#define KIOSK_KEYBINDINGS_SCHEMA "org.gnome.desktop.wm.keybindings"
#define KIOSK_SWITCH_INPUT_SOURCES_KEYBINDING "switch-input-source"
#define KIOSK_SWITCH_INPUT_SOURCES_BACKWARD_KEYBINDING "switch-input-source-backward"
#define KIOSK_DBUS_INPUT_SOURCES_MANGER_INPUT_SOURCE_INTERFACE "org.gnome.Kiosk.InputSources.InputSource"
struct _KioskInputSourcesManager
{
GObject parent;
/* weak references */
KioskCompositor *compositor;
MetaDisplay *display;
KioskDBusInputSourcesManager *dbus_service;
GDBusObjectManagerServer *dbus_object_manager;
/* strong references */
GCancellable *cancellable;
KioskInputEngineManager *input_engine_manager;
+ KioskXKeyboardManager *x_keyboard_manager;
SdLocale1 *locale_proxy;
GnomeXkbInfo *xkb_info;
GSettings *input_sources_settings;
GSettings *key_binding_settings;
GPtrArray *input_source_groups;
/* state */
ssize_t input_source_groups_index;
/* flags */
guint32 overriding_configuration : 1;
};
enum
{
PROP_COMPOSITOR = 1,
NUMBER_OF_PROPERTIES
};
static GParamSpec *kiosk_input_sources_manager_properties[NUMBER_OF_PROPERTIES] = { NULL, };
G_DEFINE_TYPE (KioskInputSourcesManager, kiosk_input_sources_manager, G_TYPE_OBJECT)
static void kiosk_input_sources_manager_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *param_spec);
static void kiosk_input_sources_manager_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *param_spec);
@@ -1363,60 +1368,189 @@ on_input_engine_manager_active_engine_changed (KioskInputSourcesManager *self)
active_input_engine = kiosk_input_engine_manager_get_active_engine (self->input_engine_manager);
if (active_input_engine == NULL) {
g_debug ("KioskInputSourcesManager: Input engine deactivated, activating first available input source");
activate_first_available_input_source_group (self);
return;
}
activate_input_source_group_with_engine (self, active_input_engine);
}
static void
kiosk_input_sources_manager_start_input_engine_manager (KioskInputSourcesManager *self)
{
self->input_engine_manager = kiosk_input_engine_manager_new (self);
g_signal_connect_object (G_OBJECT (self->input_engine_manager),
"notify::is-loaded",
G_CALLBACK (on_input_engine_manager_is_loaded_changed),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (G_OBJECT (self->input_engine_manager),
"notify::active-engine",
G_CALLBACK (on_input_engine_manager_active_engine_changed),
self,
G_CONNECT_SWAPPED);
}
+static void
+process_x_keyboard_manager_selected_layout_change (KioskInputSourcesManager *self)
+{
+ const char *selected_layout;
+
+ selected_layout = kiosk_x_keyboard_manager_get_selected_layout (self->x_keyboard_manager);
+
+ if (selected_layout == NULL) {
+ return;
+ }
+
+ g_debug ("KioskInputSourcesManager: X server changed active layout to %s", selected_layout);
+
+ activate_input_source_group_with_layout (self, selected_layout);
+
+ sync_dbus_service (self);
+}
+
+static void
+on_x_keyboard_manager_selected_layout_changed (KioskInputSourcesManager *self)
+{
+ kiosk_gobject_utils_queue_defer_callback (G_OBJECT (self),
+ "[kiosk-input-sources-manager] process_x_keyboard_manager_selected_layout_change",
+ self->cancellable,
+ KIOSK_OBJECT_CALLBACK (process_x_keyboard_manager_selected_layout_change),
+ NULL);
+}
+
+static gboolean
+layouts_match_selected_input_source_group (KioskInputSourcesManager *self,
+ const char * const *layouts,
+ const char *options)
+{
+ KioskInputSourceGroup *input_source_group;
+
+ g_auto (GStrv) current_layouts = NULL;
+ const char *input_source_group_options;
+ const char *input_engine_name;
+
+ input_source_group = kiosk_input_sources_manager_get_selected_input_source_group (self);
+
+ if (input_source_group == NULL) {
+ return FALSE;
+ }
+
+ input_engine_name = kiosk_input_source_group_get_input_engine (input_source_group);
+
+ if (input_engine_name != NULL) {
+ return FALSE;
+ }
+
+ current_layouts = kiosk_input_source_group_get_layouts (input_source_group);
+
+ if (!g_strv_equal ((const char * const *) current_layouts, layouts)) {
+ return FALSE;
+ }
+
+ input_source_group_options = kiosk_input_source_group_get_options (input_source_group);
+
+ if (g_strcmp0 (input_source_group_options, options) != 0) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+process_x_keyboard_manager_layouts_change (KioskInputSourcesManager *self)
+{
+ const char * const *new_layouts;
+ const char *selected_layout;
+ const char *options;
+ gboolean layouts_match;
+ size_t i;
+
+ new_layouts = kiosk_x_keyboard_manager_get_layouts (self->x_keyboard_manager);
+ options = kiosk_x_keyboard_manager_get_options (self->x_keyboard_manager);
+ layouts_match = layouts_match_selected_input_source_group (self, new_layouts, options);
+
+ if (layouts_match) {
+ return;
+ }
+
+ g_debug ("KioskInputSorcesManager: X server keyboard layouts changed");
+
+ self->overriding_configuration = TRUE;
+ kiosk_input_sources_manager_clear_input_sources (self);
+
+ for (i = 0; new_layouts[i] != NULL; i++) {
+ kiosk_input_sources_manager_add_layout (self, new_layouts[i], options);
+ }
+
+ selected_layout = kiosk_x_keyboard_manager_get_selected_layout (self->x_keyboard_manager);
+
+ if (selected_layout != NULL) {
+ activate_best_available_input_source_group (self, NULL, selected_layout);
+ }
+}
+
+static void
+on_x_keyboard_manager_layouts_changed (KioskInputSourcesManager *self)
+{
+ kiosk_gobject_utils_queue_defer_callback (G_OBJECT (self),
+ "[kiosk-input-sources-manager] process_x_keyboard_manager_layouts_change",
+ self->cancellable,
+ KIOSK_OBJECT_CALLBACK (process_x_keyboard_manager_layouts_change),
+ NULL);
+}
+
+static void
+kiosk_input_source_manager_start_x_keyboard_manager (KioskInputSourcesManager *self)
+{
+ if (meta_is_wayland_compositor ()) {
+ return;
+ }
+
+ self->x_keyboard_manager = kiosk_x_keyboard_manager_new (self->compositor);
+ g_signal_connect_object (G_OBJECT (self->x_keyboard_manager),
+ "notify::selected-layout",
+ G_CALLBACK (on_x_keyboard_manager_selected_layout_changed),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (G_OBJECT (self->x_keyboard_manager),
+ "notify::layouts",
+ G_CALLBACK (on_x_keyboard_manager_layouts_changed),
+ self,
+ G_CONNECT_SWAPPED);
+}
+
static void
kiosk_input_sources_manager_handle_dbus_service (KioskInputSourcesManager *self)
{
KioskService *service;
service = kiosk_compositor_get_service (self->compositor);
g_set_weak_pointer (&self->dbus_service, KIOSK_DBUS_INPUT_SOURCES_MANAGER (kiosk_service_get_input_sources_manager_skeleton (service)));
g_set_weak_pointer (&self->dbus_object_manager, kiosk_service_get_input_sources_object_manager (service));
g_signal_connect_object (G_OBJECT (self->dbus_service),
"handle-set-input-sources",
G_CALLBACK (on_dbus_service_handle_set_input_sources),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (G_OBJECT (self->dbus_service),
"handle-set-input-sources-from-locales",
G_CALLBACK (on_dbus_service_handle_set_input_sources_from_locales),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (G_OBJECT (self->dbus_service),
"handle-set-input-sources-from-session-configuration",
G_CALLBACK (on_dbus_service_handle_set_input_sources_from_session_configuration),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (G_OBJECT (self->dbus_service),
"handle-select-input-source",
G_CALLBACK (on_dbus_service_handle_select_input_source),
self,
G_CONNECT_SWAPPED);
@@ -1430,63 +1564,71 @@ kiosk_input_sources_manager_handle_dbus_service (KioskInputSourcesManager *self)
G_CALLBACK (on_dbus_service_handle_select_previous_input_source),
self,
G_CONNECT_SWAPPED);
}
static void
kiosk_input_sources_manager_constructed (GObject *object)
{
KioskInputSourcesManager *self = KIOSK_INPUT_SOURCES_MANAGER (object);
g_debug ("KioskInputSourcesManager: Initializing");
G_OBJECT_CLASS (kiosk_input_sources_manager_parent_class)->constructed (object);
g_set_weak_pointer (&self->display, meta_plugin_get_display (META_PLUGIN (self->compositor)));
self->cancellable = g_cancellable_new ();
self->xkb_info = gnome_xkb_info_new ();
self->input_source_groups = g_ptr_array_new_full (1, g_object_unref);
kiosk_input_sources_manager_handle_dbus_service (self);
kiosk_input_sources_manager_start_input_engine_manager (self);
kiosk_input_sources_manager_connect_to_localed (self);
kiosk_input_sources_manager_add_key_bindings (self);
kiosk_input_sources_manager_set_input_sources_from_session_configuration (self);
+
+ kiosk_gobject_utils_queue_defer_callback (G_OBJECT (self),
+ "[kiosk-input-sources-manager] kiosk_input_source_manager_start_x_keyboard_manager",
+ self->cancellable,
+ KIOSK_OBJECT_CALLBACK (kiosk_input_source_manager_start_x_keyboard_manager ),
+ NULL);
}
static void
kiosk_input_sources_manager_init (KioskInputSourcesManager *self)
{
}
static void
kiosk_input_sources_manager_dispose (GObject *object)
{
KioskInputSourcesManager *self = KIOSK_INPUT_SOURCES_MANAGER (object);
if (self->cancellable != NULL) {
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
}
kiosk_input_sources_manager_clear_input_sources (self);
g_clear_object (&self->input_engine_manager);
+ g_clear_object (&self->x_keyboard_manager);
g_clear_object (&self->xkb_info);
g_clear_object (&self->locale_proxy);
kiosk_input_sources_manager_remove_key_bindings (self);
g_clear_weak_pointer (&self->dbus_service);
g_clear_weak_pointer (&self->dbus_object_manager);
+
g_clear_weak_pointer (&self->display);
g_clear_weak_pointer (&self->compositor);
G_OBJECT_CLASS (kiosk_input_sources_manager_parent_class)->dispose (object);
}
diff --git a/compositor/kiosk-x-keyboard-manager.c b/compositor/kiosk-x-keyboard-manager.c
new file mode 100644
index 0000000..d5c64fb
--- /dev/null
+++ b/compositor/kiosk-x-keyboard-manager.c
@@ -0,0 +1,579 @@
+#include "config.h"
+#include "kiosk-x-keyboard-manager.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <meta/display.h>
+#include <meta/util.h>
+
+#include <meta/meta-backend.h>
+#include <meta/meta-x11-display.h>
+#include <meta/meta-x11-errors.h>
+
+#include <X11/Xatom.h>
+#include <X11/XKBlib.h>
+
+#include "kiosk-compositor.h"
+#include "kiosk-gobject-utils.h"
+
+struct _KioskXKeyboardManager
+{
+ GObject parent;
+
+ /* weak references */
+ KioskCompositor *compositor;
+ MetaBackend *backend;
+ MetaDisplay *display;
+ MetaX11Display *x11_display;
+ Display *x_server_display;
+
+ /* strong references */
+ GCancellable *cancellable;
+ GBytes *xkb_rules_names_data;
+ char **layouts;
+ char *options;
+
+ /* state */
+ Window x_server_root_window;
+ Atom xkb_rules_names_atom;
+ int xkb_event_base;
+
+ size_t layout_index;
+
+ /* flags */
+ guint32 watching_x_server_root_window : 1;
+ guint32 disallow_layout_selection: 1;
+};
+
+enum
+{
+ PROP_COMPOSITOR = 1,
+ PROP_LAYOUTS,
+ PROP_SELECTED_LAYOUT,
+ NUMBER_OF_PROPERTIES
+};
+
+static GParamSpec *kiosk_x_keyboard_manager_properties[NUMBER_OF_PROPERTIES] = { NULL, };
+
+G_DEFINE_TYPE (KioskXKeyboardManager, kiosk_x_keyboard_manager, G_TYPE_OBJECT)
+
+static void kiosk_x_keyboard_manager_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *param_spec);
+static void kiosk_x_keyboard_manager_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *param_spec);
+
+static void kiosk_x_keyboard_manager_constructed (GObject *object);
+static void kiosk_x_keyboard_manager_dispose (GObject *object);
+
+KioskXKeyboardManager *
+kiosk_x_keyboard_manager_new (KioskCompositor *compositor)
+{
+ GObject *object;
+
+ object = g_object_new (KIOSK_TYPE_X_KEYBOARD_MANAGER,
+ "compositor", compositor,
+ NULL);
+
+ return KIOSK_X_KEYBOARD_MANAGER (object);
+}
+
+static void
+kiosk_x_keyboard_manager_class_init (KioskXKeyboardManagerClass *x_keyboard_manager_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (x_keyboard_manager_class);
+
+ object_class->constructed = kiosk_x_keyboard_manager_constructed;
+ object_class->set_property = kiosk_x_keyboard_manager_set_property;
+ object_class->get_property = kiosk_x_keyboard_manager_get_property;
+ object_class->dispose = kiosk_x_keyboard_manager_dispose;
+
+ kiosk_x_keyboard_manager_properties[PROP_COMPOSITOR] = g_param_spec_object ("compositor",
+ "compositor",
+ "compositor",
+ KIOSK_TYPE_COMPOSITOR,
+ G_PARAM_CONSTRUCT_ONLY
+ | G_PARAM_WRITABLE
+ | G_PARAM_STATIC_NAME
+ | G_PARAM_STATIC_NICK
+ | G_PARAM_STATIC_BLURB);
+ kiosk_x_keyboard_manager_properties[PROP_SELECTED_LAYOUT] = g_param_spec_string ("selected-layout",
+ "selected-layout",
+ "selected-layout",
+ NULL,
+ G_PARAM_READABLE
+ | G_PARAM_STATIC_NAME
+ | G_PARAM_STATIC_NICK
+ | G_PARAM_STATIC_BLURB);
+
+ kiosk_x_keyboard_manager_properties[PROP_LAYOUTS] = g_param_spec_boxed ("layouts",
+ "layouts",
+ "layouts",
+ G_TYPE_STRV,
+ G_PARAM_READABLE
+ | G_PARAM_STATIC_NAME
+ | G_PARAM_STATIC_NICK
+ | G_PARAM_STATIC_BLURB);
+
+ g_object_class_install_properties (object_class, NUMBER_OF_PROPERTIES, kiosk_x_keyboard_manager_properties);
+}
+
+static void
+kiosk_x_keyboard_manager_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *param_spec)
+{
+ KioskXKeyboardManager *self = KIOSK_X_KEYBOARD_MANAGER (object);
+
+ switch (property_id) {
+ case PROP_COMPOSITOR:
+ g_set_weak_pointer (&self->compositor, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
+ break;
+ }
+}
+
+static void
+kiosk_x_keyboard_manager_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *param_spec)
+{
+ KioskXKeyboardManager *self = KIOSK_X_KEYBOARD_MANAGER (object);
+
+ switch (property_id) {
+ case PROP_SELECTED_LAYOUT:
+ g_value_set_string (value, self->layouts[self->layout_index]);
+ break;
+ case PROP_LAYOUTS:
+ g_value_set_boxed (value, self->layouts);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
+ break;
+ }
+}
+
+static char **
+qualify_layouts_with_variants (KioskXKeyboardManager *self,
+ const char * const *layouts,
+ const char * const *variants)
+{
+ size_t number_of_layouts = 0;
+ size_t number_of_variants = 0;
+ char **fully_qualified_layouts = NULL;
+
+ size_t i, j;
+
+ g_return_val_if_fail (KIOSK_IS_X_KEYBOARD_MANAGER (self), FALSE);
+
+ number_of_layouts = g_strv_length ((GStrv) layouts);
+ number_of_variants = g_strv_length ((GStrv) variants);
+
+ if (number_of_layouts < number_of_variants) {
+ g_debug ("KioskXKeyboardManager: There is a layout variant mismatch");
+ return NULL;
+ }
+
+ fully_qualified_layouts = g_new0 (char *, number_of_layouts + 1);
+
+ for (i = 0, j = 0; layouts[i] != NULL; i++) {
+ const char *layout = layouts[i];
+ const char *variant = "";
+
+ if (variants[j] != NULL) {
+ variant = variants[j++];
+ }
+
+ if (variant[0] == '\0') {
+ fully_qualified_layouts[i] = g_strdup (layout);
+ } else {
+ fully_qualified_layouts[i] = g_strdup_printf ("%s+%s", layout, variant);
+ }
+ }
+
+ return fully_qualified_layouts;
+}
+
+static void
+kiosk_x_keyboard_manager_disallow_layout_selection (KioskXKeyboardManager *self)
+{
+ self->disallow_layout_selection = TRUE;
+}
+
+static void
+kiosk_x_keyboard_manager_allow_layout_selection (KioskXKeyboardManager *self)
+{
+ self->disallow_layout_selection = FALSE;
+}
+
+static void
+kiosk_x_keyboard_manager_set_layout_index (KioskXKeyboardManager *self,
+ size_t layout_index)
+{
+ size_t number_of_layouts;
+
+ if (self->layout_index == layout_index) {
+ return;
+ }
+
+ g_debug ("KioskXKeyboardManager: X server is using layout with index %ld",
+ layout_index);
+
+ number_of_layouts = g_strv_length (self->layouts);
+
+ if (layout_index >= number_of_layouts) {
+ layout_index = 0;
+ }
+
+ self->layout_index = layout_index;
+ g_object_notify (G_OBJECT (self), "selected-layout");
+}
+
+static void
+kiosk_x_keyboard_manager_read_current_layout_index (KioskXKeyboardManager *self)
+{
+ XkbStateRec xkb_state = { 0 };
+ int status;
+
+ meta_x11_error_trap_push (self->x11_display);
+ status = XkbGetState (self->x_server_display, XkbUseCoreKbd, &xkb_state);
+ meta_x11_error_trap_pop (self->x11_display);
+
+ if (status != Success) {
+ g_debug ("KioskXKeyboardManager: Could not read current layout index");
+ return;
+ }
+ g_debug ("KioskXKeyboardManager: Current layout index: %u", xkb_state.locked_group);
+
+ kiosk_x_keyboard_manager_set_layout_index (self, xkb_state.locked_group);
+}
+
+static gboolean
+kiosk_x_keyboard_manager_read_xkb_rules_names_data (KioskXKeyboardManager *self)
+{
+ g_autoptr (GBytes) new_xkb_rules_names_data = NULL;
+ g_autoptr (GVariant) input_source_group = NULL;
+ size_t number_of_layouts = 0;
+ g_autofree char *layouts_string = NULL;
+ g_autofree char *variants_string = NULL;
+ g_autofree char *options = NULL;
+ g_auto (GStrv) layouts = NULL;
+ g_auto (GStrv) variants = NULL;
+ g_auto (GStrv) qualified_layouts = NULL;
+ int status;
+ Atom returned_type = 0;
+ int returned_format = 0;
+ gulong number_of_bytes_read = 0;
+ gulong number_of_bytes_unread = 0;
+ guchar *property_values;
+ size_t i;
+ enum {
+ RULES_NAME = 0,
+ MODEL,
+ LAYOUTS,
+ VARIANTS,
+ OPTIONS
+ } property_value_index;
+
+ kiosk_x_keyboard_manager_allow_layout_selection (self);
+
+ g_debug ("KioskXKeyboardManager: Reading active keyboard layouts from X server");
+
+ status = XGetWindowProperty (self->x_server_display,
+ self->x_server_root_window,
+ self->xkb_rules_names_atom,
+ 0, 1024, FALSE, XA_STRING,
+ &returned_type,
+ &returned_format,
+ &number_of_bytes_read,
+ &number_of_bytes_unread,
+ &property_values);
+
+ if (status != Success) {
+ g_debug ("KioskXKeyboardManager: Could not read active keyboard layouts from X server");
+ return FALSE;
+ }
+
+ if (returned_type != XA_STRING ||
+ returned_format != 8 ||
+ number_of_bytes_unread != 0) {
+ g_debug ("KioskXKeyboardManager: Active keyboard layouts propery from X server is corrupted");
+ return FALSE;
+ }
+
+ new_xkb_rules_names_data = g_bytes_new_with_free_func (property_values,
+ number_of_bytes_read,
+ (GDestroyNotify) XFree,
+ NULL);
+
+ if (self->xkb_rules_names_data != NULL && g_bytes_equal (self->xkb_rules_names_data, new_xkb_rules_names_data)) {
+ g_debug ("KioskXKeyboardManager: XKB rules names data is unchanged");
+ return FALSE;
+ }
+
+ g_clear_pointer (&self->xkb_rules_names_data, g_bytes_unref);
+ self->xkb_rules_names_data = g_steal_pointer (&new_xkb_rules_names_data);
+
+ property_value_index = 0;
+ for (i = 0; i < number_of_bytes_read; i++) {
+ g_autofree char *value = g_strdup ((char *) property_values + i);
+ size_t value_length = strlen (value);
+
+ switch (property_value_index) {
+ case RULES_NAME:
+ case MODEL:
+ break;
+ case LAYOUTS:
+ layouts_string = g_steal_pointer (&value);
+ g_debug ("KioskXKeyboardManager: Read layouts '%s'", layouts_string);
+ break;
+ case VARIANTS:
+ variants_string = g_steal_pointer (&value);
+ g_debug ("KioskXKeyboardManager: Read variants '%s'", variants_string);
+ break;
+ case OPTIONS:
+ g_debug ("KioskXKeyboardManager: Read options '%s'", options);
+ options = g_steal_pointer (&value);
+ break;
+ }
+
+ i += value_length;
+ property_value_index++;
+ }
+
+ layouts = g_strsplit (layouts_string, ",", -1);
+ variants = g_strsplit (variants_string, ",", -1);
+
+ qualified_layouts = qualify_layouts_with_variants (self, (const char * const *)layouts, (const char * const *)variants);
+
+ if (qualified_layouts == NULL) {
+ g_debug ("KioskXKeyboardManager: Unable to qualify layouts with variants");
+ return FALSE;
+ }
+
+ number_of_layouts = g_strv_length (qualified_layouts);
+
+ if (number_of_layouts == 0) {
+ g_debug ("KioskXKeyboardManager: No layouts found");
+ return FALSE;
+ }
+
+ g_clear_pointer (&self->layouts, g_strfreev);
+ self->layouts = g_steal_pointer (&qualified_layouts);
+ self->options = g_steal_pointer (&options);
+
+ g_object_freeze_notify (G_OBJECT (self));
+ g_object_notify (G_OBJECT (self), "layouts");
+ kiosk_x_keyboard_manager_read_current_layout_index (self);
+ g_object_thaw_notify (G_OBJECT (self));
+
+ return TRUE;
+}
+
+static void
+on_x_server_xkb_rules_names_data_changed (KioskXKeyboardManager *self)
+{
+ g_debug ("KioskXKeyboardManager: X server keyboard layouts changed");
+
+ kiosk_x_keyboard_manager_read_xkb_rules_names_data (self);
+}
+
+static void
+monitor_x_server_display_for_property_changes (KioskXKeyboardManager *self,
+ Display *x_server_display)
+{
+ XWindowAttributes attributes;
+
+ if (self->watching_x_server_root_window) {
+ return;
+ }
+
+ XGetWindowAttributes (x_server_display, self->x_server_root_window, &attributes);
+
+ if (!(attributes.your_event_mask & PropertyChangeMask)) {
+ XSelectInput (x_server_display,
+ self->x_server_root_window,
+ attributes.your_event_mask | PropertyChangeMask);
+ }
+
+ self->watching_x_server_root_window = TRUE;
+}
+
+static void
+kiosk_x_keyboard_manager_handle_x_server_property_notify (KioskXKeyboardManager *self,
+ XPropertyEvent *x_server_event)
+{
+ if (x_server_event->window != self->x_server_root_window) {
+ return;
+ }
+
+ if (x_server_event->atom != self->xkb_rules_names_atom) {
+ return;
+ }
+
+ g_debug ("KioskXKeyboardManager: XKB rules names data changed in X server");
+
+ /* We block layout index changes until we have a chance to figure out what the new layouts are
+ */
+ kiosk_x_keyboard_manager_disallow_layout_selection (self);
+
+ kiosk_gobject_utils_queue_defer_callback (G_OBJECT (self),
+ "[kiosk-input-sources-manager] on_x_server_xkb_rules_names_data_changed",
+ self->cancellable,
+ KIOSK_OBJECT_CALLBACK (on_x_server_xkb_rules_names_data_changed),
+ NULL);
+}
+
+static void
+kiosk_x_keyboard_manager_handle_xkb_event (KioskXKeyboardManager *self,
+ XkbEvent *x_server_event)
+{
+ size_t layout_index;
+
+ layout_index = XkbStateGroup (&x_server_event->state);
+ switch (x_server_event->any.xkb_type) {
+ case XkbStateNotify:
+ if (!(x_server_event->state.changed & XkbGroupStateMask)) {
+ return;
+ }
+ if (self->disallow_layout_selection) {
+ g_debug ("KioskXKeyboardManager: Ignoring layout change request to group %lu", layout_index);
+ return;
+ }
+ g_debug ("KioskXKeyboardManager: Approving keyboard group change to group %lu", layout_index);
+ meta_backend_lock_layout_group (self->backend, layout_index);
+ kiosk_x_keyboard_manager_set_layout_index (self, layout_index);
+ break;
+ }
+}
+
+static void
+on_x_server_event (KioskXKeyboardManager *self,
+ XEvent *x_server_event)
+{
+ monitor_x_server_display_for_property_changes (self, x_server_event->xany.display);
+
+ switch (x_server_event->type) {
+ case PropertyNotify:
+ kiosk_x_keyboard_manager_handle_x_server_property_notify (self, &x_server_event->xproperty);
+ break;
+ default:
+ if (x_server_event->type == self->xkb_event_base) {
+ kiosk_x_keyboard_manager_handle_xkb_event (self, (XkbEvent *) x_server_event);
+ }
+ break;
+ }
+}
+
+const char * const *
+kiosk_x_keyboard_manager_get_layouts (KioskXKeyboardManager *self)
+{
+ g_return_val_if_fail (KIOSK_IS_X_KEYBOARD_MANAGER (self), NULL);
+
+ return (const char * const *) self->layouts;
+}
+
+const char *
+kiosk_x_keyboard_manager_get_selected_layout (KioskXKeyboardManager *self)
+{
+ g_return_val_if_fail (KIOSK_IS_X_KEYBOARD_MANAGER (self), NULL);
+
+ if (self->layouts == NULL) {
+ return NULL;
+ }
+
+ return self->layouts[self->layout_index];
+}
+
+const char *
+kiosk_x_keyboard_manager_get_options (KioskXKeyboardManager *self)
+{
+ g_return_val_if_fail (KIOSK_IS_X_KEYBOARD_MANAGER (self), NULL);
+
+ return self->options;
+}
+
+static void
+kiosk_x_keyboard_manager_init (KioskXKeyboardManager *self)
+{
+}
+
+static void
+kiosk_x_keyboard_manager_initialize_xkb_extension (KioskXKeyboardManager *self)
+{
+ int major = XkbMajorVersion;
+ int minor = XkbMinorVersion;
+
+ XkbQueryExtension (self->x_server_display, NULL, &self->xkb_event_base, NULL, &major, &minor);
+
+ self->xkb_rules_names_atom = XInternAtom (self->x_server_display, "_XKB_RULES_NAMES", False);
+}
+
+static void
+kiosk_x_keyboard_manager_constructed (GObject *object)
+{
+ KioskXKeyboardManager *self = KIOSK_X_KEYBOARD_MANAGER (object);
+
+ g_debug ("KioskXKeyboardManager: Initializing");
+
+ G_OBJECT_CLASS (kiosk_x_keyboard_manager_parent_class)->constructed (object);
+
+ self->cancellable = g_cancellable_new ();
+
+ g_set_weak_pointer (&self->backend, meta_get_backend ());
+ g_set_weak_pointer (&self->display, meta_plugin_get_display (META_PLUGIN (self->compositor)));
+ g_set_weak_pointer (&self->x11_display, meta_display_get_x11_display (self->display));
+
+ self->x_server_display = meta_x11_display_get_xdisplay (self->x11_display);
+ g_object_add_weak_pointer (G_OBJECT (self->x11_display), (gpointer *) &self->x_server_display);
+
+ self->x_server_root_window = meta_x11_display_get_xroot (self->x11_display);
+ g_object_add_weak_pointer (G_OBJECT (self->x11_display), (gpointer *) &self->x_server_root_window);
+
+ kiosk_x_keyboard_manager_initialize_xkb_extension (self);
+
+ g_signal_connect_object (G_OBJECT (self->compositor),
+ "x-server-event",
+ G_CALLBACK (on_x_server_event),
+ self,
+ G_CONNECT_SWAPPED);
+}
+
+static void
+kiosk_x_keyboard_manager_dispose (GObject *object)
+{
+ KioskXKeyboardManager *self = KIOSK_X_KEYBOARD_MANAGER (object);
+
+ g_debug ("KioskXKeyboardManager: Disposing");
+
+ if (self->cancellable != NULL) {
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->cancellable);
+ }
+
+ g_clear_pointer (&self->xkb_rules_names_data, g_bytes_unref);
+ g_clear_pointer (&self->layouts, g_strfreev);
+ g_clear_pointer (&self->options, g_free);
+
+ if (self->x11_display != NULL) {
+ g_object_remove_weak_pointer (G_OBJECT (self->x11_display),
+ (gpointer *) &self->x_server_display);
+ g_object_remove_weak_pointer (G_OBJECT (self->x11_display),
+ (gpointer *) &self->x_server_root_window);
+ }
+ g_clear_weak_pointer (&self->x11_display);
+
+ g_clear_weak_pointer (&self->display);
+ g_clear_weak_pointer (&self->backend);
+ g_clear_weak_pointer (&self->compositor);
+
+ G_OBJECT_CLASS (kiosk_x_keyboard_manager_parent_class)->dispose (object);
+}
diff --git a/compositor/kiosk-x-keyboard-manager.h b/compositor/kiosk-x-keyboard-manager.h
new file mode 100644
index 0000000..b402465
--- /dev/null
+++ b/compositor/kiosk-x-keyboard-manager.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <glib-object.h>
+
+typedef struct _KioskCompositor KioskCompositor;
+
+G_BEGIN_DECLS
+
+#define KIOSK_TYPE_X_KEYBOARD_MANAGER (kiosk_x_keyboard_manager_get_type ())
+
+G_DECLARE_FINAL_TYPE (KioskXKeyboardManager,
+ kiosk_x_keyboard_manager,
+ KIOSK, X_KEYBOARD_MANAGER,
+ GObject)
+
+KioskXKeyboardManager *kiosk_x_keyboard_manager_new (KioskCompositor *compositor);
+
+const char * const *kiosk_x_keyboard_manager_get_layouts (KioskXKeyboardManager *manager);
+const char *kiosk_x_keyboard_manager_get_selected_layout (KioskXKeyboardManager *manager);
+const char *kiosk_x_keyboard_manager_get_options (KioskXKeyboardManager *manager);
+
+G_END_DECLS
diff --git a/meson.build b/meson.build
index 2b5640a..44afcea 100644
--- a/meson.build
+++ b/meson.build
@@ -86,60 +86,61 @@ sources = gnome.gdbus_codegen(dbus_interface, dbus_interface_file,
namespace: 'Kiosk',
interface_prefix: 'org.gnome',
annotations: [
[ dbus_interface, 'org.gtk.GDBus.C.Name', 'ShellDBusService' ]
]
)
dbus_interface_sources_map += { dbus_interface: sources }
compositor_dependencies = []
compositor_dependencies += c_compiler.find_library('m')
compositor_dependencies += dependency('gio-2.0')
compositor_dependencies += dependency('glib-2.0')
compositor_dependencies += dependency('gnome-desktop-3.0')
compositor_dependencies += dependency('gobject-2.0')
compositor_dependencies += dependency('ibus-1.0')
compositor_dependencies += dependency('mutter-cogl-8')
compositor_dependencies += dependency('mutter-cogl-pango-8')
compositor_dependencies += dependency('mutter-clutter-8')
compositor_dependencies += mutter_dependency
compositor_sources = []
compositor_sources += 'compositor/kiosk-backgrounds.c'
compositor_sources += 'compositor/kiosk-compositor.c'
compositor_sources += 'compositor/kiosk-dbus-utils.c'
compositor_sources += 'compositor/kiosk-gobject-utils.c'
compositor_sources += 'compositor/kiosk-input-sources-manager.c'
compositor_sources += 'compositor/kiosk-input-engine-manager.c'
compositor_sources += 'compositor/kiosk-input-source-group.c'
compositor_sources += 'compositor/kiosk-service.c'
compositor_sources += 'compositor/kiosk-shell-service.c'
+compositor_sources += 'compositor/kiosk-x-keyboard-manager.c'
compositor_sources += 'compositor/main.c'
foreach dbus_interface, sources: dbus_interface_sources_map
compositor_sources += sources
endforeach
executable('gnome-kiosk', compositor_sources,
dependencies: compositor_dependencies,
build_rpath: mutter_libdir,
install_rpath: mutter_libdir,
install: true
)
desktop_config_data = configuration_data()
desktop_config_data.set('bindir', bindir)
desktop_file = configure_file(
input: 'compositor/data/org.gnome.Kiosk.desktop.in.in',
output: 'org.gnome.Kiosk.desktop.in',
configuration: desktop_config_data
)
i18n.merge_file('desktop',
input: desktop_file,
output: 'org.gnome.Kiosk.desktop',
po_dir: po_dir,
install: true,
install_dir: desktop_data_dir,
type: 'desktop'
)
--
2.31.1