]> git.ipfire.org Git - thirdparty/plymouth.git/commitdiff
src: Add support for /dev/input devices
authorDiego Augusto <diego.augusto@protonmail.com>
Sat, 15 Oct 2022 17:45:42 +0000 (13:45 -0400)
committerRay Strode <rstrode@redhat.com>
Tue, 29 Nov 2022 14:22:06 +0000 (09:22 -0500)
Plymouth currently gets keyboard input from the terminal. This isn't
ideal, since it means plymouth requires VTs to be enabled in the kernel.

Furthermore, most display servers use /dev/input and libxkbcommon for
keyboard handling these days.

This commit adds similar support to the plymouth core code. Subsequent
commits will add support to the render plugins.

Some contributions by n3rdopolis and Ray Strode.

meson.build
src/libply-splash-core/meson.build
src/libply-splash-core/ply-device-manager.c
src/libply-splash-core/ply-input-device.c [new file with mode: 0644]
src/libply-splash-core/ply-input-device.h [new file with mode: 0644]
src/libply-splash-core/ply-renderer-plugin.h
src/libply-splash-core/ply-renderer.c
src/libply-splash-core/ply-renderer.h
src/libply-splash-core/ply-terminal.c
src/libply-splash-core/ply-terminal.h
src/libply-splash-graphics/ply-keymap-icon.c

index f5cb08699d4822f442113adddf5ea0c473247330..bd55c1c847ee5e719be8a57bedc2e3209b02b565 100644 (file)
@@ -43,6 +43,9 @@ libpangocairo_dep = dependency('pangocairo', required: get_option('pango'))
 libfreetype_dep = dependency('freetype2', required: get_option('freetype'))
 gtk3_dep = dependency('gtk+-3.0', version: '>= 3.14.0', required: get_option('gtk'))
 libdrm_dep = dependency('libdrm', required: get_option('drm'))
+libevdev_dep = dependency('libevdev')
+xkbcommon_dep = dependency('xkbcommon')
+xkeyboard_config_dep = dependency('xkeyboard-config')
 
 if get_option('systemd-integration')
   systemd_dep = dependency('systemd')
