diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 07a59076..30fbb3b4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -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 diff --git a/meson.build b/meson.build index f5cb0869..bd55c1c8 100644 --- a/meson.build +++ b/meson.build @@ -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') diff --git a/scripts/keymap-render.py b/scripts/keymap-render.py index 53b67dd3..2dc86575 100755 --- a/scripts/keymap-render.py +++ b/scripts/keymap-render.py @@ -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("static struct ply_keymap_metadata ply_keymap_metadata[] = {") +print("typedef enum {") +print(" PLY_LAYOUT_TERMINAL,") +print(" PLY_LAYOUT_XKB,") +print("} ply_layout_types_t;") +print("") +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("};") diff --git a/scripts/plymouth-populate-initrd.in b/scripts/plymouth-populate-initrd.in index a130616f..6ee6b353 100755 --- a/scripts/plymouth-populate-initrd.in +++ b/scripts/plymouth-populate-initrd.in @@ -33,6 +33,10 @@ ddebug() { [ "$verbose" = "true" ] && echo "$@" } +dfatal() { + echo "$@" > /proc/self/fd/2 +} + # normalize_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 } " > $output + echo "usage: plymouth [ --verbose | -v ] { --targetdir | -t } { --x11-directory | -x } " > $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 diff --git a/src/libply-splash-core/meson.build b/src/libply-splash-core/meson.build index 8914e8ec..69636b13 100644 --- a/src/libply-splash-core/meson.build +++ b/src/libply-splash-core/meson.build @@ -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', diff --git a/src/libply-splash-core/ply-device-manager.c b/src/libply-splash-core/ply-device-manager.c index f87466ff..12dce530 100644 --- a/src/libply-splash-core/ply-device-manager.c +++ b/src/libply-splash-core/ply-device-manager.c @@ -19,6 +19,7 @@ */ #include "config.h" #include "ply-device-manager.h" +#include "ply-renderer.h" #include #include @@ -34,14 +35,19 @@ #include #endif +#include + #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 index 00000000..be473fc5 --- /dev/null +++ b/src/libply-splash-core/ply-input-device.c @@ -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 + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 index 00000000..ecc88982 --- /dev/null +++ b/src/libply-splash-core/ply-input-device.h @@ -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 + */ +#ifndef PLY_INPUT_DEVICE_H +#define PLY_INPUT_DEVICE_H + +#include "ply-buffer.h" +#include +#include + +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 diff --git a/src/libply-splash-core/ply-renderer-plugin.h b/src/libply-splash-core/ply-renderer-plugin.h index 6817dac1..483ad27c 100644 --- a/src/libply-splash-core/ply-renderer-plugin.h +++ b/src/libply-splash-core/ply-renderer-plugin.h @@ -26,6 +26,7 @@ #include #include +#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 */ diff --git a/src/libply-splash-core/ply-renderer.c b/src/libply-splash-core/ply-renderer.c index edebc030..75492885 100644 --- a/src/libply-splash-core/ply-renderer.c +++ b/src/libply-splash-core/ply-renderer.c @@ -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); } - diff --git a/src/libply-splash-core/ply-renderer.h b/src/libply-splash-core/ply-renderer.h index 0b4e0323..5fbf819d 100644 --- a/src/libply-splash-core/ply-renderer.h +++ b/src/libply-splash-core/ply-renderer.h @@ -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); diff --git a/src/libply-splash-core/ply-terminal.c b/src/libply-splash-core/ply-terminal.c index 686b4f4f..eee23c74 100644 --- a/src/libply-splash-core/ply-terminal.c +++ b/src/libply-splash-core/ply-terminal.c @@ -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); +} diff --git a/src/libply-splash-core/ply-terminal.h b/src/libply-splash-core/ply-terminal.h index c701f2ca..14d75d13 100644 --- a/src/libply-splash-core/ply-terminal.h +++ b/src/libply-splash-core/ply-terminal.h @@ -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 */ - diff --git a/src/libply-splash-graphics/ply-keymap-icon.c b/src/libply-splash-graphics/ply-keymap-icon.c index 94cf5b34..4455f032 100644 --- a/src/libply-splash-graphics/ply-keymap-icon.c +++ b/src/libply-splash-graphics/ply-keymap-icon.c @@ -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; } - diff --git a/src/libply-splash-graphics/ply-keymap-metadata.h b/src/libply-splash-graphics/ply-keymap-metadata.h index ef715ae4..d8b13f1c 100644 --- a/src/libply-splash-graphics/ply-keymap-metadata.h +++ b/src/libply-splash-graphics/ply-keymap-metadata.h @@ -1,139 +1,256 @@ /* 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; -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 */ +typedef enum +{ + PLY_LAYOUT_TERMINAL, + PLY_LAYOUT_XKB, +} ply_layout_types_t; + +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 */ }; diff --git a/src/libply/ply-buffer.c b/src/libply/ply-buffer.c index 7392ee6b..e91eb0d6 100644 --- a/src/libply/ply-buffer.c +++ b/src/libply/ply-buffer.c @@ -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; } - diff --git a/src/libply/ply-list.h b/src/libply/ply-list.h index 2a3e187b..1dd848f5 100644 --- a/src/libply/ply-list.h +++ b/src/libply/ply-list.h @@ -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 */ diff --git a/src/libply/ply-trigger.c b/src/libply/ply-trigger.c index 620fa9fb..dda73d3d 100644 --- a/src/libply/ply-trigger.c +++ b/src/libply/ply-trigger.c @@ -30,16 +30,29 @@ #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; } diff --git a/src/libply/ply-trigger.h b/src/libply/ply-trigger.h index 162a794c..bb088386 100644 --- a/src/libply/ply-trigger.h +++ b/src/libply/ply-trigger.h @@ -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); diff --git a/src/plugins/renderers/drm/plugin.c b/src/plugins/renderers/drm/plugin.c index fb0c7e8a..56c9f898 100644 --- a/src/plugins/renderers/drm/plugin.c +++ b/src/plugins/renderers/drm/plugin.c @@ -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; } - diff --git a/src/plugins/renderers/frame-buffer/plugin.c b/src/plugins/renderers/frame-buffer/plugin.c index 97dabeef..2ff76b43 100644 --- a/src/plugins/renderers/frame-buffer/plugin.c +++ b/src/plugins/renderers/frame-buffer/plugin.c @@ -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; } - diff --git a/themes/spinfinity/keymap-render.png b/themes/spinfinity/keymap-render.png index 4f8c459d..91a3eec9 100644 Binary files a/themes/spinfinity/keymap-render.png and b/themes/spinfinity/keymap-render.png differ diff --git a/themes/spinner/keymap-render.png b/themes/spinner/keymap-render.png index 4aaed5ab..91a3eec9 100644 Binary files a/themes/spinner/keymap-render.png and b/themes/spinner/keymap-render.png differ