]> git.ipfire.org Git - thirdparty/plymouth.git/commitdiff
Add input device support
authorDiego Augusto <diegocastro169169@gmail.com>
Sun, 27 Nov 2022 01:38:00 +0000 (01:38 +0000)
committerRay Strode <halfline@gmail.com>
Sun, 27 Nov 2022 01:38:00 +0000 (01:38 +0000)
23 files changed:
.gitlab-ci.yml
meson.build
scripts/keymap-render.py
scripts/plymouth-populate-initrd.in
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
src/libply-splash-graphics/ply-keymap-metadata.h
src/libply/ply-buffer.c
src/libply/ply-list.h
src/libply/ply-trigger.c
src/libply/ply-trigger.h
src/plugins/renderers/drm/plugin.c
src/plugins/renderers/frame-buffer/plugin.c
themes/spinfinity/keymap-render.png
themes/spinner/keymap-render.png

index 07a5907625612c9857818e467d4aee1df946574d..30fbb3b48d36d6806d3032f0255af0b25d43b186 100644 (file)
@@ -17,7 +17,7 @@ fedora-x86_64:
   stage: build
   image: 'registry.fedoraproject.org/fedora:37'
   before_script:
-    - dnf install -y gcc gtk3-devel git libpng-devel gettext-devel libxslt docbook-style-xsl cairo-devel systemd-devel systemd-udev kernel-headers libdrm-devel pango-devel make ShellCheck uncrustify patchutils meson binutils-gold
+    - dnf install -y gcc gtk3-devel git libpng-devel gettext-devel libxslt docbook-style-xsl cairo-devel systemd-devel systemd-udev kernel-headers libdrm-devel pango-devel make ShellCheck uncrustify patchutils meson binutils-gold xkeyboard-config-devel libevdev-devel
     - alternatives --set ld /usr/bin/ld.gold
   <<: *check-format
   <<: *meson-build
@@ -29,7 +29,7 @@ debian-unstable-x86_64:
   image: debian:unstable
   before_script:
     - apt-get update -qq
-    - apt-get install -y -qq --no-install-recommends bc build-essential docbook-xsl gcc gettext git libdrm-dev libgtk-3-dev libpango1.0-dev libpng-dev libudev-dev make pkg-config libsystemd-dev udev xsltproc shellcheck uncrustify patchutils meson
+    - apt-get install -y -qq --no-install-recommends bc build-essential docbook-xsl gcc gettext git libdrm-dev libgtk-3-dev libpango1.0-dev libpng-dev libudev-dev make pkg-config libsystemd-dev udev xsltproc shellcheck uncrustify patchutils meson xkb-data libevdev-dev
   <<: *meson-build
   only:
     - merge_requests
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 53b67dd30b86137bf18885f142f5a5d65a74ef72..2dc86575202af18f69a9b8313e1c4a46676699b9 100755 (executable)
@@ -3,12 +3,18 @@
 import cairo
 import subprocess
 import math
+import lxml
+from lxml import etree
 
 
 FONT_SIZE = 30
 MARGIN = int(FONT_SIZE / 3)
 
+#python3-lxml utility is required
+
 def get_keymaps():
+        xml = etree.XML(bytes(open('/usr/share/X11/xkb/rules/evdev.xml', 'r').read(), encoding="utf-8"))
+        keymaps_x11 = xml.xpath('//layout/configItem/description/text()')
         keymaps = subprocess.check_output(["localectl", "list-keymaps"]).decode("utf-8").strip().split()
 
         # Note when changing this you MUST keep ply_keymap_normalize_keymap()
@@ -31,12 +37,17 @@ def get_keymaps():
                 return parts[0]
 
         keymaps = list(map(normalize_keymaps ,keymaps))
-
         #Remove duplicates
-        ret = []
+        keymaps_dedupe = []
         for k in keymaps:
-                if k not in ret:
-                        ret.append(k)
+                if k not in keymaps_dedupe:
+                        keymaps_dedupe.append(k)
+
+        ret = []
+        for k in keymaps_dedupe:
+                ret.append((k, "PLY_LAYOUT_TERMINAL"))
+        for k in keymaps_x11:
+                ret.append((k, "PLY_LAYOUT_XKB"))
         return ret
 
 
@@ -49,7 +60,8 @@ ct.set_font_size(FONT_SIZE)
 max_height = 0.0
 total_width = 0.0
 for i in get_keymaps():
-        extents = ct.text_extents(i)
+        text=i[0]
+        extents = ct.text_extents(text)
         h = extents.height
         if h > max_height:
                 max_height = h
@@ -73,9 +85,11 @@ ct.move_to(MARGIN, MARGIN + max_height - descent)
 current_x, current_y = (MARGIN, MARGIN + max_height - descent)
 metadata = []
 for km in get_keymaps():
-        extents = ct.text_extents(km)
-        ct.show_text(km)
-        metadata.append((km, current_x, extents.width + MARGIN * 2))
+        km_text=km[0]
+        km_type=km[1]
+        extents = ct.text_extents(km_text)
+        ct.show_text(km_text)
+        metadata.append((km_text, current_x, extents.width + MARGIN * 2, km_type))
         current_x += extents.width + (MARGIN * 2)
         ct.move_to(current_x, current_y)
 
@@ -84,16 +98,22 @@ sf.write_to_png("keymap-render.png")
 print("/* This file is autogenerated by running:")
 print(" * scripts/keymap-render.py > src/libply-splash-graphics/ply-keymap-metadata.h")
 print(" */")
-print("struct ply_keymap_metadata {")
+print("typedef struct {")
 print("        const char *name;")
 print("        int offset;")
 print("        int width;")
-print("};")
+print("        int type;")
+print("} ply_keymap_metadata_t;")
+print("")
+print("typedef enum {")
+print("        PLY_LAYOUT_TERMINAL,")
+print("        PLY_LAYOUT_XKB,")
+print("} ply_layout_types_t;")
 print("")
-print("static struct ply_keymap_metadata ply_keymap_metadata[] = {")
+print("static ply_keymap_metadata_t ply_keymap_metadata[] = {")
 
 for i in metadata:
-        print(("        { \"%s\", %d, %d }," % (i[0],i[1],i[2])))
+        print(("        { \"%s\", %d, %d, %s}," % (i[0],i[1],i[2],i[3])))
 
 print("        { NULL, } /* End of array marker */ ")
 print("};")
index a130616f0f802177ac156b43e7ccf096f636ade7..6ee6b3533fa821d19b1081199d022dc30758c3c3 100755 (executable)
@@ -33,6 +33,10 @@ ddebug() {
     [ "$verbose" = "true" ] && echo "$@"
 }
 
+dfatal() {
+    echo "$@" > /proc/self/fd/2
+}
+
 # normalize_path <path>
 # Prints the normalized path, where it removes any duplicated
 # and trailing slashes.
@@ -420,12 +424,13 @@ function usage() {
         rc=1
     fi
 
-    echo "usage: plymouth [ --verbose | -v ] { --targetdir | -t } <initrd_directory>" > $output
+    echo "usage: plymouth [ --verbose | -v ] { --targetdir | -t } <initrd_directory> { --x11-directory | -x } <x11_directory>" > $output
     exit $rc
 }
 
 verbose=false
 INITRDDIR=""
+X11_DIRECTORY="/usr/share/X11"
 while [ $# -gt 0 ]; do
     case $1 in
         --verbose|-v)
@@ -438,6 +443,10 @@ while [ $# -gt 0 ]; do
         --help|-h)
             usage normal
             ;;
+        --x11-directory|-x)
+            shift
+            X11_DIRECTORY="$1"
+            ;;
         *)
             usage error
             break
@@ -465,6 +474,51 @@ inst @RELEASE_FILE@ $INITRDDIR
 inst ${PLYMOUTH_POLICYDIR}/plymouthd.defaults $INITRDDIR
 inst ${PLYMOUTH_CONFDIR}/plymouthd.conf $INITRDDIR
 
