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')
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',
libply_splash_core_private_deps = [
lm_dep,
+ libevdev_dep,
+ xkbcommon_dep,
+ xkeyboard_config_dep,
]
if libudev_dep.found()
'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',
*/
#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);
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;
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;
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)
{
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;
force_fb = true;
device_path = udev_device_get_devnode (device);
+ device_sysname = udev_device_get_sysname (device);
if (device_path != NULL) {
const char *subsystem;
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) {
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);
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");
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);
}
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)
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),
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)
manager = calloc (1, sizeof(ply_device_manager_t));
manager->loop = NULL;
+ manager->xkb_context = xkb_context_new (XKB_CONTEXT_NO_FLAGS);
+ parse_vconsole_conf (manager);
+
manager->terminals = ply_hashtable_new (ply_hashtable_string_hash, ply_hashtable_string_compare);
manager->renderers = ply_hashtable_new (ply_hashtable_string_hash, ply_hashtable_string_compare);
- manager->local_console_terminal = ply_terminal_new (default_tty);
+
+ manager->local_console_terminal = ply_terminal_new (default_tty, manager->keymap);
ply_terminal_open (manager->local_console_terminal);
+ manager->input_devices = ply_hashtable_new (ply_hashtable_string_hash, ply_hashtable_string_compare);
manager->keyboards = ply_list_new ();
manager->text_displays = ply_list_new ();
manager->pixel_displays = ply_list_new ();
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)
renderer = NULL;
return true;
}
+
+ add_input_devices_to_renderer (manager, renderer);
}
}
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);
#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,
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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
#include <stdint.h>
#include <unistd.h>
+#include "ply-input-device.h"
#include "ply-terminal.h"
#include "ply-event-loop.h"
#include "ply-list.h"
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 */
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)
{
return renderer->plugin_interface->get_keymap (renderer->backend);
}
-
#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;
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);
struct termios original_locked_term_attributes;
char *name;
- char *keymap;
+ const char *keymap;
int fd;
int vt_number;
int initial_vt_number;
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;
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);
free_vt_change_closures (terminal);
free_input_closures (terminal);
- free (terminal->keymap);
free (terminal->name);
free (terminal);
}
} 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);
#endif
#endif /* PLY_TERMINAL_H */
-
{
const char *keymap_with_variant;
ply_renderer_t *renderer;
- char *keymap;
+ char *keymap, *compare_keymap;
int i;
keymap_icon->keymap_offset = -1;
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;
ply_trace ("Error no pre-rendered text for '%s' keymap", keymap);
free (keymap);
+ free (compare_keymap);
}
ply_keymap_icon_t *
{
return keymap_icon->height;
}
-