2021-04-20 14:10:50 +00:00
|
|
|
From 75e759aa88e75d55b956bd531509306f50e6fcc2 Mon Sep 17 00:00:00 2001
|
2021-04-19 02:10:43 +00:00
|
|
|
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 +-
|
2021-04-19 13:49:21 +00:00
|
|
|
compositor/kiosk-input-sources-manager.c | 142 ++++++
|
2021-04-20 14:10:50 +00:00
|
|
|
compositor/kiosk-x-keyboard-manager.c | 578 +++++++++++++++++++++++
|
2021-04-19 02:10:43 +00:00
|
|
|
compositor/kiosk-x-keyboard-manager.h | 22 +
|
|
|
|
meson.build | 1 +
|
2021-04-20 14:10:50 +00:00
|
|
|
6 files changed, 764 insertions(+), 1 deletion(-)
|
2021-04-19 02:10:43 +00:00
|
|
|
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
|
2021-04-19 13:49:21 +00:00
|
|
|
index 4b4ef62..c6c8c91 100644
|
2021-04-19 02:10:43 +00:00
|
|
|
--- 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);
|
2021-04-19 13:49:21 +00:00
|
|
|
@@ -1363,60 +1368,189 @@ on_input_engine_manager_active_engine_changed (KioskInputSourcesManager *self)
|
2021-04-19 02:10:43 +00:00
|
|
|
|
|
|
|
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);
|
2021-04-19 13:49:21 +00:00
|
|
|
+
|
|
|
|
+ if (input_source_group == NULL) {
|
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
2021-04-19 02:10:43 +00:00
|
|
|
+ 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);
|
2021-04-19 13:49:21 +00:00
|
|
|
@@ -1430,63 +1564,71 @@ kiosk_input_sources_manager_handle_dbus_service (KioskInputSourcesManager *self)
|
2021-04-19 02:10:43 +00:00
|
|
|
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
|
2021-04-20 14:10:50 +00:00
|
|
|
index 0000000..23c3de3
|
2021-04-19 02:10:43 +00:00
|
|
|
--- /dev/null
|
|
|
|
+++ b/compositor/kiosk-x-keyboard-manager.c
|
2021-04-20 14:10:50 +00:00
|
|
|
@@ -0,0 +1,578 @@
|
2021-04-19 02:10:43 +00:00
|
|
|
+#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;
|
2021-04-20 14:10:50 +00:00
|
|
|
+ guint32 disallow_layout_selection: 1;
|
2021-04-19 02:10:43 +00:00
|
|
|
+};
|
|
|
|
+
|
|
|
|
+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
|
2021-04-20 14:10:50 +00:00
|
|
|
+kiosk_x_keyboard_manager_disallow_layout_selection (KioskXKeyboardManager *self)
|
|
|
|
+{
|
|
|
|
+ self->disallow_layout_selection = FALSE;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+kiosk_x_keyboard_manager_allow_layout_selection (KioskXKeyboardManager *self)
|
|
|
|
+{
|
|
|
|
+ self->disallow_layout_selection = TRUE;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
2021-04-19 02:10:43 +00:00
|
|
|
+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) {
|
2021-04-20 14:10:50 +00:00
|
|
|
+ g_debug ("KioskXKeyboardManager: Could not read current layout index");
|
2021-04-19 02:10:43 +00:00
|
|
|
+ return;
|
|
|
|
+ }
|
2021-04-20 14:10:50 +00:00
|
|
|
+ g_debug ("KioskXKeyboardManager: Current layout index: %u", xkb_state.locked_group);
|
2021-04-19 02:10:43 +00:00
|
|
|
+
|
|
|
|
+ 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;
|
|
|
|
+
|
|
|
|
+ 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);
|
2021-04-20 14:10:50 +00:00
|
|
|
+ g_debug ("KioskXKeyboardManager: Read layouts '%s'", layouts_string);
|
2021-04-19 02:10:43 +00:00
|
|
|
+ break;
|
|
|
|
+ case VARIANTS:
|
|
|
|
+ variants_string = g_steal_pointer (&value);
|
2021-04-20 14:10:50 +00:00
|
|
|
+ g_debug ("KioskXKeyboardManager: Read variants '%s'", variants_string);
|
2021-04-19 02:10:43 +00:00
|
|
|
+ break;
|
|
|
|
+ case OPTIONS:
|
2021-04-20 14:10:50 +00:00
|
|
|
+ g_debug ("KioskXKeyboardManager: Read options '%s'", options);
|
2021-04-19 02:10:43 +00:00
|
|
|
+ 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) {
|
2021-04-20 14:10:50 +00:00
|
|
|
+ g_debug ("KioskXKeyboardManager: Unable to qualify layouts with variants");
|
2021-04-19 02:10:43 +00:00
|
|
|
+ return FALSE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ number_of_layouts = g_strv_length (qualified_layouts);
|
|
|
|
+
|
|
|
|
+ if (number_of_layouts == 0) {
|
2021-04-20 14:10:50 +00:00
|
|
|
+ g_debug ("KioskXKeyboardManager: No layouts found");
|
2021-04-19 02:10:43 +00:00
|
|
|
+ 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));
|
|
|
|
+
|
2021-04-20 14:10:50 +00:00
|
|
|
+ kiosk_x_keyboard_manager_allow_layout_selection (self);
|
|
|
|
+
|
2021-04-19 02:10:43 +00:00
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+
|
2021-04-20 14:10:50 +00:00
|
|
|
+ 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_diallow_layout_selection (self);
|
|
|
|
+
|
2021-04-19 02:10:43 +00:00
|
|
|
+ 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:
|
2021-04-20 14:10:50 +00:00
|
|
|
+ if (!(x_server_event->state.changed & XkbGroupStateMask)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ if (self->disallow_layout_selection) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
2021-04-19 02:10:43 +00:00
|
|
|
+ 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'
|
|
|
|
)
|
|
|
|
--
|
2021-04-19 13:49:21 +00:00
|
|
|
2.31.1
|
2021-04-19 02:10:43 +00:00
|
|
|
|