+# Install xkb info
+mkdir -p "${INITRDDIR}/${X11_DIRECTORY}/xkb/"
+mkdir -p "${INITRDDIR}/${X11_DIRECTORY}/xkb/compat/"
+
+mkdir -p "${INITRDDIR}/${X11_DIRECTORY}/xkb/keycodes/"
+mkdir -p "${INITRDDIR}/${X11_DIRECTORY}/xkb/rules/"
+mkdir -p "${INITRDDIR}/${X11_DIRECTORY}/xkb/symbols/"
+mkdir -p "${INITRDDIR}/${X11_DIRECTORY}/xkb/types/"
+mkdir -p "${INITRDDIR}/${X11_DIRECTORY}/locale/"
+inst ${X11_DIRECTORY}/xkb/compat/accessx $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/compat/basic $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/compat/caps $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/compat/complete $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/compat/iso9995 $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/compat/ledcaps $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/compat/lednum $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/compat/ledscroll $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/compat/level5 $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/compat/misc $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/compat/mousekeys $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/compat/xfree86 $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/keycodes/aliases $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/keycodes/evdev $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/rules/evdev $INITRDDIR
+find ${X11_DIRECTORY}/xkb/symbols -maxdepth 1 ! -type d | while read file; do
+    inst $file $INITRDDIR
+done
+inst ${X11_DIRECTORY}/xkb/types/basic $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/types/complete $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/types/extra $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/types/iso9995 $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/types/level5 $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/types/mousekeys $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/types/numpad $INITRDDIR
+inst ${X11_DIRECTORY}/xkb/types/pc $INITRDDIR
+
+# In the off chance the user uses their compose key when
+# typing their password, install compose sequences
+inst ${X11_DIRECTORY}/locale/compose.dir $INITRDDIR
+grep UTF-8/Compose: ${X11_DIRECTORY}/locale/compose.dir | awk -F: '{ print $1 }' | sort -u | xargs dirname | while read DIR; do
+    find ${X11_DIRECTORY}/locale/$DIR -maxdepth 1 ! -type d | while read file; do
+        inst $file $INITRDDIR
+     done
+done
+
 if [ -z "$PLYMOUTH_THEME_NAME" ]; then
     echo "No default plymouth plugin is set" >&2
     exit 1
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 f87466ffa5c389d039f23741dd47c99ba4fabdfd..12dce53071b0dfde854b9dd26b6f4988696845c0 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) {
@@ -375,7 +469,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);
 
@@ -393,18 +487,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");
@@ -456,7 +542,7 @@ verify_add_or_change (ply_device_manager_t *manager,
 {
         const char *subsystem;
 
-        if (strcmp (action, "add") && strcmp (action, "change"))
+        if (strcmp (action, "add") != 0 && strcmp (action, "change") != 0)
                 return false;
 
         if (manager->local_console_managed && manager->local_console_is_text) {
@@ -468,7 +554,7 @@ verify_add_or_change (ply_device_manager_t *manager,
                 return true;
 
         subsystem = udev_device_get_subsystem (device);
-        if (strcmp (subsystem, SUBSYSTEM_DRM)) {
+        if (strcmp (subsystem, SUBSYSTEM_FRAME_BUFFER) == 0) {
                 ply_trace ("ignoring since we only handle subsystem %s devices after timeout", subsystem);
                 return false;
         }
@@ -578,14 +664,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);
         }
 
@@ -629,6 +716,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)
@@ -655,7 +757,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),
@@ -684,6 +786,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)
@@ -692,9 +862,13 @@ 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);
+        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 ();
@@ -727,10 +901,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)
@@ -918,6 +1099,8 @@ create_devices_for_terminal_and_renderer_type (ply_device_manager_t *manager,
                                 renderer = NULL;
                                 return true;
                         }
+
+                        add_input_devices_to_renderer (manager, renderer);
                 }
         }
 
@@ -1023,6 +1206,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);
 
@@ -1085,6 +1269,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 686b4f4f7225b93b83ddd807c0297c32201c4210..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);
 }
@@ -1101,3 +1072,12 @@ ply_terminal_stop_watching_for_input (ply_terminal_t              *terminal,
         }
 }
 
+void
+ply_terminal_flush_input (ply_terminal_t *terminal)
+{
+        if (!terminal->is_open)
+                return;
+
+        if (tcflush (terminal->fd, TCIFLUSH) < 0)
+                ply_trace ("could not flush input buffer of terminal %s: %m", terminal->name);
+}
index c701f2caa3d91cee5002143a0028f35069f63042..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);
 
@@ -116,7 +117,8 @@ void ply_terminal_stop_watching_for_input (ply_terminal_t              *terminal
                                            ply_terminal_input_handler_t input_handler,
                                            void                        *user_data);
 
+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;
 }
-
index ef715ae4152e4e019577c1ba3274b55b432444ae..d8b13f1c75c2d1ab4378b3597517400e003f93ed 100644 (file)
 /* This file is autogenerated by running:
  * scripts/keymap-render.py > src/libply-splash-graphics/ply-keymap-metadata.h
  */
-struct ply_keymap_metadata
+typedef struct
 {
         const char *name;
         int         offset;
         int         width;
-};
+        int         type;
+} ply_keymap_metadata_t;
+
+typedef enum
+{
+        PLY_LAYOUT_TERMINAL,
+        PLY_LAYOUT_XKB,
+} ply_layout_types_t;
 