index 8914e8ec5e89aff38416f03d0867a1df37a789b1..69636b1338393562fd90af9c3034bdbf9c14e07e 100644 (file)
@@ -1,6 +1,7 @@
 libply_splash_core_sources = files(
   'ply-boot-splash.c',
   'ply-device-manager.c',
+  'ply-input-device.c',
   'ply-keyboard.c',
   'ply-pixel-buffer.c',
   'ply-pixel-display.c',
@@ -17,6 +18,9 @@ libply_splash_core_public_deps = [
 
 libply_splash_core_private_deps = [
   lm_dep,
+  libevdev_dep,
+  xkbcommon_dep,
+  xkeyboard_config_dep,
 ]
 
 if libudev_dep.found()
@@ -49,6 +53,7 @@ libply_splash_core_headers = files(
   'ply-boot-splash-plugin.h',
   'ply-boot-splash.h',
   'ply-device-manager.h',
+  'ply-input-device.h',
   'ply-keyboard.h',
   'ply-pixel-buffer.h',
   'ply-pixel-display.h',
index 7451922fd4705586f84cfdec0ef089274128194e..ca2af770ee952a55e48500fd7310100150cb17b3 100644 (file)
@@ -19,6 +19,7 @@
  */
 #include "config.h"
 #include "ply-device-manager.h"
+#include "ply-renderer.h"
 
 #include <assert.h>
 #include <fcntl.h>
 #include <libudev.h>
 #endif
 
+#include <xkbcommon/xkbcommon.h>
+
 #include "ply-logger.h"
 #include "ply-event-loop.h"
 #include "ply-hashtable.h"
 #include "ply-list.h"
+#include "ply-key-file.h"
 #include "ply-utils.h"
+#include "ply-input-device.h"
 
 #define SUBSYSTEM_DRM "drm"
 #define SUBSYSTEM_FRAME_BUFFER "graphics"
+#define SUBSYSTEM_INPUT "input"
 
 #ifdef HAVE_UDEV
 static void create_devices_from_udev (ply_device_manager_t *manager);
@@ -60,7 +66,9 @@ struct _ply_device_manager
         ply_event_loop_t                   *loop;
         ply_hashtable_t                    *terminals;
         ply_hashtable_t                    *renderers;
+        ply_hashtable_t                    *input_devices;
         ply_terminal_t                     *local_console_terminal;
+        const char                         *keymap;
         ply_list_t                         *keyboards;
         ply_list_t                         *text_displays;
         ply_list_t                         *pixel_displays;
@@ -68,6 +76,9 @@ struct _ply_device_manager
         struct udev_monitor                *udev_monitor;
         ply_fd_watch_t                     *fd_watch;
 
+        struct xkb_context                 *xkb_context;
+        struct xkb_keymap                  *xkb_keymap;
+
         ply_keyboard_added_handler_t        keyboard_added_handler;
         ply_keyboard_removed_handler_t      keyboard_removed_handler;
         ply_pixel_display_added_handler_t   pixel_display_added_handler;
@@ -262,6 +273,73 @@ fb_device_has_drm_device (ply_device_manager_t *manager,
         return has_drm_device;
 }
 
+static void
+on_each_renderer_add_input_device (const char         *key,
+                                   ply_renderer_t     *renderer,
+                                   ply_input_device_t *input_device)
+{
+        ply_trace ("Adding input device '%s' to renderer for output device '%s'",
+                   ply_input_device_get_name (input_device),
+                   ply_renderer_get_device_name (renderer));
+
+        ply_renderer_add_input_device (renderer, input_device);
+}
+
+static void
+add_input_device_to_renderers (ply_device_manager_t *manager,
+                               ply_input_device_t   *input_device)
+{
+        const char *device_path = ply_input_device_get_path (input_device);
+        if (ply_hashtable_lookup (manager->input_devices, (void *) device_path) != NULL) {
+                ply_trace ("Input device '%s' already added, skipping...", ply_input_device_get_name (input_device));
+                ply_input_device_free (input_device);
+                return;
+        }
+        ply_hashtable_insert (manager->input_devices, (void *) device_path, input_device);
+        ply_hashtable_foreach (manager->renderers,
+                               (ply_hashtable_foreach_func_t *)
+                               on_each_renderer_add_input_device,
+                               input_device);
+}
+
+static void
+on_each_input_device_add_to_renderer (const char         *key,
+                                      ply_input_device_t *input_device,
+                                      ply_renderer_t     *renderer)
+{
+        ply_trace ("Adding input device '%s' to renderer for output device '%s'",
+                   ply_input_device_get_name (input_device),
+                   ply_renderer_get_device_name (renderer));
+
+        ply_renderer_add_input_device (renderer, input_device);
+}
+
+static void
+add_input_devices_to_renderer (ply_device_manager_t *manager,
+                               ply_renderer_t       *renderer)
+{
+        ply_hashtable_foreach (manager->input_devices,
+                               (ply_hashtable_foreach_func_t *)
+                               on_each_input_device_add_to_renderer,
+                               renderer);
+}
+static void
+on_each_input_device_remove_from_renderer (const char         *key,
+                                           ply_renderer_t     *renderer,
+                                           ply_input_device_t *input_device)
+{
+        ply_renderer_remove_input_device (renderer, input_device);
+}
+
+static void
+remove_input_device_from_renderers (ply_device_manager_t *manager,
+                                    ply_input_device_t   *input_device)
+{
+        const char *device_path = ply_input_device_get_path (input_device);
+        ply_hashtable_remove (manager->input_devices, (void *) device_path);
+        ply_hashtable_foreach (manager->renderers, (ply_hashtable_foreach_func_t *) on_each_input_device_remove_from_renderer, input_device);
+}
+
 static bool
 verify_drm_device (struct udev_device *device)
 {
@@ -300,7 +378,7 @@ static bool
 create_devices_for_udev_device (ply_device_manager_t *manager,
                                 struct udev_device   *device)
 {
-        const char *device_path;
+        const char *device_path, *device_sysname;
         bool created = false;
         bool force_fb = false;
 
@@ -308,6 +386,7 @@ create_devices_for_udev_device (ply_device_manager_t *manager,
                 force_fb = true;
 
         device_path = udev_device_get_devnode (device);
+        device_sysname = udev_device_get_sysname (device);
 
         if (device_path != NULL) {
                 const char *subsystem;
@@ -331,6 +410,21 @@ create_devices_for_udev_device (ply_device_manager_t *manager,
                                 renderer_type = PLY_RENDERER_TYPE_FRAME_BUFFER;
                         else
                                 ply_trace ("ignoring, since there's a DRM device associated with it");
+                } else if (strcmp (subsystem, SUBSYSTEM_INPUT) == 0) {
+                        if (ply_string_has_prefix (device_sysname, "event")) {
+                                ply_trace ("found input device %s", device_path);
+                                ply_input_device_t *input_device = ply_input_device_open (manager->xkb_context, manager->xkb_keymap, device_path);
+                                if (input_device != NULL) {
+                                        ply_input_device_set_disconnect_handler (input_device, (ply_input_device_disconnect_handler_t) remove_input_device_from_renderers, manager);
+                                        if (ply_input_device_is_keyboard (input_device)) {
+                                                add_input_device_to_renderers (manager, input_device);
+                                        } else {
+                                                ply_input_device_free (input_device);
+                                        }
+                                }
+                        } else {
+                                ply_trace ("Ignoring, since this is a non-evdev device");
+                        }
                 }
 
                 if (renderer_type != PLY_RENDERER_TYPE_NONE) {
@@ -377,7 +471,7 @@ create_devices_for_subsystem (ply_device_manager_t *manager,
 
         udev_list_entry_foreach (entry, udev_enumerate_get_list_entry (matches)){
                 struct udev_device *device = NULL;
-                const char *path;
+                const char *path, *node;
 
                 path = udev_list_entry_get_name (entry);
 
@@ -395,18 +489,10 @@ create_devices_for_subsystem (ply_device_manager_t *manager,
                 if (udev_device_get_is_initialized (device)) {
                         ply_trace ("device is initialized");
 
-                        /* We only care about devices assigned to a (any) devices. Floating
-                         * devices should be ignored.
-                         */
-                        if (udev_device_has_tag (device, "seat")) {
-                                const char *node;
-                                node = udev_device_get_devnode (device);
-                                if (node != NULL) {
-                                        ply_trace ("found node %s", node);
-                                        found_device = create_devices_for_udev_device (manager, device);
-                                }
-                        } else {
-                                ply_trace ("device doesn't have a devices tag");
+                        node = udev_device_get_devnode (device);
+                        if (node != NULL) {
+                                ply_trace ("found node %s", node);
+                                found_device = create_devices_for_udev_device (manager, device);
                         }
                 } else {
                         ply_trace ("it's not initialized");
@@ -580,14 +666,15 @@ watch_for_udev_events (ply_device_manager_t *manager)
         if (manager->fd_watch != NULL)
                 return;
 
-        ply_trace ("watching for udev graphics device add and remove events");
+        ply_trace ("watching for udev graphics device and input device add and remove events");
 
         if (manager->udev_monitor == NULL) {
                 manager->udev_monitor = udev_monitor_new_from_netlink (manager->udev_context, "udev");
 
                 udev_monitor_filter_add_match_subsystem_devtype (manager->udev_monitor, SUBSYSTEM_DRM, NULL);
                 udev_monitor_filter_add_match_subsystem_devtype (manager->udev_monitor, SUBSYSTEM_FRAME_BUFFER, NULL);
-                udev_monitor_filter_add_match_tag (manager->udev_monitor, "seat");
+                if (!ply_kernel_command_line_has_argument ("plymouth.use-legacy-input"))
+                        udev_monitor_filter_add_match_subsystem_devtype (manager->udev_monitor, SUBSYSTEM_INPUT, NULL);
                 udev_monitor_enable_receiving (manager->udev_monitor);
         }
 
@@ -631,6 +718,21 @@ free_terminals (ply_device_manager_t *manager)
                                manager);
 }
 
+static void
+free_input_device (char                 *device,
+                   ply_input_device_t   *input_device,
+                   ply_device_manager_t *manager)
+{
+        ply_hashtable_remove (manager->input_devices, device);
+        ply_input_device_free (input_device);
+}
+
+static void
+free_input_devices (ply_device_manager_t *manager)
+{
+        ply_hashtable_foreach (manager->input_devices, (ply_hashtable_foreach_func_t *) free_input_device, manager);
+}
+
 static ply_terminal_t *
 get_terminal (ply_device_manager_t *manager,
               const char           *device_name)
@@ -657,7 +759,7 @@ get_terminal (ply_device_manager_t *manager,
         terminal = ply_hashtable_lookup (manager->terminals, full_name);
 
         if (terminal == NULL) {
-                terminal = ply_terminal_new (full_name);
+                terminal = ply_terminal_new (full_name, manager->keymap);
 
                 ply_hashtable_insert (manager->terminals,
                                       (void *) ply_terminal_get_name (terminal),
@@ -686,6 +788,74 @@ free_renderers (ply_device_manager_t *manager)
                                manager);
 }
 
+static char *
+strip_quotes (char *str)
+{
+        char *old_str;
+        if (str && str[0] == '"' && str[strlen (str) - 1] == '"') {
+                old_str = str;
+                str = strndup (str + 1, strlen (str) - 2);
+                free (old_str);
+        }
+        return str;
+}
+
+static void
+parse_vconsole_conf (ply_device_manager_t *manager)
+{
+        ply_key_file_t *vconsole_conf;
+        char *keymap = NULL, *xkb_layout = NULL, *xkb_model = NULL, *xkb_variant = NULL, *xkb_options = NULL;
+
+        keymap = ply_kernel_command_line_get_key_value ("rd.vconsole.keymap=");
+
+        if (!keymap)
+                keymap = ply_kernel_command_line_get_key_value ("vconsole.keymap=");
+
+        vconsole_conf = ply_key_file_new ("/etc/vconsole.conf");
+        if (ply_key_file_load_groupless_file (vconsole_conf)) {
+                /* The values in vconsole.conf might be quoted, strip these */
+                if (!keymap) {
+                        keymap = ply_key_file_get_value (vconsole_conf, NULL, "KEYMAP");
+                        keymap = strip_quotes (keymap);
+                }
+                xkb_layout = ply_key_file_get_value (vconsole_conf, NULL, "XKB_LAYOUT");
+                xkb_layout = strip_quotes (xkb_layout);
+
+                xkb_model = ply_key_file_get_value (vconsole_conf, NULL, "XKB_MODEL");
+                xkb_model = strip_quotes (xkb_model);
+
+                xkb_variant = ply_key_file_get_value (vconsole_conf, NULL, "XKB_VARIANT");
+                xkb_variant = strip_quotes (xkb_variant);
+
+                xkb_options = ply_key_file_get_value (vconsole_conf, NULL, "XKB_OPTIONS");
+                xkb_options = strip_quotes (xkb_options);
+        }
+        ply_key_file_free (vconsole_conf);
+
+        ply_trace ("XKB_KEYMAP: %s     KEYMAP: %s", xkb_layout, keymap);
+
+        struct xkb_rule_names xkb_keymap = {
+                .layout  = xkb_layout,
+                .model   = xkb_model,
+                .variant = xkb_variant,
+                .options = xkb_options,
+        };
+        manager->xkb_keymap = xkb_keymap_new_from_names (manager->xkb_context, &xkb_keymap, XKB_MAP_COMPILE_NO_FLAGS);
+
+        if (manager->xkb_keymap == NULL) {
+                ply_trace ("Failed to set xkb keymap of LAYOUT: %s MODEL: %s VARIANT: %s OPTIONS: %s", xkb_layout, xkb_model, xkb_variant, xkb_options);
+
+                manager->xkb_keymap = xkb_keymap_new_from_names (manager->xkb_context, NULL, XKB_MAP_COMPILE_NO_FLAGS);
+                assert (manager->xkb_keymap != NULL);
+        }
+
+        free (xkb_layout);
+        free (xkb_model);
+        free (xkb_variant);
+        free (xkb_options);
+        manager->keymap = keymap;
+}
+
 ply_device_manager_t *
 ply_device_manager_new (const char                *default_tty,
                         ply_device_manager_flags_t flags)
@@ -694,11 +864,16 @@ ply_device_manager_new (const char                *default_tty,
 
         manager = calloc (1, sizeof(ply_device_manager_t));
         manager->loop = NULL;
+        manager->xkb_context = xkb_context_new (XKB_CONTEXT_NO_FLAGS);
+        parse_vconsole_conf (manager);
+
         manager->terminals = ply_hashtable_new (ply_hashtable_string_hash, ply_hashtable_string_compare);
         manager->renderers = ply_hashtable_new (ply_hashtable_string_hash, ply_hashtable_string_compare);
-        manager->local_console_terminal = ply_terminal_new (default_tty);
+
+        manager->local_console_terminal = ply_terminal_new (default_tty, manager->keymap);
         ply_terminal_open (manager->local_console_terminal);
 
+        manager->input_devices = ply_hashtable_new (ply_hashtable_string_hash, ply_hashtable_string_compare);
         manager->keyboards = ply_list_new ();
         manager->text_displays = ply_list_new ();
         manager->pixel_displays = ply_list_new ();
@@ -731,10 +906,17 @@ ply_device_manager_free (ply_device_manager_t *manager)
 
         free_terminals (manager);
         ply_hashtable_free (manager->terminals);
+        free ((void *) manager->keymap);
 
         free_renderers (manager);
         ply_hashtable_free (manager->renderers);
 
+        free_input_devices (manager);
+        ply_hashtable_free (manager->input_devices);
+
+        if (manager->xkb_context)
+                xkb_context_unref (manager->xkb_context);
+
 #ifdef HAVE_UDEV
         ply_event_loop_stop_watching_for_timeout (manager->loop,
                                                   (ply_event_loop_timeout_handler_t)
@@ -922,6 +1104,8 @@ create_devices_for_terminal_and_renderer_type (ply_device_manager_t *manager,
                                 renderer = NULL;
                                 return true;
                         }
+
+                        add_input_devices_to_renderer (manager, renderer);
                 }
         }
 
@@ -1027,6 +1211,7 @@ create_devices_from_udev (ply_device_manager_t *manager)
 
         ply_trace ("Timeout elapsed, looking for devices from udev");
 
+        create_devices_for_subsystem (manager, SUBSYSTEM_INPUT);
         create_devices_for_subsystem (manager, SUBSYSTEM_DRM);
         create_devices_for_subsystem (manager, SUBSYSTEM_FRAME_BUFFER);
 
@@ -1089,6 +1274,7 @@ ply_device_manager_watch_devices (ply_device_manager_t               *manager,
 
 #ifdef HAVE_UDEV
         watch_for_udev_events (manager);
+        create_devices_for_subsystem (manager, SUBSYSTEM_INPUT);
         create_devices_for_subsystem (manager, SUBSYSTEM_DRM);
         ply_event_loop_watch_for_timeout (manager->loop,
                                           device_timeout,
diff --git a/src/libply-splash-core/ply-input-device.c b/src/libply-splash-core/ply-input-device.c
new file mode 100644 (file)
index 0000000..be473fc
--- /dev/null
@@ -0,0 +1,509 @@
+/* ply-input-device.c - evdev input device handling implementation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written By: Diego Augusto <diego.augusto@protonmail.com>
+ */
+#include <assert.h>
+#include <fcntl.h>
+#include <linux/input-event-codes.h>
+#include <malloc.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libevdev-1.0/libevdev/libevdev.h>
+#include <linux/input.h>
+#include <unistd.h>
+#include <xkbcommon/xkbcommon.h>
+#include <xkbcommon/xkbcommon-keysyms.h>
+
+#include "ply-buffer.h"
+#include "ply-event-loop.h"
+#include "ply-input-device.h"
+#include "ply-list.h"
+#include "ply-logger.h"
+#include "ply-trigger.h"
+#include "ply-utils.h"
+
+/* The docs say this needs to be at least 7, the code enforces this, but also never uses more
+ * than 5. We'll just do 7.
+ */
+#define PLY_XKB_KEYSYM_TO_UTF8_BUFFER_SIZE 7
+
+struct _ply_input_device
+{
+        int                       fd;
+        char                     *path;
+        ply_event_loop_t         *loop;
+        ply_trigger_t            *input_trigger;
+        ply_trigger_t            *leds_changed_trigger;
+        ply_trigger_t            *disconnect_trigger;
+        ply_fd_watch_t           *fd_watch;
+
+        struct xkb_context       *xkb_context;
+        struct xkb_keymap        *keymap;
+        struct xkb_state         *keyboard_state;
+        struct xkb_compose_table *compose_table;
+        struct xkb_compose_state *compose_state;
+
+        struct libevdev          *dev;
+};
+
+static bool
+apply_compose_sequence_to_input_buffer (ply_input_device_t *input_device,
+                                        xkb_keysym_t        input_symbol,
+                                        ply_buffer_t       *input_buffer)
+{
+        enum xkb_compose_feed_result compose_feed_result;
+        enum xkb_compose_status compose_status;
+
+        if (input_device->compose_state == NULL)
+                return false;
+
+        if (input_symbol == XKB_KEY_NoSymbol)
+                return false;
+
+        compose_feed_result = xkb_compose_state_feed (input_device->compose_state, input_symbol);
+
+        if (compose_feed_result == XKB_COMPOSE_FEED_IGNORED)
+                return false;
+
+        assert (compose_feed_result == XKB_COMPOSE_FEED_ACCEPTED);
+
+        compose_status = xkb_compose_state_get_status (input_device->compose_state);
+
+        if (compose_status == XKB_COMPOSE_NOTHING)
+                return false;
+
+        if (compose_status == XKB_COMPOSE_COMPOSED) {
+                xkb_keysym_t output_symbol;
+                ssize_t character_size;
+                char character_buf[PLY_XKB_KEYSYM_TO_UTF8_BUFFER_SIZE] = "";
+
+                output_symbol = xkb_compose_state_get_one_sym (input_device->compose_state);
+                character_size = xkb_keysym_to_utf8 (output_symbol, character_buf, sizeof(character_buf));
+
+                if (character_size > 0)
+                        ply_buffer_append_bytes (input_buffer, character_buf, character_size);
+        } else {
+                /* Either we're mid compose sequence (XKB_COMPOSE_COMPOSING) or the compose sequence has
+                 * been aborted (XKB_COMPOSE_CANCELLED). Either way, we shouldn't append anything to the
+                 * input buffer
+                 */
+        }
+
+        return true;
+}
+
+static void
+apply_key_to_input_buffer (ply_input_device_t *input_device,
+                           xkb_keysym_t        symbol,
+                           int                 keycode,
+                           ply_buffer_t       *input_buffer)
+{
+        ssize_t character_size;
+        bool was_compose_sequence;
+
+        was_compose_sequence = apply_compose_sequence_to_input_buffer (input_device, symbol, input_buffer);
+
+        if (was_compose_sequence)
+                return;
+
+        switch (symbol) {
+        case XKB_KEY_Escape:
+                ply_buffer_append_bytes (input_buffer, "\033", 1);
+                break;
+        case XKB_KEY_Return:
+                ply_buffer_append_bytes (input_buffer, "\n", 1);
+                break;
+        case XKB_KEY_BackSpace:
+                ply_buffer_append_bytes (input_buffer, "\177", 1);
+                break;
+        case XKB_KEY_NoSymbol:
+                break;
+        default:
+                character_size = xkb_state_key_get_utf8 (input_device->keyboard_state, keycode, NULL, 0);
+
+                if (character_size > 0) {
+                        char character_buf[character_size + 1];
+
+                        character_size = xkb_state_key_get_utf8 (input_device->keyboard_state, keycode, character_buf, sizeof(character_buf));
+
+                        assert (character_size + 1 == sizeof(character_buf));
+
+                        ply_buffer_append_bytes (input_buffer, character_buf, character_size);
+                }
+                break;
+        }
+}
+
+static void
+on_input (ply_input_device_t *input_device)
+{
+        struct input_event ev;
+        int rc;
+        ply_buffer_t *input_buffer = ply_buffer_new ();
+
+        for (;;) {
+                ply_key_direction_t key_state;
+                enum xkb_key_direction xkb_key_direction;
+                xkb_keycode_t keycode;
+                xkb_keysym_t symbol;
+                enum xkb_state_component updated_state;
+
+                rc = libevdev_next_event (input_device->dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+                if (rc != LIBEVDEV_READ_STATUS_SUCCESS)
+                        break;
+
+                if (!libevdev_event_is_type (&ev, EV_KEY))
+                        continue;
+
+                /* According to `https://docs.kernel.org/input/event-codes.html#ev-key`:
+                 * if ev.value = 2, then the key is being held down. libxkbcommon doesn't appear to define this
+                 * if ev.value = 1, then key was pressed down
+                 * if ev.value = 0, then key was released up
+                 */
+                switch (ev.value) {
+                case 0:
+                        key_state = PLY_KEY_UP;
+                        xkb_key_direction = XKB_KEY_UP;
+                        break;
+
+                case 1:
+                        key_state = PLY_KEY_DOWN;
+                        xkb_key_direction = XKB_KEY_DOWN;
+                        break;
+
+                case 2:
+                        key_state = PLY_KEY_HELD;
+                        xkb_key_direction = XKB_KEY_UP;
+                        break;
+                }
+
+                /* According to
+                 * `https://xkbcommon.org/doc/current/xkbcommon_8h.html#ac29aee92124c08d1953910ab28ee1997`
+                 *  A xkb keycode = linux evdev code + 8
+                 */
+                keycode = (xkb_keycode_t) (ev.code + 8);
+
+                symbol = xkb_state_key_get_one_sym (input_device->keyboard_state, keycode);
+
+                updated_state = xkb_state_update_key (input_device->keyboard_state, keycode, xkb_key_direction);
+
+                if ((updated_state & XKB_STATE_LEDS) != 0)
+                        ply_trigger_pull (input_device->leds_changed_trigger, input_device);
+
+                /* If the key is repeating, or is being pressed down */
+                if (key_state == PLY_KEY_HELD || key_state == PLY_KEY_DOWN)
+                        apply_key_to_input_buffer (input_device, symbol, keycode, input_buffer);
+        }
+        if (rc != -EAGAIN) {
+                ply_error ("There was an error reading events for device '%s': %s",
+                           input_device->path, strerror (-rc));
+                goto error;
+        }
+        if (ply_buffer_get_size (input_buffer) != 0) {
+                ply_trigger_pull (input_device->input_trigger, ply_buffer_get_bytes (input_buffer));
+        }
+
+error:
+        ply_buffer_free (input_buffer);
+}
+
+static void
+on_disconnect (ply_input_device_t *input_device)
+{
+        ply_trace ("Input disconnected: %s (%s)", libevdev_get_name (input_device->dev),
+                   input_device->path);
+        ply_trigger_pull (input_device->disconnect_trigger, input_device);
+
+        ply_input_device_free (input_device);
+}
+
+void
+ply_input_device_set_disconnect_handler (ply_input_device_t                   *input_device,
+                                         ply_input_device_disconnect_handler_t callback,
+                                         void                                 *user_data)
+{
+        ply_trigger_add_handler (input_device->disconnect_trigger, (ply_trigger_handler_t) callback, user_data);
+}
+
+ply_input_device_t *
+ply_input_device_open (struct xkb_context *xkb_context,
+                       struct xkb_keymap  *xkb_keymap,
+                       const char         *path)
+{
+        int error;
+        const char *locale;
+
+        /* Look up the preferred locale, falling back to "C" as default */
+        if (!(locale = getenv ("LC_ALL")))
+                if (!(locale = getenv ("LC_CTYPE")))
+                        if (!(locale = getenv ("LANG")))
+                                locale = "C";
+
+        ply_input_device_t *input_device = calloc (1, sizeof(ply_input_device_t));
+
+        if (input_device == NULL) {
+                ply_error ("Out of memory");
+                return NULL;
+        }
+
+        input_device->disconnect_trigger = ply_trigger_new (NULL);
+        input_device->path = strdup (path);
+        input_device->input_trigger = ply_trigger_new (NULL);
+        ply_trigger_set_instance (input_device->input_trigger, input_device);
+
+        input_device->leds_changed_trigger = ply_trigger_new (NULL);
+        input_device->loop = ply_event_loop_get_default ();
+
+        input_device->fd = open (path, O_RDWR | O_NONBLOCK);
+
+        if (input_device->fd < 0) {
+                ply_error ("Failed to open input device \"%s\"", path);
+                goto error;
+        }
+        input_device->dev = libevdev_new ();
+        error = libevdev_set_fd (input_device->dev, input_device->fd);
+        if (error != 0) {
+                ply_error ("Failed to set fd for device \"%s\": %s", path, strerror (-error));
+                goto error;
+        }
+
+        input_device->fd_watch = ply_event_loop_watch_fd (
+                input_device->loop, input_device->fd, PLY_EVENT_LOOP_FD_STATUS_HAS_DATA,
+                (ply_event_handler_t) on_input, (ply_event_handler_t) on_disconnect,
+                input_device);
+
+        input_device->keymap = xkb_keymap_ref (xkb_keymap);
+        input_device->keyboard_state = xkb_state_new (input_device->keymap);
+        if (input_device->keyboard_state == NULL) {
+                ply_error ("Failed to initialize input device \"%s\" keyboard_state", path);
+                goto error;
+        }
+        input_device->compose_table = xkb_compose_table_new_from_locale (xkb_context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
+        if (input_device->compose_table)
+                input_device->compose_state = xkb_compose_state_new (input_device->compose_table, XKB_COMPOSE_STATE_NO_FLAGS);
+
+        return input_device;
+
+error:
+        ply_input_device_free (input_device);
+        return NULL;
+}
+
+void
+ply_input_device_watch_for_input (ply_input_device_t                     *input_device,
+                                  ply_input_device_input_handler_t        input_callback,
+                                  ply_input_device_leds_changed_handler_t leds_changed_callback,
+                                  void                                   *user_data)
+{
+        ply_trigger_add_instance_handler (input_device->input_trigger, (ply_trigger_instance_handler_t) input_callback, user_data);
+        ply_trigger_add_handler (input_device->leds_changed_trigger, (ply_trigger_handler_t) leds_changed_callback, user_data);
+}
+
+void
+ply_input_device_stop_watching_for_input (ply_input_device_t                     *input_device,
+                                          ply_input_device_input_handler_t        input_callback,
+                                          ply_input_device_leds_changed_handler_t leds_changed_callback,
+                                          void                                   *user_data)
+{
+        ply_trigger_remove_instance_handler (input_device->input_trigger, (ply_trigger_instance_handler_t) input_callback, user_data);
+        ply_trigger_remove_handler (input_device->leds_changed_trigger, (ply_trigger_handler_t) leds_changed_callback, user_data);
+}
+
+int
+ply_input_device_is_keyboard (ply_input_device_t *input_device)
+{
+        return libevdev_has_event_type (input_device->dev, EV_KEY);
+}
+
+int
+ply_input_device_is_keyboard_with_leds (ply_input_device_t *input_device)
+{
+        return (libevdev_has_event_type (input_device->dev, EV_KEY)) &&
+               (libevdev_has_event_type (input_device->dev, EV_LED));
+}
+
+const char *
+ply_input_device_get_name (ply_input_device_t *input_device)
+{
+        return libevdev_get_name (input_device->dev);
+}
+
+const char *
+ply_input_device_get_path (ply_input_device_t *input_device)
+{
+        return input_device->path;
+}
+
+/*
+ * from libinput's evdev_device_led_update and Weston's weston_keyboard_set_locks
+ */
+void
+ply_input_device_set_state (ply_input_device_t       *input_device,
+                            ply_xkb_keyboard_state_t *xkb_state)
+{
+        static struct
+        {
+                ply_led_t      ply_led;
+                int            evdev;
+                xkb_mod_mask_t status;
+        } map[] = {
+                { PLY_LED_NUM_LOCK,    LED_NUML,    false },
+                { PLY_LED_CAPS_LOCK,   LED_CAPSL,   false },
+                { PLY_LED_SCROLL_LOCK, LED_SCROLLL, false },
+        };
+        struct input_event ev[PLY_NUMBER_OF_ELEMENTS (map) + 1];
+        xkb_mod_mask_t mods_depressed, mods_latched, mods_locked, group;
+        unsigned int i;
+
+        mods_depressed = xkb_state_serialize_mods (input_device->keyboard_state,
+                                                   XKB_STATE_DEPRESSED);
+        mods_latched = xkb_state_serialize_mods (input_device->keyboard_state,
+                                                 XKB_STATE_LATCHED);
+        mods_locked = xkb_state_serialize_mods (input_device->keyboard_state,
+                                                XKB_STATE_LOCKED);
+        group = xkb_state_serialize_group (input_device->keyboard_state,
+                                           XKB_STATE_EFFECTIVE);
+
+        if (mods_depressed == xkb_state->mods_depressed &&
+            mods_latched == xkb_state->mods_latched &&
+            mods_locked == xkb_state->mods_locked &&
+            group == xkb_state->group)
+                return;
+
+        mods_depressed = xkb_state->mods_depressed;
+        mods_latched = xkb_state->mods_latched;
+        mods_locked = xkb_state->mods_locked;
+        group = xkb_state->group;
+
+        xkb_state_update_mask (input_device->keyboard_state,
+                               mods_depressed,
+                               mods_latched,
+                               mods_locked,
+                               0,
+                               0,
+                               group);
+
+        map[LED_NUML].status = xkb_state_led_name_is_active (input_device->keyboard_state, XKB_LED_NAME_NUM);
+        map[LED_CAPSL].status = xkb_state_led_name_is_active (input_device->keyboard_state, XKB_LED_NAME_CAPS);
+        map[LED_SCROLLL].status = xkb_state_led_name_is_active (input_device->keyboard_state, XKB_LED_NAME_SCROLL);
+
+        memset (ev, 0, sizeof(ev));
+        for (i = 0; i < PLY_NUMBER_OF_ELEMENTS (map); i++) {
+                ev[i].type = EV_LED;
+                ev[i].code = map[i].evdev;
+                ev[i].value = map[i].status;
+        }
+        ev[i].type = EV_SYN;
+        ev[i].code = SYN_REPORT;
+
+        ply_write (input_device->fd, ev, sizeof(ev));
+}
+
+ply_xkb_keyboard_state_t
+*ply_input_device_get_state (ply_input_device_t *input_device)
+{
+        ply_xkb_keyboard_state_t *xkb_state = calloc (1, sizeof(ply_xkb_keyboard_state_t));
+
+        xkb_state->mods_depressed = xkb_state_serialize_mods (input_device->keyboard_state,
+                                                              XKB_STATE_DEPRESSED);
+        xkb_state->mods_latched = xkb_state_serialize_mods (input_device->keyboard_state,
+                                                            XKB_STATE_LATCHED);
+        xkb_state->mods_locked = xkb_state_serialize_mods (input_device->keyboard_state,
+                                                           XKB_STATE_LOCKED);
+        xkb_state->group = xkb_state_serialize_group (input_device->keyboard_state,
+                                                      XKB_STATE_EFFECTIVE);
+
+        return xkb_state;
+}
+
+bool
+ply_input_device_get_capslock_state (ply_input_device_t *input_device)
+{
+        return xkb_state_led_name_is_active (input_device->keyboard_state, XKB_LED_NAME_CAPS);
+}
+
+const char *
+ply_input_device_get_keymap (ply_input_device_t *input_device)
+{
+        xkb_layout_index_t num_indices = xkb_keymap_num_layouts (input_device->keymap);
+        ply_trace ("xkb layout has %d groups", num_indices);
+        if (num_indices == 0) {
+                return NULL;
+        }
+        /* According to xkbcommon docs:
+         * (https://xkbcommon.org/doc/current/xkbcommon_8h.html#ad37512642806c55955e1cd5a30efcc39)
+         *
+         * Each layout is not required to have a name, and the names are not
+         * guaranteed to be unique (though they are usually provided and
+         * unique). Therefore, it is not safe to use the name as a unique
+         * identifier for a layout. Layout names are case-sensitive.
+         *
+         * Layout names are specified in the layout's definition, for example "English
+         * (US)". These are different from the (conventionally) short names
+         * which are used to locate the layout, for example "us" or "us(intl)".
+         * These names are not present in a compiled keymap.
+         *
+         * This string shouldn't be used as a unique indentifier for a keymap
+         */
+        return xkb_keymap_layout_get_name (input_device->keymap, num_indices - 1);
+}
+
+int
+ply_input_device_get_fd (ply_input_device_t *input_device)
+{
+        return input_device->fd;
+}
+
+void
+ply_input_device_free (ply_input_device_t *input_device)
+{
+        if (input_device == NULL)
+                return;
+
+        if (input_device->xkb_context)
+                xkb_context_unref (input_device->xkb_context);
+
+        if (input_device->keyboard_state)
+                xkb_state_unref (input_device->keyboard_state);
+
+        if (input_device->keymap)
+                xkb_keymap_unref (input_device->keymap);
+
+        if (input_device->compose_state)
+                xkb_compose_state_unref (input_device->compose_state);
+
+        if (input_device->compose_table)
+                xkb_compose_table_unref (input_device->compose_table);
+
+        if (input_device->dev)
+                libevdev_free (input_device->dev);
+
+        ply_trigger_free (input_device->input_trigger);
+        ply_trigger_free (input_device->leds_changed_trigger);
+        ply_trigger_free (input_device->disconnect_trigger);
+
+        free (input_device->path);
+
+        ply_event_loop_stop_watching_fd (input_device->loop, input_device->fd_watch);
+
+        close (input_device->fd);
+
+        free (input_device);
+}
diff --git a/src/libply-splash-core/ply-input-device.h b/src/libply-splash-core/ply-input-device.h
new file mode 100644 (file)
index 0000000..ecc8898
--- /dev/null
@@ -0,0 +1,92 @@
+/* ply-input-device.h - evdev input device handling
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written By: Diego Augusto <diego.augusto@protonmail.com>
+ */
+#ifndef PLY_INPUT_DEVICE_H
+#define PLY_INPUT_DEVICE_H
+
+#include "ply-buffer.h"
+#include <xkbcommon/xkbcommon.h>
+#include <xkbcommon/xkbcommon-compose.h>
+
+typedef enum
+{
+        PLY_LED_NUM_LOCK    = (1 << 0),
+        PLY_LED_CAPS_LOCK   = (1 << 1),
+        PLY_LED_SCROLL_LOCK = (1 << 2)
+} ply_led_t;
+
+typedef enum
+{
+        PLY_KEY_UP,
+        PLY_KEY_DOWN,
+        PLY_KEY_HELD,
+} ply_key_direction_t;
+
+typedef struct _ply_input_device ply_input_device_t;
+typedef void (*ply_input_device_input_handler_t) (void               *user_data,
+                                                  ply_input_device_t *input_device,
+                                                  const char         *buf);
+typedef void (*ply_input_device_leds_changed_handler_t) (void               *user_data,
+                                                         ply_input_device_t *input_device);
+typedef void (*ply_input_device_disconnect_handler_t) (void               *user_data,
+                                                       ply_input_device_t *input_device);
+typedef struct
+{
+        xkb_mod_mask_t mods_depressed;
+        xkb_mod_mask_t mods_latched;
+        xkb_mod_mask_t mods_locked;
+        xkb_mod_mask_t group;
+} ply_xkb_keyboard_state_t;
+
+#ifndef PLY_HIDE_FUNCTION_DECLARATIONS
+
+ply_input_device_t *ply_input_device_open (struct xkb_context *xkb_context,
+                                           struct xkb_keymap  *xkb_keymap,
+                                           const char         *path);
+void ply_input_device_free (ply_input_device_t *input_device);
+void ply_input_device_watch_for_input (ply_input_device_t                     *input_device,
+                                       ply_input_device_input_handler_t        input_callback,
+                                       ply_input_device_leds_changed_handler_t led_callback,
+                                       void                                   *user_data);
+
+void ply_input_device_stop_watching_for_input (ply_input_device_t                     *input_device,
+                                               ply_input_device_input_handler_t        input_callback,
+                                               ply_input_device_leds_changed_handler_t led_callback,
+                                               void                                   *user_data);
+
+
+ply_xkb_keyboard_state_t *ply_input_device_get_state (ply_input_device_t *input_device);
+void ply_input_device_set_state (ply_input_device_t       *input_device,
+                                 ply_xkb_keyboard_state_t *xkb_state);
+
+void ply_input_device_set_disconnect_handler (ply_input_device_t                   *input_device,
+                                              ply_input_device_disconnect_handler_t callback,
+                                              void                                 *user_data);
+int ply_input_device_get_fd (ply_input_device_t *input_device);
+int  ply_input_device_is_keyboard (ply_input_device_t *input_device);
+int  ply_input_device_is_keyboard_with_leds (ply_input_device_t *input_device);
+const char *ply_input_device_get_name (ply_input_device_t *input_device);
+bool ply_input_device_get_capslock_state (ply_input_device_t *input_device);
+/* The value shouldn't be used as a unique indentifier for a keymap */
+const char *ply_input_device_get_keymap (ply_input_device_t *input_device);
+const char *ply_input_device_get_path (ply_input_device_t *input_device);
+
+#endif //PLY_HIDE_FUNCTION_DECLARATIONS
+
+#endif //PLY_INPUT_DEVICE_H
index 6817dac1343461914e0767f7077ee98a0f0d84b6..483ad27c3ff94bbd8356a3411bce68502968a892 100644 (file)
@@ -26,6 +26,7 @@
 #include <stdint.h>
 #include <unistd.h>
 
+#include "ply-input-device.h"
 #include "ply-terminal.h"
 #include "ply-event-loop.h"
 #include "ply-list.h"
@@ -76,6 +77,11 @@ typedef struct
                                      int                         *scale);
         bool (*get_capslock_state)(ply_renderer_backend_t *backend);
         const char * (*get_keymap)(ply_renderer_backend_t *backend);
+
+        void (*add_input_device)(ply_renderer_backend_t *backend,
+                                 ply_input_device_t     *input_device);
+        void (*remove_input_device)(ply_renderer_backend_t *backend,
+                                    ply_input_device_t     *input_device);
 } ply_renderer_plugin_interface_t;
 
 #endif /* PLY_RENDERER_PLUGIN_H */
index edebc0304aaa7146b8d85ee3a269a99eb4b2bdc3..75492885bbbe42f36da1bd38c79c5729cd582d89 100644 (file)
@@ -368,6 +368,34 @@ ply_renderer_flush_head (ply_renderer_t      *renderer,
         renderer->plugin_interface->flush_head (renderer->backend, head);
 }
 
+void
+ply_renderer_add_input_device (ply_renderer_t     *renderer,
+                               ply_input_device_t *input_device)
+{
+        assert (renderer != NULL);
+        assert (renderer->plugin_interface != NULL);
+        assert (input_device != NULL);
+
+        if (!renderer->plugin_interface->add_input_device)
+                return;
+
+        renderer->plugin_interface->add_input_device (renderer->backend, input_device);
+}
+
+void
+ply_renderer_remove_input_device (ply_renderer_t     *renderer,
+                                  ply_input_device_t *input_device)
+{
+        assert (renderer != NULL);
+        assert (renderer->plugin_interface != NULL);
+        assert (input_device != NULL);
+
+        if (!renderer->plugin_interface->remove_input_device)
+                return;
+
+        renderer->plugin_interface->remove_input_device (renderer->backend, input_device);
+}
+
 ply_renderer_input_source_t *
 ply_renderer_get_input_source (ply_renderer_t *renderer)
 {
@@ -452,4 +480,3 @@ ply_renderer_get_keymap (ply_renderer_t *renderer)
 
         return renderer->plugin_interface->get_keymap (renderer->backend);
 }
-
index 0b4e03230593d1413ca479dbe2ef99f4cb39c0cc..5fbf819d99b1589798396ee8b02452158c57f73c 100644 (file)
@@ -30,6 +30,7 @@
 #include "ply-pixel-buffer.h"
 #include "ply-terminal.h"
 #include "ply-utils.h"
+#include "ply-input-device.h"
 
 typedef struct _ply_renderer ply_renderer_t;
 typedef struct _ply_renderer_head ply_renderer_head_t;
@@ -68,6 +69,12 @@ ply_pixel_buffer_t *ply_renderer_get_buffer_for_head (ply_renderer_t      *rende
 void ply_renderer_flush_head (ply_renderer_t      *renderer,
                               ply_renderer_head_t *head);
 
+void ply_renderer_add_input_device (ply_renderer_t     *renderer,
+                                    ply_input_device_t *input_device);
+
+void ply_renderer_remove_input_device (ply_renderer_t     *renderer,
+                                       ply_input_device_t *input_device);
+
 ply_renderer_input_source_t *ply_renderer_get_input_source (ply_renderer_t *renderer);
 bool ply_renderer_open_input_source (ply_renderer_t              *renderer,
                                      ply_renderer_input_source_t *input_source);
index 51771244e63c9e41c8ed38f0bd7204a7bc55e801..eee23c741f0fc36ffef2ddc47b197f93ec956d6b 100644 (file)
@@ -80,7 +80,7 @@ struct _ply_terminal
         struct termios       original_locked_term_attributes;
 
         char                *name;
-        char                *keymap;
+        const char          *keymap;
         int                  fd;
         int                  vt_number;
         int                  initial_vt_number;
@@ -117,37 +117,9 @@ typedef enum
 
 static ply_terminal_open_result_t ply_terminal_open_device (ply_terminal_t *terminal);
 
-static char *
-ply_terminal_parse_keymap_conf (ply_terminal_t *terminal)
-{
-        ply_key_file_t *vconsole_conf;
-        char *keymap, *old_keymap;
-
-        keymap = ply_kernel_command_line_get_key_value ("rd.vconsole.keymap=");
-        if (keymap)
-                return keymap;
-
-        keymap = ply_kernel_command_line_get_key_value ("vconsole.keymap=");
-        if (keymap)
-                return keymap;
-
-        vconsole_conf = ply_key_file_new ("/etc/vconsole.conf");
-        if (ply_key_file_load_groupless_file (vconsole_conf))
-                keymap = ply_key_file_get_value (vconsole_conf, NULL, "KEYMAP");
-        ply_key_file_free (vconsole_conf);
-
-        /* The keymap name in vconsole.conf might be quoted, strip these */
-        if (keymap && keymap[0] == '"' && keymap[strlen (keymap) - 1] == '"') {
-                old_keymap = keymap;
-                keymap = strndup (keymap + 1, strlen (keymap) - 2);
-                free (old_keymap);
-        }
-
-        return keymap;
-}
-
 ply_terminal_t *
-ply_terminal_new (const char *device_name)
+ply_terminal_new (const char *device_name,
+                  const char *keymap)
 {
         ply_terminal_t *terminal;
 
@@ -167,7 +139,7 @@ ply_terminal_new (const char *device_name)
         terminal->fd = -1;
         terminal->vt_number = -1;
         terminal->initial_vt_number = -1;
-        terminal->keymap = ply_terminal_parse_keymap_conf (terminal);
+        terminal->keymap = keymap;
         if (terminal->keymap)
                 ply_trace ("terminal %s keymap: %s", terminal->name, terminal->keymap);
 
@@ -879,7 +851,6 @@ ply_terminal_free (ply_terminal_t *terminal)
 
         free_vt_change_closures (terminal);
         free_input_closures (terminal);
-        free (terminal->keymap);
         free (terminal->name);
         free (terminal);
 }
index a2685f5c63061ad56ab1e8552a082bb94bef47c2..14d75d1315ced4eff4e189f4b313e8c4cbe6ab09 100644 (file)
@@ -55,7 +55,8 @@ typedef enum
 } ply_terminal_mode_t;
 
 #ifndef PLY_HIDE_FUNCTION_DECLARATIONS
-ply_terminal_t *ply_terminal_new (const char *device_name);
+ply_terminal_t *ply_terminal_new (const char *device_name,
+                                  const char *keymap);
 
 void ply_terminal_free (ply_terminal_t *terminal);
 
@@ -121,4 +122,3 @@ void ply_terminal_flush_input (ply_terminal_t *terminal);
 #endif
 
 #endif /* PLY_TERMINAL_H */
-
index 94cf5b34f628d2010e53f002a3e50856225a35ba..4455f032a4a31537ba5cbd6a163406d4287921eb 100644 (file)
@@ -82,7 +82,7 @@ ply_keymap_icon_fill_keymap_info (ply_keymap_icon_t *keymap_icon)
 {
         const char *keymap_with_variant;
         ply_renderer_t *renderer;
-        char *keymap;
+        char *keymap, *compare_keymap;
         int i;
 
         keymap_icon->keymap_offset = -1;
@@ -95,7 +95,13 @@ ply_keymap_icon_fill_keymap_info (ply_keymap_icon_t *keymap_icon)
         keymap = ply_keymap_normalize_keymap (keymap_with_variant);
 
         for (i = 0; ply_keymap_metadata[i].name; i++) {
-                if (strcmp (ply_keymap_metadata[i].name, keymap) == 0) {
+                if (ply_keymap_metadata[i].type == PLY_LAYOUT_TERMINAL) {
+                        compare_keymap = strdup (keymap);
+                } else if (ply_keymap_metadata[i].type == PLY_LAYOUT_XKB) {
+                        compare_keymap = strdup (keymap_with_variant);
+                }
+
+                if (strcmp (ply_keymap_metadata[i].name, compare_keymap) == 0) {
                         keymap_icon->keymap_offset = ply_keymap_metadata[i].offset;
                         keymap_icon->keymap_width = ply_keymap_metadata[i].width;
                         break;
@@ -106,6 +112,7 @@ ply_keymap_icon_fill_keymap_info (ply_keymap_icon_t *keymap_icon)
                 ply_trace ("Error no pre-rendered text for '%s' keymap", keymap);
 
         free (keymap);
+        free (compare_keymap);
 }
 
 ply_keymap_icon_t *
@@ -273,4 +280,3 @@ ply_keymap_icon_get_height (ply_keymap_icon_t *keymap_icon)
 {
         return keymap_icon->height;
 }
-