-static struct ply_keymap_metadata ply_keymap_metadata[] = {
-        { "dvorak",     10,   113 },
-        { "al",         123,  44  },
-        { "amiga",      167,  101 },
-        { "applkey",    268,  126 },
-        { "at",         394,  46  },
-        { "atari",      440,  81  },
-        { "az",         521,  49  },
-        { "azerty",     570,  104 },
-        { "ba",         674,  49  },
-        { "backspace",  723,  157 },
-        { "bashkir",    880,  119 },
-        { "be",         999,  50  },
-        { "bg",         1049, 51  },
-        { "br",         1100, 48  },
-        { "by",         1148, 51  },
-        { "bywin",      1199, 98  },
-        { "ca",         1297, 47  },
-        { "carpalx",    1344, 118 },
-        { "cf",         1462, 45  },
-        { "ch",         1507, 49  },
-        { "cm",         1556, 59  },
-        { "cn",         1615, 49  },
-        { "croat",      1664, 89  },
-        { "ctrl",       1753, 65  },
-        { "cz",         1818, 47  },
-        { "de",         1865, 51  },
-        { "defkeymap",  1916, 170 },
-        { "dk",         2086, 53  },
-        { "dz",         2139, 50  },
-        { "ee",         2189, 50  },
-        { "emacs",      2239, 106 },
-        { "emacs2",     2345, 122 },
-        { "en",         2467, 51  },
-        { "epo",        2518, 68  },
-        { "es",         2586, 49  },
-        { "et",         2635, 46  },
-        { "euro",       2681, 80  },
-        { "euro1",      2761, 95  },
-        { "euro2",      2856, 97  },
-        { "fi",         2953, 38  },
-        { "fo",         2991, 47  },
-        { "fr",         3038, 44  },
-        { "gb",         3082, 53  },
-        { "ge",         3135, 51  },
-        { "gh",         3186, 52  },
-        { "gr",         3238, 49  },
-        { "hr",         3287, 48  },
-        { "hu",         3335, 50  },
-        { "hu101",      3385, 99  },
-        { "ie",         3484, 42  },
-        { "il",         3526, 36  },
-        { "in",         3562, 43  },
-        { "iq",         3605, 43  },
-        { "ir",         3648, 40  },
-        { "is",         3688, 41  },
-        { "it",         3729, 38  },
-        { "it2",        3767, 54  },
-        { "jp",         3821, 46  },
-        { "jp106",      3867, 96  },
-        { "kazakh",     3963, 112 },
-        { "ke",         4075, 49  },
-        { "keypad",     4124, 114 },
-        { "kr",         4238, 47  },
-        { "ky",         4285, 50  },
-        { "kyrgyz",     4335, 107 },
-        { "kz",         4442, 48  },
-        { "la",         4490, 42  },
-        { "latam",      4532, 97  },
-        { "lk",         4629, 45  },
-        { "lt",         4674, 39  },
-        { "lv",         4713, 44  },
-        { "ma",         4757, 59  },
-        { "pl",         4816, 44  },
-        { "pt",         4860, 46  },
-        { "se",         4906, 48  },
-        { "template",   4954, 142 },
-        { "uk",         5096, 53  },
-        { "us",         5149, 50  },
-        { "md",         5199, 61  },
-        { "me",         5260, 60  },
-        { "mk",         5320, 62  },
-        { "mk0",        5382, 79  },
-        { "ml",         5461, 54  },
-        { "mm",         5515, 71  },
-        { "mt",         5586, 56  },
-        { "ng",         5642, 51  },
-        { "nl",         5693, 44  },
-        { "nl2",        5737, 60  },
-        { "no",         5797, 51  },
-        { "pc110",      5848, 95  },
-        { "ph",         5943, 51  },
-        { "pl1",        5994, 58  },
-        { "pl2",        6052, 60  },
-        { "pl3",        6112, 60  },
-        { "pl4",        6172, 62  },
-        { "ro",         6234, 46  },
-        { "rs",         6280, 44  },
-        { "ru",         6324, 45  },
-        { "ru1",        6369, 61  },
-        { "ru2",        6430, 63  },
-        { "ru3",        6493, 63  },
-        { "ru4",        6556, 65  },
-        { "ruwin",      6621, 95  },
-        { "sg",         6716, 49  },
-        { "si",         6765, 40  },
-        { "sk",         6805, 50  },
-        { "slovene",    6855, 122 },
-        { "sr",         6977, 46  },
-        { "sunkeymap",  7023, 174 },
-        { "sv",         7197, 49  },
-        { "sy",         7246, 49  },
-        { "tj",         7295, 38  },
-        { "tm",         7333, 57  },
-        { "tr",         7390, 44  },
-        { "tralt",      7434, 79  },
-        { "trf",        7513, 55  },
-        { "trq",        7568, 59  },
-        { "ttwin",      7627, 90  },
-        { "tw",         7717, 56  },
-        { "ua",         7773, 50  },
-        { "unicode",    7823, 124 },
-        { "uz",         7947, 50  },
-        { "vn",         7997, 51  },
-        { "wangbe",     8048, 126 },
-        { "wangbe2",    8174, 143 },
-        { "windowkeys", 8317, 188 },
-        { NULL,         } /* End of array marker */
+static ply_keymap_metadata_t ply_keymap_metadata[] = {
+        { "3l",                                        10,    47,  PLY_LAYOUT_TERMINAL },
+        { "dvorak",                                    57,    113, PLY_LAYOUT_TERMINAL },
+        { "adnw",                                      170,   94,  PLY_LAYOUT_TERMINAL },
+        { "al",                                        264,   45,  PLY_LAYOUT_TERMINAL },
+        { "amiga",                                     309,   103, PLY_LAYOUT_TERMINAL },
+        { "apple",                                     412,   93,  PLY_LAYOUT_TERMINAL },
+        { "applkey",                                   505,   126, PLY_LAYOUT_TERMINAL },
+        { "ara",                                       631,   62,  PLY_LAYOUT_TERMINAL },
+        { "at",                                        693,   46,  PLY_LAYOUT_TERMINAL },
+        { "atari",                                     739,   81,  PLY_LAYOUT_TERMINAL },
+        { "az",                                        820,   49,  PLY_LAYOUT_TERMINAL },
+        { "azerty",                                    869,   105, PLY_LAYOUT_TERMINAL },
+        { "ba",                                        974,   50,  PLY_LAYOUT_TERMINAL },
+        { "backspace",                                 1024,  157, PLY_LAYOUT_TERMINAL },
+        { "bashkir",                                   1181,  119, PLY_LAYOUT_TERMINAL },
+        { "be",                                        1300,  50,  PLY_LAYOUT_TERMINAL },
+        { "bg",                                        1350,  51,  PLY_LAYOUT_TERMINAL },
+        { "bone",                                      1401,  84,  PLY_LAYOUT_TERMINAL },
+        { "br",                                        1485,  48,  PLY_LAYOUT_TERMINAL },
+        { "by",                                        1533,  51,  PLY_LAYOUT_TERMINAL },
+        { "bywin",                                     1584,  98,  PLY_LAYOUT_TERMINAL },
+        { "ca",                                        1682,  48,  PLY_LAYOUT_TERMINAL },
+        { "carpalx",                                   1730,  118, PLY_LAYOUT_TERMINAL },
+        { "cf",                                        1848,  46,  PLY_LAYOUT_TERMINAL },
+        { "ch",                                        1894,  49,  PLY_LAYOUT_TERMINAL },
+        { "cm",                                        1943,  59,  PLY_LAYOUT_TERMINAL },
+        { "cn",                                        2002,  49,  PLY_LAYOUT_TERMINAL },
+        { "croat",                                     2051,  89,  PLY_LAYOUT_TERMINAL },
+        { "ctrl",                                      2140,  66,  PLY_LAYOUT_TERMINAL },
+        { "cz",                                        2206,  47,  PLY_LAYOUT_TERMINAL },
+        { "de",                                        2253,  51,  PLY_LAYOUT_TERMINAL },
+        { "defkeymap",                                 2304,  170, PLY_LAYOUT_TERMINAL },
+        { "dk",                                        2474,  53,  PLY_LAYOUT_TERMINAL },
+        { "dz",                                        2527,  50,  PLY_LAYOUT_TERMINAL },
+        { "ee",                                        2577,  50,  PLY_LAYOUT_TERMINAL },
+        { "emacs",                                     2627,  106, PLY_LAYOUT_TERMINAL },
+        { "emacs2",                                    2733,  122, PLY_LAYOUT_TERMINAL },
+        { "en",                                        2855,  51,  PLY_LAYOUT_TERMINAL },
+        { "epo",                                       2906,  69,  PLY_LAYOUT_TERMINAL },
+        { "es",                                        2975,  49,  PLY_LAYOUT_TERMINAL },
+        { "et",                                        3024,  46,  PLY_LAYOUT_TERMINAL },
+        { "euro",                                      3070,  81,  PLY_LAYOUT_TERMINAL },
+        { "euro1",                                     3151,  92,  PLY_LAYOUT_TERMINAL },
+        { "euro2",                                     3243,  97,  PLY_LAYOUT_TERMINAL },
+        { "fa",                                        3340,  46,  PLY_LAYOUT_TERMINAL },
+        { "fi",                                        3386,  38,  PLY_LAYOUT_TERMINAL },
+        { "fo",                                        3424,  48,  PLY_LAYOUT_TERMINAL },
+        { "fr",                                        3472,  44,  PLY_LAYOUT_TERMINAL },
+        { "gb",                                        3516,  54,  PLY_LAYOUT_TERMINAL },
+        { "ge",                                        3570,  52,  PLY_LAYOUT_TERMINAL },
+        { "gh",                                        3622,  53,  PLY_LAYOUT_TERMINAL },
+        { "gr",                                        3675,  50,  PLY_LAYOUT_TERMINAL },
+        { "hr",                                        3725,  48,  PLY_LAYOUT_TERMINAL },
+        { "hu",                                        3773,  50,  PLY_LAYOUT_TERMINAL },
+        { "hu101",                                     3823,  96,  PLY_LAYOUT_TERMINAL },
+        { "id",                                        3919,  43,  PLY_LAYOUT_TERMINAL },
+        { "ie",                                        3962,  42,  PLY_LAYOUT_TERMINAL },
+        { "il",                                        4004,  37,  PLY_LAYOUT_TERMINAL },
+        { "in",                                        4041,  43,  PLY_LAYOUT_TERMINAL },
+        { "iq",                                        4084,  43,  PLY_LAYOUT_TERMINAL },
+        { "ir",                                        4127,  40,  PLY_LAYOUT_TERMINAL },
+        { "is",                                        4167,  41,  PLY_LAYOUT_TERMINAL },
+        { "it",                                        4208,  38,  PLY_LAYOUT_TERMINAL },
+        { "it2",                                       4246,  54,  PLY_LAYOUT_TERMINAL },
+        { "jp",                                        4300,  46,  PLY_LAYOUT_TERMINAL },
+        { "jp106",                                     4346,  96,  PLY_LAYOUT_TERMINAL },
+        { "kazakh",                                    4442,  113, PLY_LAYOUT_TERMINAL },
+        { "ke",                                        4555,  49,  PLY_LAYOUT_TERMINAL },
+        { "keypad",                                    4604,  114, PLY_LAYOUT_TERMINAL },
+        { "ko",                                        4718,  51,  PLY_LAYOUT_TERMINAL },
+        { "koy",                                       4769,  67,  PLY_LAYOUT_TERMINAL },
+        { "kr",                                        4836,  47,  PLY_LAYOUT_TERMINAL },
+        { "ky",                                        4883,  50,  PLY_LAYOUT_TERMINAL },
+        { "kyrgyz",                                    4933,  108, PLY_LAYOUT_TERMINAL },
+        { "kz",                                        5041,  48,  PLY_LAYOUT_TERMINAL },
+        { "la",                                        5089,  42,  PLY_LAYOUT_TERMINAL },
+        { "latam",                                     5131,  96,  PLY_LAYOUT_TERMINAL },
+        { "lk",                                        5227,  44,  PLY_LAYOUT_TERMINAL },
+        { "lt",                                        5271,  38,  PLY_LAYOUT_TERMINAL },
+        { "lv",                                        5309,  43,  PLY_LAYOUT_TERMINAL },
+        { "ma",                                        5352,  60,  PLY_LAYOUT_TERMINAL },
+        { "no",                                        5412,  52,  PLY_LAYOUT_TERMINAL },
+        { "pl",                                        5464,  45,  PLY_LAYOUT_TERMINAL },
+        { "pt",                                        5509,  46,  PLY_LAYOUT_TERMINAL },
+        { "se",                                        5555,  48,  PLY_LAYOUT_TERMINAL },
+        { "template",                                  5603,  142, PLY_LAYOUT_TERMINAL },
+        { "uk",                                        5745,  53,  PLY_LAYOUT_TERMINAL },
+        { "us",                                        5798,  50,  PLY_LAYOUT_TERMINAL },
+        { "md",                                        5848,  61,  PLY_LAYOUT_TERMINAL },
+        { "me",                                        5909,  60,  PLY_LAYOUT_TERMINAL },
+        { "mk",                                        5969,  62,  PLY_LAYOUT_TERMINAL },
+        { "mk0",                                       6031,  79,  PLY_LAYOUT_TERMINAL },
+        { "ml",                                        6110,  55,  PLY_LAYOUT_TERMINAL },
+        { "mm",                                        6165,  71,  PLY_LAYOUT_TERMINAL },
+        { "mt",                                        6236,  56,  PLY_LAYOUT_TERMINAL },
+        { "neo",                                       6292,  68,  PLY_LAYOUT_TERMINAL },
+        { "neoqwertz",                                 6360,  162, PLY_LAYOUT_TERMINAL },
+        { "ng",                                        6522,  51,  PLY_LAYOUT_TERMINAL },
+        { "nl",                                        6573,  45,  PLY_LAYOUT_TERMINAL },
+        { "nl2",                                       6618,  60,  PLY_LAYOUT_TERMINAL },
+        { "pc110",                                     6678,  95,  PLY_LAYOUT_TERMINAL },
+        { "ph",                                        6773,  51,  PLY_LAYOUT_TERMINAL },
+        { "pl1",                                       6824,  55,  PLY_LAYOUT_TERMINAL },
+        { "pl2",                                       6879,  60,  PLY_LAYOUT_TERMINAL },
+        { "pl3",                                       6939,  61,  PLY_LAYOUT_TERMINAL },
+        { "pl4",                                       7000,  62,  PLY_LAYOUT_TERMINAL },
+        { "ro",                                        7062,  47,  PLY_LAYOUT_TERMINAL },
+        { "rs",                                        7109,  44,  PLY_LAYOUT_TERMINAL },
+        { "ru",                                        7153,  45,  PLY_LAYOUT_TERMINAL },
+        { "ru1",                                       7198,  58,  PLY_LAYOUT_TERMINAL },
+        { "ru2",                                       7256,  63,  PLY_LAYOUT_TERMINAL },
+        { "ru3",                                       7319,  64,  PLY_LAYOUT_TERMINAL },
+        { "ru4",                                       7383,  65,  PLY_LAYOUT_TERMINAL },
+        { "ruwin",                                     7448,  95,  PLY_LAYOUT_TERMINAL },
+        { "sg",                                        7543,  49,  PLY_LAYOUT_TERMINAL },
+        { "si",                                        7592,  40,  PLY_LAYOUT_TERMINAL },
+        { "sk",                                        7632,  50,  PLY_LAYOUT_TERMINAL },
+        { "slovene",                                   7682,  122, PLY_LAYOUT_TERMINAL },
+        { "sr",                                        7804,  46,  PLY_LAYOUT_TERMINAL },
+        { "sunkeymap",                                 7850,  174, PLY_LAYOUT_TERMINAL },
+        { "sv",                                        8024,  49,  PLY_LAYOUT_TERMINAL },
+        { "sy",                                        8073,  49,  PLY_LAYOUT_TERMINAL },
+        { "tj",                                        8122,  38,  PLY_LAYOUT_TERMINAL },
+        { "tm",                                        8160,  57,  PLY_LAYOUT_TERMINAL },
+        { "tr",                                        8217,  44,  PLY_LAYOUT_TERMINAL },
+        { "tralt",                                     8261,  79,  PLY_LAYOUT_TERMINAL },
+        { "trf",                                       8340,  56,  PLY_LAYOUT_TERMINAL },
+        { "trq",                                       8396,  59,  PLY_LAYOUT_TERMINAL },
+        { "ttwin",                                     8455,  90,  PLY_LAYOUT_TERMINAL },
+        { "tw",                                        8545,  56,  PLY_LAYOUT_TERMINAL },
+        { "ua",                                        8601,  51,  PLY_LAYOUT_TERMINAL },
+        { "unicode",                                   8652,  124, PLY_LAYOUT_TERMINAL },
+        { "us1",                                       8776,  61,  PLY_LAYOUT_TERMINAL },
+        { "uz",                                        8837,  50,  PLY_LAYOUT_TERMINAL },
+        { "vn",                                        8887,  51,  PLY_LAYOUT_TERMINAL },
+        { "wangbe",                                    8938,  127, PLY_LAYOUT_TERMINAL },
+        { "wangbe2",                                   9065,  144, PLY_LAYOUT_TERMINAL },
+        { "windowkeys",                                9209,  188, PLY_LAYOUT_TERMINAL },
+        { "English (US)",                              9397,  182, PLY_LAYOUT_XKB      },
+        { "Dari",                                      9579,  75,  PLY_LAYOUT_XKB      },
+        { "Arabic",                                    9654,  107, PLY_LAYOUT_XKB      },
+        { "Albanian",                                  9761,  139, PLY_LAYOUT_XKB      },
+        { "Armenian",                                  9900,  152, PLY_LAYOUT_XKB      },
+        { "German (Austria)",                          10052, 252, PLY_LAYOUT_XKB      },
+        { "English (Australian)",                      10304, 284, PLY_LAYOUT_XKB      },
+        { "Azerbaijani",                               10588, 172, PLY_LAYOUT_XKB      },
+        { "Belarusian",                                10760, 162, PLY_LAYOUT_XKB      },
+        { "Belgian",                                   10922, 121, PLY_LAYOUT_XKB      },
+        { "Bangla",                                    11043, 113, PLY_LAYOUT_XKB      },
+        { "Indian",                                    11156, 101, PLY_LAYOUT_XKB      },
+        { "Bosnian",                                   11257, 126, PLY_LAYOUT_XKB      },
+        { "Portuguese (Brazil)",                       11383, 279, PLY_LAYOUT_XKB      },
+        { "Bulgarian",                                 11662, 150, PLY_LAYOUT_XKB      },
+        { "Berber (Algeria, Latin)",                   11812, 317, PLY_LAYOUT_XKB      },
+        { "Arabic (Morocco)",                          12129, 249, PLY_LAYOUT_XKB      },
+        { "English (Cameroon)",                        12378, 285, PLY_LAYOUT_XKB      },
+        { "Burmese",                                   12663, 139, PLY_LAYOUT_XKB      },
+        { "French (Canada)",                           12802, 237, PLY_LAYOUT_XKB      },
+        { "French (Democratic Republic of the Congo)", 13039, 594, PLY_LAYOUT_XKB      },
+        { "Chinese",                                   13633, 125, PLY_LAYOUT_XKB      },
+        { "Croatian",                                  13758, 134, PLY_LAYOUT_XKB      },
+        { "Czech",                                     13892, 99,  PLY_LAYOUT_XKB      },
+        { "Danish",                                    13991, 111, PLY_LAYOUT_XKB      },
+        { "Dutch",                                     14102, 98,  PLY_LAYOUT_XKB      },
+        { "Dzongkha",                                  14200, 155, PLY_LAYOUT_XKB      },
+        { "Estonian",                                  14355, 135, PLY_LAYOUT_XKB      },
+        { "Persian",                                   14490, 118, PLY_LAYOUT_XKB      },
+        { "Iraqi",                                     14608, 79,  PLY_LAYOUT_XKB      },
+        { "Faroese",                                   14687, 125, PLY_LAYOUT_XKB      },
+        { "Finnish",                                   14812, 115, PLY_LAYOUT_XKB      },
+        { "French",                                    14927, 110, PLY_LAYOUT_XKB      },
+        { "English (Ghana)",                           15037, 232, PLY_LAYOUT_XKB      },
+        { "N'Ko (AZERTY)",                             15269, 225, PLY_LAYOUT_XKB      },
+        { "Georgian",                                  15494, 144, PLY_LAYOUT_XKB      },
+        { "German",                                    15638, 128, PLY_LAYOUT_XKB      },
+        { "Greek",                                     15766, 102, PLY_LAYOUT_XKB      },
+        { "Hungarian",                                 15868, 160, PLY_LAYOUT_XKB      },
+        { "Icelandic",                                 16028, 138, PLY_LAYOUT_XKB      },
+        { "Hebrew",                                    16166, 126, PLY_LAYOUT_XKB      },
+        { "Italian",                                   16292, 103, PLY_LAYOUT_XKB      },
+        { "Japanese",                                  16395, 144, PLY_LAYOUT_XKB      },
+        { "Kyrgyz",                                    16539, 112, PLY_LAYOUT_XKB      },
+        { "Khmer (Cambodia)",                          16651, 272, PLY_LAYOUT_XKB      },
+        { "Kazakh",                                    16923, 117, PLY_LAYOUT_XKB      },
+        { "Lao",                                       17040, 67,  PLY_LAYOUT_XKB      },
+        { "Spanish (Latin American)",                  17107, 355, PLY_LAYOUT_XKB      },
+        { "Lithuanian",                                17462, 160, PLY_LAYOUT_XKB      },
+        { "Latvian",                                   17622, 116, PLY_LAYOUT_XKB      },
+        { "Maori",                                     17738, 96,  PLY_LAYOUT_XKB      },
+        { "Montenegrin",                               17834, 192, PLY_LAYOUT_XKB      },
+        { "Macedonian",                                18026, 181, PLY_LAYOUT_XKB      },
+        { "Maltese",                                   18207, 125, PLY_LAYOUT_XKB      },
+        { "Mongolian",                                 18332, 162, PLY_LAYOUT_XKB      },
+        { "Norwegian",                                 18494, 168, PLY_LAYOUT_XKB      },
+        { "Polish",                                    18662, 100, PLY_LAYOUT_XKB      },
+        { "Portuguese",                                18762, 173, PLY_LAYOUT_XKB      },
+        { "Romanian",                                  18935, 154, PLY_LAYOUT_XKB      },
+        { "Russian",                                   19089, 122, PLY_LAYOUT_XKB      },
+        { "Serbian",                                   19211, 122, PLY_LAYOUT_XKB      },
+        { "Slovenian",                                 19333, 151, PLY_LAYOUT_XKB      },
+        { "Slovak",                                    19484, 111, PLY_LAYOUT_XKB      },
+        { "Spanish",                                   19595, 125, PLY_LAYOUT_XKB      },
+        { "Swedish",                                   19720, 132, PLY_LAYOUT_XKB      },
+        { "German (Switzerland)",                      19852, 316, PLY_LAYOUT_XKB      },
+        { "Arabic (Syria)",                            20168, 200, PLY_LAYOUT_XKB      },
+        { "Tajik",                                     20368, 87,  PLY_LAYOUT_XKB      },
+        { "Sinhala (phonetic)",                        20455, 262, PLY_LAYOUT_XKB      },
+        { "Thai",                                      20717, 78,  PLY_LAYOUT_XKB      },
+        { "Turkish",                                   20795, 121, PLY_LAYOUT_XKB      },
+        { "Taiwanese",                                 20916, 164, PLY_LAYOUT_XKB      },
+        { "Ukrainian",                                 21080, 149, PLY_LAYOUT_XKB      },
+        { "English (UK)",                              21229, 185, PLY_LAYOUT_XKB      },
+        { "Uzbek",                                     21414, 105, PLY_LAYOUT_XKB      },
+        { "Vietnamese",                                21519, 179, PLY_LAYOUT_XKB      },
+        { "Korean",                                    21698, 115, PLY_LAYOUT_XKB      },
+        { "Irish",                                     21813, 77,  PLY_LAYOUT_XKB      },
+        { "Urdu (Pakistan)",                           21890, 227, PLY_LAYOUT_XKB      },
+        { "Dhivehi",                                   22117, 120, PLY_LAYOUT_XKB      },
+        { "English (South Africa)",                    22237, 310, PLY_LAYOUT_XKB      },
+        { "Esperanto",                                 22547, 156, PLY_LAYOUT_XKB      },
+        { "Nepali",                                    22703, 106, PLY_LAYOUT_XKB      },
+        { "English (Nigeria)",                         22809, 245, PLY_LAYOUT_XKB      },
+        { "Amharic",                                   23054, 134, PLY_LAYOUT_XKB      },
+        { "Wolof",                                     23188, 106, PLY_LAYOUT_XKB      },
+        { "Braille",                                   23294, 107, PLY_LAYOUT_XKB      },
+        { "Turkmen",                                   23401, 142, PLY_LAYOUT_XKB      },
+        { "Bambara",                                   23543, 141, PLY_LAYOUT_XKB      },
+        { "Swahili (Tanzania)",                        23684, 267, PLY_LAYOUT_XKB      },
+        { "French (Togo)",                             23951, 206, PLY_LAYOUT_XKB      },
+        { "Swahili (Kenya)",                           24157, 228, PLY_LAYOUT_XKB      },
+        { "Tswana",                                    24385, 124, PLY_LAYOUT_XKB      },
+        { "Filipino",                                  24509, 119, PLY_LAYOUT_XKB      },
+        { "Moldavian",                                 24628, 158, PLY_LAYOUT_XKB      },
+        { "Indonesian (Latin)",                        24786, 259, PLY_LAYOUT_XKB      },
+        { "Malay (Jawi, Arabic Keyboard)",             25045, 423, PLY_LAYOUT_XKB      },
+        { "A user-defined custom Layout",              25468, 423, PLY_LAYOUT_XKB      },
+        { NULL,                                        } /* End of array marker */
 };
index 7392ee6b048ef30257e3cde6aea036bfdd53ec0f..e91eb0d6b357007f9f83703483714e91370c1a72 100644 (file)
@@ -181,10 +181,12 @@ ply_buffer_append_bytes (ply_buffer_t *buffer,
 {
         assert (buffer != NULL);
         assert (bytes_in != NULL);
-        assert (length != 0);
 
         const uint8_t *bytes = bytes_in;
 
+        if (length == 0)
+                return;
+
         if (length > PLY_BUFFER_MAX_BUFFER_CAPACITY) {
                 bytes += length - (PLY_BUFFER_MAX_BUFFER_CAPACITY - 1);
                 length = (PLY_BUFFER_MAX_BUFFER_CAPACITY - 1);
@@ -257,4 +259,3 @@ ply_buffer_clear (ply_buffer_t *buffer)
         memset (buffer->data, '\0', buffer->capacity);
         buffer->size = 0;
 }
-
index 2a3e187b9c006f2059c0096bc5680321460a70e7..1dd848f51285db18b5b979a319f00e24d9b6e8b9 100644 (file)
@@ -56,6 +56,9 @@ ply_list_node_t *ply_list_get_nth_node (ply_list_t *list,
 ply_list_node_t *ply_list_get_next_node (ply_list_t      *list,
                                          ply_list_node_t *node);
 void *ply_list_node_get_data (ply_list_node_t *node);
+
+#define ply_list_foreach(list, node) \
+        for (node = ply_list_get_first_node (list); node != NULL; node = ply_list_get_next_node (list, node))
 #endif
 
 #endif /* PLY_LIST_H */
index 620fa9fb3f4a696fa2b5f7965f6be0c8315062f7..dda73d3d7f4d71a3d52fb4f0c1707f2a0db99641 100644 (file)
 #include "ply-list.h"
 #include "ply-utils.h"
 
+typedef enum
+{
+        PLY_TRIGGER_HANDLER_TYPE_HANDLER,
+        PLY_TRIGGER_HANDLER_TYPE_INSTANCE_HANDLER
+} ply_trigger_handler_type_t;
+
 typedef struct
 {
-        ply_trigger_handler_t handler;
-        void                 *user_data;
+        ply_trigger_handler_type_t handler_type;
+        union
+        {
+                ply_trigger_handler_t          handler;
+                ply_trigger_instance_handler_t instance_handler;
+        };
+        void                      *user_data;
 } ply_trigger_closure_t;
 
 struct _ply_trigger
 {
         ply_list_t     *closures;
 
+        void           *instance;
+
         ply_trigger_t **free_address;
         int             ignore_count;
 };
@@ -90,6 +103,62 @@ ply_trigger_free (ply_trigger_t *trigger)
         free (trigger);
 }
 
+void
+ply_trigger_set_instance (ply_trigger_t *trigger,
+                          void          *instance)
+{
+        trigger->instance = instance;
+}
+
+void *
+ply_trigger_get_instance (ply_trigger_t *trigger)
+{
+        return trigger->instance;
+}
+
+void
+ply_trigger_add_instance_handler (ply_trigger_t                 *trigger,
+                                  ply_trigger_instance_handler_t handler,
+                                  void                          *user_data)
+{
+        ply_trigger_closure_t *closure;
+
+        closure = calloc (1, sizeof(ply_trigger_closure_t));
+        closure->handler_type = PLY_TRIGGER_HANDLER_TYPE_INSTANCE_HANDLER;
+        closure->instance_handler = handler;
+        closure->user_data = user_data;
+
+        ply_list_append_data (trigger->closures, closure);
+}
+
+void
+ply_trigger_remove_instance_handler (ply_trigger_t                 *trigger,
+                                     ply_trigger_instance_handler_t handler,
+                                     void                          *user_data)
+{
+        ply_list_node_t *node;
+
+        node = ply_list_get_first_node (trigger->closures);
+        while (node != NULL) {
+                ply_list_node_t *next_node;
+                ply_trigger_closure_t *closure;
+
+                closure = (ply_trigger_closure_t *) ply_list_node_get_data (node);
+
+                next_node = ply_list_get_next_node (trigger->closures, node);
+
+                if (closure->handler_type == PLY_TRIGGER_HANDLER_TYPE_INSTANCE_HANDLER &&
+                    closure->instance_handler == handler &&
+                    closure->user_data == user_data) {
+                        free (closure);
+                        ply_list_remove_node (trigger->closures, node);
+                        break;
+                }
+
+                node = next_node;
+        }
+}
+
 void
 ply_trigger_add_handler (ply_trigger_t        *trigger,
                          ply_trigger_handler_t handler,
@@ -98,6 +167,7 @@ ply_trigger_add_handler (ply_trigger_t        *trigger,
         ply_trigger_closure_t *closure;
 
         closure = calloc (1, sizeof(ply_trigger_closure_t));
+        closure->handler_type = PLY_TRIGGER_HANDLER_TYPE_HANDLER;
         closure->handler = handler;
         closure->user_data = user_data;
 
@@ -120,7 +190,9 @@ ply_trigger_remove_handler (ply_trigger_t        *trigger,
 
                 next_node = ply_list_get_next_node (trigger->closures, node);
 
-                if (closure->handler == handler && closure->user_data == user_data) {
+                if (closure->handler_type == PLY_TRIGGER_HANDLER_TYPE_HANDLER &&
+                    closure->handler == handler &&
+                    closure->user_data == user_data) {
                         free (closure);
                         ply_list_remove_node (trigger->closures, node);
                         break;
@@ -158,8 +230,16 @@ ply_trigger_pull (ply_trigger_t *trigger,
                 closure = (ply_trigger_closure_t *) ply_list_node_get_data (node);
 
                 next_node = ply_list_get_next_node (trigger->closures, node);
-
-                closure->handler (closure->user_data, data, trigger);
+                switch (closure->handler_type) {
+                case PLY_TRIGGER_HANDLER_TYPE_HANDLER:
+                        closure->handler (closure->user_data, data, trigger);
+                        break;
+                case PLY_TRIGGER_HANDLER_TYPE_INSTANCE_HANDLER:
+                        closure->instance_handler (closure->user_data, trigger->instance, data, trigger);
+                        break;
+                default:
+                        break;
+                }
 
                 node = next_node;
         }
index 162a794c07e0fdd22f03b28df9724d8070553628..bb088386864fa9ae57aee821186adfb8449022a2 100644 (file)
@@ -33,6 +33,11 @@ typedef struct _ply_trigger ply_trigger_t;
 typedef void (*ply_trigger_handler_t) (void          *user_data,
                                        const void    *trigger_data,
                                        ply_trigger_t *trigger);
+
+typedef void (*ply_trigger_instance_handler_t) (void          *user_data,
+                                                void          *instance,
+                                                const void    *trigger_data,
+                                                ply_trigger_t *trigger);
 #ifndef PLY_HIDE_FUNCTION_DECLARATIONS
 ply_trigger_t *ply_trigger_new (ply_trigger_t **free_address);
 
@@ -42,6 +47,18 @@ void ply_trigger_add_handler (ply_trigger_t        *trigger,
 void ply_trigger_remove_handler (ply_trigger_t        *trigger,
                                  ply_trigger_handler_t handler,
                                  void                 *user_data);
+
+void ply_trigger_set_instance (ply_trigger_t *trigger,
+                               void          *instance);
+void *ply_trigger_get_instance (ply_trigger_t *trigger);
+
+void ply_trigger_add_instance_handler (ply_trigger_t                 *trigger,
+                                       ply_trigger_instance_handler_t handler,
+                                       void                          *user_data);
+void ply_trigger_remove_instance_handler (ply_trigger_t                 *trigger,
+                                          ply_trigger_instance_handler_t handler,
+                                          void                          *user_data);
+
 void ply_trigger_free (ply_trigger_t *trigger);
 
 void ply_trigger_ignore_next_pull (ply_trigger_t *trigger);
index fb0c7e8add0d9eb7d38f715e38008d7be790f556..56c9f8981552882c79e0dce0e216c6b03bfb5073 100644 (file)
@@ -51,6 +51,7 @@
 #include "ply-array.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-hashtable.h"
@@ -94,6 +95,7 @@ struct _ply_renderer_input_source
 {
         ply_renderer_backend_t             *backend;
         ply_fd_watch_t                     *terminal_input_watch;
+        ply_list_t                         *input_devices;
 
         ply_buffer_t                       *key_buffer;
 
@@ -158,6 +160,7 @@ struct _ply_renderer_backend
 
         uint32_t                    is_active : 1;
         uint32_t                    requires_explicit_flushing : 1;
+        uint32_t                    input_source_is_open : 1;
 
         int                         panel_width;
         int                         panel_height;
@@ -166,6 +169,8 @@ struct _ply_renderer_backend
 };
 
 ply_renderer_plugin_interface_t *ply_renderer_backend_get_interface (void);
+
+static bool using_input_device (ply_renderer_input_source_t *backend);
 static bool open_input_source (ply_renderer_backend_t      *backend,
                                ply_renderer_input_source_t *input_source);
 static void flush_head (ply_renderer_backend_t *backend,
@@ -884,6 +889,7 @@ create_backend (const char     *device_name,
         backend->loop = ply_event_loop_get_default ();
         backend->heads = ply_list_new ();
         backend->input_source.key_buffer = ply_buffer_new ();
+        backend->input_source.input_devices = ply_list_new ();
         backend->terminal = terminal;
         backend->requires_explicit_flushing = true;
         backend->output_buffers = ply_hashtable_new (ply_hashtable_direct_hash,
@@ -908,6 +914,7 @@ destroy_backend (ply_renderer_backend_t *backend)
         free (backend->device_name);
         ply_hashtable_free (backend->output_buffers);
         ply_hashtable_free (backend->heads_by_controller_id);
+        ply_list_free (backend->input_source.input_devices);
 
         free (backend->outputs);
         free (backend);
@@ -1236,14 +1243,12 @@ find_controller_for_output (ply_renderer_backend_t *backend,
 
                 if (!(possible_controllers & (1 << i)))
                         continue; /* controller not usable for this connector */
-
                 for (j = 0; j < outputs_len; j++) {
                         if (outputs[j].controller_id == controller_id)
                                 break;
                 }
                 if (j < outputs_len)
                         continue; /* controller already in use */
-
                 return controller_id;
         }
 
@@ -1744,9 +1749,18 @@ get_input_source (ply_renderer_backend_t *backend)
 }
 
 static void
-on_key_event (ply_renderer_input_source_t *input_source,
-              int                          terminal_fd)
+on_terminal_key_event (ply_renderer_input_source_t *input_source)
 {
+        ply_renderer_backend_t *backend = input_source->backend;
+        int terminal_fd;
+
+        if (using_input_device (input_source)) {
+                ply_terminal_flush_input (backend->terminal);
+                return;
+        }
+
+        terminal_fd = ply_terminal_get_fd (backend->terminal);
+
         ply_buffer_append_from_fd (input_source->key_buffer,
                                    terminal_fd);
 
@@ -1754,6 +1768,34 @@ on_key_event (ply_renderer_input_source_t *input_source,
                 input_source->handler (input_source->user_data, input_source->key_buffer, input_source);
 }
 
+static void
+on_input_device_key (ply_renderer_input_source_t *input_source,
+                     ply_input_device_t          *input_device,
+                     const char                  *text)
+{
+        int len = strlen (text);
+        if (len > 0)
+                ply_buffer_append_bytes (input_source->key_buffer, text, len);
+
+        if (input_source->handler != NULL)
+                input_source->handler (input_source->user_data, input_source->key_buffer, input_source);
+}
+
+static void
+on_input_leds_changed (ply_renderer_input_source_t *input_source,
+                       ply_input_device_t          *input_device)
+{
+        ply_xkb_keyboard_state_t *state;
+        ply_list_node_t *node;
+
+        state = ply_input_device_get_state (input_device);
+
+        ply_list_foreach (input_source->input_devices, node) {
+                ply_input_device_t *set_input_device = ply_list_node_get_data (node);
+                ply_input_device_set_state (set_input_device, state);
+        }
+}
+
 static void
 on_input_source_disconnected (ply_renderer_input_source_t *input_source)
 {
@@ -1762,6 +1804,37 @@ on_input_source_disconnected (ply_renderer_input_source_t *input_source)
         open_input_source (input_source->backend, input_source);
 }
 
+static bool
+using_input_device (ply_renderer_input_source_t *input_source)
+{
+        return ply_list_get_length (input_source->input_devices) > 0;
+}
+
+static void
+watch_input_device (ply_renderer_backend_t *backend,
+                    ply_input_device_t     *input_device)
+{
+        ply_trace ("Listening for keys from device '%s'", ply_input_device_get_name (input_device));
+
+        ply_input_device_watch_for_input (input_device,
+                                          (ply_input_device_input_handler_t) on_input_device_key,
+                                          (ply_input_device_leds_changed_handler_t) on_input_leds_changed,
+                                          &backend->input_source);
+}
+
+static void
+watch_input_devices (ply_renderer_backend_t *backend)
+{
+        ply_list_node_t *node;
+        ply_renderer_input_source_t *input_source = &backend->input_source;
+
+        ply_list_foreach (input_source->input_devices, node) {
+                ply_input_device_t *input_device = ply_list_node_get_data (node);
+
+                watch_input_device (backend, input_device);
+        }
+}
+
 static bool
 open_input_source (ply_renderer_backend_t      *backend,
                    ply_renderer_input_source_t *input_source)
@@ -1771,16 +1844,24 @@ open_input_source (ply_renderer_backend_t      *backend,
         assert (backend != NULL);
         assert (has_input_source (backend, input_source));
 
+        if (!backend->input_source_is_open)
+                watch_input_devices (backend);
+
         if (backend->terminal == NULL)
                 return false;
 
-        terminal_fd = ply_terminal_get_fd (backend->terminal);
+        if (backend->terminal != NULL) {
+                terminal_fd = ply_terminal_get_fd (backend->terminal);
+
+                input_source->terminal_input_watch = ply_event_loop_watch_fd (backend->loop, terminal_fd, PLY_EVENT_LOOP_FD_STATUS_HAS_DATA,
+                                                                              (ply_event_handler_t) on_terminal_key_event,
+                                                                              (ply_event_handler_t)
+                                                                              on_input_source_disconnected, input_source);
+        }
 
         input_source->backend = backend;
-        input_source->terminal_input_watch = ply_event_loop_watch_fd (backend->loop, terminal_fd, PLY_EVENT_LOOP_FD_STATUS_HAS_DATA,
-                                                                      (ply_event_handler_t) on_key_event,
-                                                                      (ply_event_handler_t)
-                                                                      on_input_source_disconnected, input_source);
+        backend->input_source_is_open = true;
+
         return true;
 }
 
@@ -1804,12 +1885,28 @@ close_input_source (ply_renderer_backend_t      *backend,
         assert (backend != NULL);
         assert (has_input_source (backend, input_source));
 
-        if (backend->terminal == NULL)
+        if (!backend->input_source_is_open)
                 return;
 
-        ply_event_loop_stop_watching_fd (backend->loop, input_source->terminal_input_watch);
-        input_source->terminal_input_watch = NULL;
+        if (using_input_device (input_source)) {
+                ply_list_node_t *node;
+                ply_list_foreach (input_source->input_devices, node) {
+                        ply_input_device_t *input_device = ply_list_node_get_data (node);
+                        ply_input_device_stop_watching_for_input (input_device,
+                                                                  (ply_input_device_input_handler_t) on_input_device_key,
+                                                                  (ply_input_device_leds_changed_handler_t) on_input_leds_changed,
+                                                                  &backend->input_source);
+                }
+        }
+
+        if (input_source->terminal_input_watch != NULL) {
+                ply_event_loop_stop_watching_fd (backend->loop, input_source->terminal_input_watch);
+                input_source->terminal_input_watch = NULL;
+        }
+
         input_source->backend = NULL;
+
+        backend->input_source_is_open = false;
 }
 
 static bool
@@ -1829,9 +1926,30 @@ get_panel_properties (ply_renderer_backend_t      *backend,
         return true;
 }
 
+static ply_input_device_t *
+get_any_input_device_with_leds (ply_renderer_backend_t *backend)
+{
+        ply_list_node_t *node;
+
+        ply_list_foreach (backend->input_source.input_devices, node) {
+                ply_input_device_t *input_device;
+
+                input_device = ply_list_node_get_data (node);
+
+                if (ply_input_device_is_keyboard_with_leds (input_device))
+                        return input_device;
+        }
+
+        return NULL;
+}
+
 static bool
 get_capslock_state (ply_renderer_backend_t *backend)
 {
+        if (using_input_device (&backend->input_source)) {
+                ply_input_device_t *dev = get_any_input_device_with_leds (backend);
+                return ply_input_device_get_capslock_state (dev);
+        }
         if (!backend->terminal)
                 return false;
 
@@ -1841,12 +1959,64 @@ get_capslock_state (ply_renderer_backend_t *backend)
 static const char *
 get_keymap (ply_renderer_backend_t *backend)
 {
+        if (using_input_device (&backend->input_source)) {
+                ply_input_device_t *dev = get_any_input_device_with_leds (backend);
+                const char *keymap = ply_input_device_get_keymap (dev);
+                if (keymap != NULL) {
+                        return keymap;
+                }
+        }
         if (!backend->terminal)
                 return NULL;
 
         return ply_terminal_get_keymap (backend->terminal);
 }
 
+static void
+sync_input_devices (ply_renderer_backend_t *backend)
+{
+        ply_list_node_t *node;
+        ply_xkb_keyboard_state_t *xkb_state;
+        ply_input_device_t *source_input_device;
+
+        source_input_device = get_any_input_device_with_leds (backend);
+
+        if (source_input_device == NULL)
+                return;
+
+        xkb_state = ply_input_device_get_state (source_input_device);
+
+        ply_list_foreach (backend->input_source.input_devices, node) {
+                ply_input_device_t *target_input_device = ply_list_node_get_data (node);
+
+                if (source_input_device == target_input_device)
+                        continue;
+
+                ply_input_device_set_state (target_input_device, xkb_state);
+        }
+}
+
+static void
+add_input_device (ply_renderer_backend_t *backend,
+                  ply_input_device_t     *input_device)
+{
+        ply_list_append_data (backend->input_source.input_devices, input_device);
+
+        if (backend->input_source_is_open)
+                watch_input_device (backend, input_device);
+
+        sync_input_devices (backend);
+}
+
+static void
+remove_input_device (ply_renderer_backend_t *backend,
+                     ply_input_device_t     *input_device)
+{
+        ply_list_remove_data (backend->input_source.input_devices, input_device);
+
+        sync_input_devices (backend);
+}
+
 ply_renderer_plugin_interface_t *
 ply_renderer_backend_get_interface (void)
 {
@@ -1873,8 +2043,9 @@ ply_renderer_backend_get_interface (void)
                 .get_panel_properties         = get_panel_properties,
                 .get_capslock_state           = get_capslock_state,
                 .get_keymap                   = get_keymap,
+                .add_input_device             = add_input_device,
+                .remove_input_device          = remove_input_device,
         };
 
         return &plugin_interface;
 }
-
index 97dabeefc8b5c80a326665d821b24515837b9dee..2ff76b433055a2818f8dea18b9d4147869c2a55e 100644 (file)
@@ -46,6 +46,7 @@
 
 #include "ply-buffer.h"
 #include "ply-event-loop.h"
+#include "ply-input-device.h"
 #include "ply-list.h"
 #include "ply-logger.h"
 #include "ply-rectangle.h"
@@ -71,6 +72,7 @@ struct _ply_renderer_input_source
 {
         ply_renderer_backend_t             *backend;
         ply_fd_watch_t                     *terminal_input_watch;
+        ply_list_t                         *input_devices;
 
         ply_buffer_t                       *key_buffer;
 
@@ -108,6 +110,7 @@ struct _ply_renderer_backend
         unsigned int                row_stride;
 
         uint32_t                    is_active : 1;
+        uint32_t                    input_source_is_open : 1;
 
         void                        (*flush_area) (ply_renderer_backend_t *backend,
                                                    ply_renderer_head_t    *head,
@@ -117,6 +120,7 @@ struct _ply_renderer_backend
 ply_renderer_plugin_interface_t *ply_renderer_backend_get_interface (void);
 static void ply_renderer_head_redraw (ply_renderer_backend_t *backend,
                                       ply_renderer_head_t    *head);
+static bool using_input_device (ply_renderer_input_source_t *backend);
 static bool open_input_source (ply_renderer_backend_t      *backend,
                                ply_renderer_input_source_t *input_source);
 
@@ -260,6 +264,7 @@ create_backend (const char     *device_name,
         backend->head.map_address = MAP_FAILED;
         backend->heads = ply_list_new ();
         backend->input_source.key_buffer = ply_buffer_new ();
+        backend->input_source.input_devices = ply_list_new ();
         backend->terminal = terminal;
 
         return backend;
@@ -298,6 +303,7 @@ destroy_backend (ply_renderer_backend_t *backend)
         ply_trace ("destroying renderer backend for device %s",
                    backend->device_name);
         free (backend->device_name);
+        ply_list_free (backend->input_source.input_devices);
         uninitialize_head (backend, &backend->head);
 
         ply_list_free (backend->heads);
@@ -651,9 +657,18 @@ get_input_source (ply_renderer_backend_t *backend)
 }
 
 static void
-on_key_event (ply_renderer_input_source_t *input_source,
-              int                          terminal_fd)
+on_terminal_key_event (ply_renderer_input_source_t *input_source)
 {
+        ply_renderer_backend_t *backend = input_source->backend;
+        int terminal_fd;
+
+        if (using_input_device (input_source)) {
+                ply_terminal_flush_input (backend->terminal);
+                return;
+        }
+
+        terminal_fd = ply_terminal_get_fd (backend->terminal);
+
         ply_buffer_append_from_fd (input_source->key_buffer,
                                    terminal_fd);
 
@@ -661,6 +676,32 @@ on_key_event (ply_renderer_input_source_t *input_source,
                 input_source->handler (input_source->user_data, input_source->key_buffer, input_source);
 }
 
+static void
+on_input_device_key (ply_renderer_input_source_t *input_source,
+                     ply_input_device_t          *input_device,
+                     const char                  *text)
+{
+        ply_buffer_append_bytes (input_source->key_buffer, text, strlen (text));
+
+        if (input_source->handler != NULL)
+                input_source->handler (input_source->user_data, input_source->key_buffer, input_source);
+}
+
+static void
+on_input_leds_changed (ply_renderer_input_source_t *input_source,
+                       ply_input_device_t          *input_device)
+{
+        ply_xkb_keyboard_state_t *state;
+        ply_list_node_t *node;
+
+        state = ply_input_device_get_state (input_device);
+
+        ply_list_foreach (input_source->input_devices, node) {
+                ply_input_device_t *set_input_device = ply_list_node_get_data (node);
+                ply_input_device_set_state (set_input_device, state);
+        }
+}
+
 static void
 on_input_source_disconnected (ply_renderer_input_source_t *input_source)
 {
@@ -668,6 +709,37 @@ on_input_source_disconnected (ply_renderer_input_source_t *input_source)
         open_input_source (input_source->backend, input_source);
 }
 
+static bool
+using_input_device (ply_renderer_input_source_t *input_source)
+{
+        return ply_list_get_length (input_source->input_devices) > 0;
+}
+
+static void
+watch_input_device (ply_renderer_backend_t *backend,
+                    ply_input_device_t     *input_device)
+{
+        ply_trace ("Listening for keys from device '%s'", ply_input_device_get_name (input_device));
+
+        ply_input_device_watch_for_input (input_device,
+                                          (ply_input_device_input_handler_t) on_input_device_key,
+                                          (ply_input_device_leds_changed_handler_t) on_input_leds_changed,
+                                          &backend->input_source);
+}
+
+static void
+watch_input_devices (ply_renderer_backend_t *backend)
+{
+        ply_list_node_t *node;
+        ply_renderer_input_source_t *input_source = &backend->input_source;
+
+        ply_list_foreach (input_source->input_devices, node) {
+                ply_input_device_t *input_device = ply_list_node_get_data (node);
+
+                watch_input_device (backend, input_device);
+        }
+}
+
 static bool
 open_input_source (ply_renderer_backend_t      *backend,
                    ply_renderer_input_source_t *input_source)
@@ -677,16 +749,24 @@ open_input_source (ply_renderer_backend_t      *backend,
         assert (backend != NULL);
         assert (has_input_source (backend, input_source));
 
+        if (!backend->input_source_is_open)
+                watch_input_devices (backend);
+
         if (backend->terminal == NULL)
                 return false;
 
-        terminal_fd = ply_terminal_get_fd (backend->terminal);
+        if (backend->terminal != NULL) {
+                terminal_fd = ply_terminal_get_fd (backend->terminal);
+
+                input_source->terminal_input_watch = ply_event_loop_watch_fd (backend->loop, terminal_fd, PLY_EVENT_LOOP_FD_STATUS_HAS_DATA,
+                                                                              (ply_event_handler_t) on_terminal_key_event,
+                                                                              (ply_event_handler_t)
+                                                                              on_input_source_disconnected, input_source);
+        }
 
         input_source->backend = backend;
-        input_source->terminal_input_watch = ply_event_loop_watch_fd (backend->loop, terminal_fd, PLY_EVENT_LOOP_FD_STATUS_HAS_DATA,
-                                                                      (ply_event_handler_t) on_key_event,
-                                                                      (ply_event_handler_t) on_input_source_disconnected,
-                                                                      input_source);
+        backend->input_source_is_open = true;
+
         return true;
 }
 
@@ -710,17 +790,54 @@ close_input_source (ply_renderer_backend_t      *backend,
         assert (backend != NULL);
         assert (has_input_source (backend, input_source));
 
-        if (backend->terminal == NULL)
+        if (!backend->input_source_is_open)
                 return;
 
-        ply_event_loop_stop_watching_fd (backend->loop, input_source->terminal_input_watch);
-        input_source->terminal_input_watch = NULL;
+        if (using_input_device (input_source)) {
+                ply_list_node_t *node;
+                ply_list_foreach (input_source->input_devices, node) {
+                        ply_input_device_t *input_device = ply_list_node_get_data (node);
+                        ply_input_device_stop_watching_for_input (input_device,
+                                                                  (ply_input_device_input_handler_t) on_input_device_key,
+                                                                  (ply_input_device_leds_changed_handler_t) on_input_leds_changed,
+                                                                  &backend->input_source);
+                }
+        }
+
+        if (input_source->terminal_input_watch != NULL) {
+                ply_event_loop_stop_watching_fd (backend->loop, input_source->terminal_input_watch);
+                input_source->terminal_input_watch = NULL;
+        }
+
         input_source->backend = NULL;
+
+        backend->input_source_is_open = false;
+}
+
+static ply_input_device_t *
+get_any_input_device_with_leds (ply_renderer_backend_t *backend)
+{
+        ply_list_node_t *node;
+
+        ply_list_foreach (backend->input_source.input_devices, node) {
+                ply_input_device_t *input_device;
+
+                input_device = ply_list_node_get_data (node);
+
+                if (ply_input_device_is_keyboard_with_leds (input_device))
+                        return input_device;
+        }
+
+        return NULL;
 }
 
 static bool
 get_capslock_state (ply_renderer_backend_t *backend)
 {
+        if (using_input_device (&backend->input_source)) {
+                ply_input_device_t *dev = get_any_input_device_with_leds (backend);
+                return ply_input_device_get_capslock_state (dev);
+        }
         if (!backend->terminal)
                 return false;
 
@@ -730,12 +847,64 @@ get_capslock_state (ply_renderer_backend_t *backend)
 static const char *
 get_keymap (ply_renderer_backend_t *backend)
 {
+        if (using_input_device (&backend->input_source)) {
+                ply_input_device_t *dev = get_any_input_device_with_leds (backend);
+                const char *keymap = ply_input_device_get_keymap (dev);
+                if (keymap != NULL) {
+                        return keymap;
+                }
+        }
         if (!backend->terminal)
                 return NULL;
 
         return ply_terminal_get_keymap (backend->terminal);
 }
 
+static void
+sync_input_devices (ply_renderer_backend_t *backend)
+{
+        ply_list_node_t *node;
+        ply_xkb_keyboard_state_t *xkb_state;
+        ply_input_device_t *source_input_device;
+
+        source_input_device = get_any_input_device_with_leds (backend);
+
+        if (source_input_device == NULL)
+                return;
+
+        xkb_state = ply_input_device_get_state (source_input_device);
+
+        ply_list_foreach (backend->input_source.input_devices, node) {
+                ply_input_device_t *target_input_device = ply_list_node_get_data (node);
+
+                if (source_input_device == target_input_device)
+                        continue;
+
+                ply_input_device_set_state (target_input_device, xkb_state);
+        }
+}
+
+static void
+add_input_device (ply_renderer_backend_t *backend,
+                  ply_input_device_t     *input_device)
+{
+        ply_list_append_data (backend->input_source.input_devices, input_device);
+
+        if (backend->input_source_is_open)
+                watch_input_device (backend, input_device);
+
+        sync_input_devices (backend);
+}
+
+static void
+remove_input_device (ply_renderer_backend_t *backend,
+                     ply_input_device_t     *input_device)
+{
+        ply_list_remove_data (backend->input_source.input_devices, input_device);
+
+        sync_input_devices (backend);
+}
+
 ply_renderer_plugin_interface_t *
 ply_renderer_backend_get_interface (void)
 {
@@ -760,8 +929,9 @@ ply_renderer_backend_get_interface (void)
                 .get_device_name              = get_device_name,
                 .get_capslock_state           = get_capslock_state,
                 .get_keymap                   = get_keymap,
+                .add_input_device             = add_input_device,
+                .remove_input_device          = remove_input_device,
         };
 
         return &plugin_interface;
 }
-
index 4f8c459dae4d06bea19bbc844260d559d21d3df7..91a3eec9cad505470fa7c5494b2456099dbde0cd 100644 (file)
Binary files a/themes/spinfinity/keymap-render.png and b/themes/spinfinity/keymap-render.png differ
index 4aaed5abb487524c3a2ac064cbeba1eb6656a89a..91a3eec9cad505470fa7c5494b2456099dbde0cd 100644 (file)
Binary files a/themes/spinner/keymap-render.png and b/themes/spinner/keymap-render.png differ