devilhorns pushed a commit to branch master. http://git.enlightenment.org/core/efl.git/commit/?id=3070c4cf08a28b2a9a3f4838ec78ce7f7bde95ec
commit 3070c4cf08a28b2a9a3f4838ec78ce7f7bde95ec Author: Chris Michael <cpmich...@osg.samsung.com> Date: Wed Apr 6 08:46:29 2016 -0400 elput: Add support for creating and removing devices This patch adds start of code to create or remove input devices such as keyboards, pointers, or touch devices. Signed-off-by: Chris Michael <cpmich...@osg.samsung.com> --- src/Makefile_Elput.am | 1 + src/lib/elput/Elput.h | 15 ++ src/lib/elput/elput_evdev.c | 464 ++++++++++++++++++++++++++++++++++++++++++ src/lib/elput/elput_input.c | 100 ++++++++- src/lib/elput/elput_private.h | 139 +++++++++++++ 5 files changed, 715 insertions(+), 4 deletions(-) diff --git a/src/Makefile_Elput.am b/src/Makefile_Elput.am index c79763e..d177f6d 100644 --- a/src/Makefile_Elput.am +++ b/src/Makefile_Elput.am @@ -9,6 +9,7 @@ dist_installed_elputmainheaders_DATA = \ lib/elput/Elput.h lib_elput_libelput_la_SOURCES = \ +lib/elput/elput_evdev.c \ lib/elput/elput_input.c \ lib/elput/elput_logind.c \ lib/elput/elput_manager.c \ diff --git a/src/lib/elput/Elput.h b/src/lib/elput/Elput.h index 234597f..f6ad5d2 100644 --- a/src/lib/elput/Elput.h +++ b/src/lib/elput/Elput.h @@ -28,6 +28,21 @@ /* opaque structure to represent an input manager */ typedef struct _Elput_Manager Elput_Manager; +/* opaque structure to represent an input seat */ +typedef struct _Elput_Seat Elput_Seat; + +/* opaque structure to represent an input device */ +typedef struct _Elput_Device Elput_Device; + +/* opaque structure to represent a keyboard */ +typedef struct _Elput_Keyboard Elput_Keyboard; + +/* opaque structure to represent a mouse */ +typedef struct _Elput_Pointer Elput_Pointer; + +/* opaque structure to represent a touch device */ +typedef struct _Elput_Touch Elput_Touch; + /** * @file * @brief Ecore functions for dealing with libinput diff --git a/src/lib/elput/elput_evdev.c b/src/lib/elput/elput_evdev.c new file mode 100644 index 0000000..4c34738 --- /dev/null +++ b/src/lib/elput/elput_evdev.c @@ -0,0 +1,464 @@ +#include "elput_private.h" + +static void +_seat_caps_update(Elput_Seat *seat) +{ + /* TODO: raise event for seat caps */ +} + +static int +_keyboard_fd_get(off_t size) +{ + int fd = 0, blen = 0, len = 0; + const char *path; + char tmp[PATH_MAX]; + long flags; + + blen = sizeof(tmp) - 1; + + if (!(path = getenv("XDG_RUNTIME_DIR"))) + return -1; + + len = strlen(path); + if (len < blen) + { + strcpy(tmp, path); + strcat(tmp, "/elput-keymap-XXXXXX"); + } + else + return -1; + + if ((fd = mkstemp(tmp)) < 0) return -1; + + flags = fcntl(fd, F_GETFD); + if (flags < 0) + { + close(fd); + return -1; + } + + if (fcntl(fd, F_SETFD, (flags | FD_CLOEXEC)) == -1) + { + close(fd); + return -1; + } + + if (ftruncate(fd, size) < 0) + { + close(fd); + return -1; + } + + unlink(tmp); + return fd; +} + +static Elput_Keyboard_Info * +_keyboard_info_create(struct xkb_keymap *keymap, Eina_Bool external) +{ + Elput_Keyboard_Info *info; + char *str; + + info = calloc(1, sizeof(Elput_Keyboard_Info)); + if (!info) return NULL; + + info->keymap.map = xkb_keymap_ref(keymap); + info->refs = 1; + + info->mods.super = + 1 << xkb_keymap_mod_get_index(info->keymap.map, XKB_MOD_NAME_LOGO); + info->mods.shift = + 1 << xkb_keymap_mod_get_index(info->keymap.map, XKB_MOD_NAME_SHIFT); + info->mods.caps = + 1 << xkb_keymap_mod_get_index(info->keymap.map, XKB_MOD_NAME_CAPS); + info->mods.ctrl = + 1 << xkb_keymap_mod_get_index(info->keymap.map, XKB_MOD_NAME_CTRL); + info->mods.alt = + 1 << xkb_keymap_mod_get_index(info->keymap.map, XKB_MOD_NAME_ALT); + info->mods.altgr = + 1 << xkb_keymap_mod_get_index(info->keymap.map, "ISO_Level3_Shift"); + + /* if we are using an external keymap then we do not need go further */ + if (external) return info; + + str = xkb_keymap_get_as_string(info->keymap.map, XKB_KEYMAP_FORMAT_TEXT_V1); + if (!str) goto err; + + info->keymap.size = strlen(str) + 1; + + info->keymap.fd = _keyboard_fd_get(info->keymap.size); + if (info->keymap.fd < 0) goto err_fd; + + info->keymap.area = + mmap(NULL, info->keymap.size, PROT_READ | PROT_WRITE, + MAP_SHARED, info->keymap.fd, 0); + if (info->keymap.area == MAP_FAILED) goto err_map; + + strcpy(info->keymap.area, str); + free(str); + + return info; + +err_map: + close(info->keymap.fd); +err_fd: + free(str); +err: + xkb_keymap_unref(info->keymap.map); + free(info); + return NULL; +} + +static void +_keyboard_info_destroy(Elput_Keyboard_Info *info, Eina_Bool external) +{ + if (--info->refs > 0) return; + + if (!external) + { + xkb_keymap_unref(info->keymap.map); + if (info->keymap.area) munmap(info->keymap.area, info->keymap.size); + if (info->keymap.fd >= 0) close(info->keymap.fd); + } + + free(info); +} + +static Eina_Bool +_keyboard_global_build(Elput_Keyboard *kbd) +{ + struct xkb_keymap *keymap; + + kbd->context = xkb_context_new(0); + if (!kbd->context) return EINA_FALSE; + + if (!kbd->names.rules) kbd->names.rules = strdup("evdev"); + if (!kbd->names.model) kbd->names.model = strdup("pc105"); + if (!kbd->names.layout) kbd->names.layout = strdup("us"); + + keymap = xkb_keymap_new_from_names(kbd->context, &kbd->names, 0); + if (!keymap) return EINA_FALSE; + + kbd->info = _keyboard_info_create(keymap, EINA_FALSE); + xkb_keymap_unref(keymap); + + if (!kbd->info) return EINA_FALSE; + return EINA_TRUE; +} + +static Elput_Keyboard * +_keyboard_create(Elput_Seat *seat) +{ + Elput_Keyboard *kbd; + + kbd = calloc(1, sizeof(Elput_Keyboard)); + if (!kbd) return NULL; + + kbd->seat = seat; + return kbd; +} + +static Eina_Bool +_keyboard_init(Elput_Seat *seat, struct xkb_keymap *keymap) +{ + Elput_Keyboard *kbd; + + if (seat->kbd) + { + seat->count.kbd += 1; + if (seat->count.kbd == 1) + { + _seat_caps_update(seat); + return EINA_TRUE; + } + } + + kbd = _keyboard_create(seat); + if (!kbd) return EINA_FALSE; + + if (keymap) + { + kbd->info = _keyboard_info_create(keymap, EINA_TRUE); + if (!kbd->info) goto err; + } + else + { + if (!_keyboard_global_build(kbd)) goto err; + kbd->info->refs++; + } + + kbd->state = xkb_state_new(kbd->info->keymap.map); + if (!kbd->state) goto err; + + seat->kbd = kbd; + seat->count.kbd = 1; + + _seat_caps_update(seat); + + return EINA_TRUE; + +err: + if (kbd->info) _keyboard_info_destroy(kbd->info, kbd->external_map); + free(kbd); + return EINA_FALSE; +} + +static void +_keyboard_state_reset(Elput_Keyboard *kbd) +{ + struct xkb_state *state; + + state = xkb_state_new(kbd->info->keymap.map); + if (!state) return; + + xkb_state_unref(kbd->state); + kbd->state = state; +} + +static void +_keyboard_release(Elput_Seat *seat) +{ + seat->count.kbd--; + if (seat->count.kbd == 0) + { + _keyboard_state_reset(seat->kbd); + _seat_caps_update(seat); + } +} + +static Elput_Pointer * +_pointer_create(Elput_Seat *seat) +{ + Elput_Pointer *ptr; + + ptr = calloc(1, sizeof(Elput_Pointer)); + if (!ptr) return NULL; + + ptr->seat = seat; + + return ptr; +} + +static Eina_Bool +_pointer_init(Elput_Seat *seat) +{ + Elput_Pointer *ptr; + + if (seat->ptr) + { + seat->count.ptr += 1; + if (seat->count.ptr == 1) + { + _seat_caps_update(seat); + return EINA_TRUE; + } + } + + ptr = _pointer_create(seat); + if (!ptr) return EINA_FALSE; + + seat->ptr = ptr; + seat->count.ptr = 1; + + _seat_caps_update(seat); + + return EINA_TRUE; +} + +static void +_pointer_release(Elput_Seat *seat) +{ + seat->count.ptr--; + if (seat->count.ptr == 0) + { + seat->ptr->buttons = 0; + _seat_caps_update(seat); + } +} + +static Elput_Touch * +_touch_create(Elput_Seat *seat) +{ + Elput_Touch *touch; + + touch = calloc(1, sizeof(Elput_Touch)); + if (!touch) return NULL; + + touch->seat = seat; + + return touch; +} + +static Eina_Bool +_touch_init(Elput_Seat *seat) +{ + Elput_Touch *touch; + + if (seat->touch) + { + seat->count.touch += 1; + if (seat->count.touch == 1) + { + _seat_caps_update(seat); + return EINA_TRUE; + } + } + + touch = _touch_create(seat); + if (!touch) return EINA_FALSE; + + seat->touch = touch; + seat->count.touch = 1; + + _seat_caps_update(seat); + + return EINA_TRUE; +} + +static void +_touch_release(Elput_Seat *seat) +{ + seat->count.touch--; + if (seat->count.touch == 0) + { + seat->touch->points = 0; + _seat_caps_update(seat); + } +} + +int +_evdev_event_process(struct libinput_event *event) +{ + struct libinput_device *idev; + int ret = 1; + Eina_Bool frame = EINA_FALSE; + + idev = libinput_event_get_device(event); + switch (libinput_event_get_type(event)) + { + case LIBINPUT_EVENT_KEYBOARD_KEY: + break; + case LIBINPUT_EVENT_POINTER_MOTION: + break; + case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: + break; + case LIBINPUT_EVENT_POINTER_BUTTON: + break; + case LIBINPUT_EVENT_POINTER_AXIS: + break; + case LIBINPUT_EVENT_TOUCH_DOWN: + break; + case LIBINPUT_EVENT_TOUCH_MOTION: + break; + case LIBINPUT_EVENT_TOUCH_UP: + break; + case LIBINPUT_EVENT_TOUCH_FRAME: + default: + ret = 0; + break; + } + + if (frame) + { + + } + + return ret; +} + +Elput_Device * +_evdev_device_create(Elput_Seat *seat, struct libinput_device *device) +{ + Elput_Device *edev; + + edev = calloc(1, sizeof(Elput_Device)); + if (!edev) return NULL; + + edev->seat = seat; + edev->device = device; + + if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_KEYBOARD)) + { + _keyboard_init(seat, NULL); + edev->caps |= EVDEV_SEAT_KEYBOARD; + } + + if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER)) + { + _pointer_init(seat); + edev->caps |= EVDEV_SEAT_POINTER; + } + + if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TOUCH)) + { + _touch_init(seat); + edev->caps |= EVDEV_SEAT_TOUCH; + } + + libinput_device_set_user_data(device, edev); + libinput_device_ref(edev->device); + + if (libinput_device_config_tap_get_finger_count(edev->device) > 0) + { + Eina_Bool enable = EINA_FALSE; + + enable = libinput_device_config_tap_get_default_enabled(edev->device); + libinput_device_config_tap_set_enabled(edev->device, enable); + } + + /* FIXME: Normally we would do a device calibration set here however + * that requires Output support. Since this is just an input library, we + * may need to add external facing APIs to do calibration. Then a user of + * elput would handle outputs, and make calls to calibrate */ + + return edev; +} + +void +_evdev_device_destroy(Elput_Device *edev) +{ + if (!edev) return; + + if (edev->caps & EVDEV_SEAT_POINTER) + _pointer_release(edev->seat); + if (edev->caps & EVDEV_SEAT_KEYBOARD) + _keyboard_release(edev->seat); + if (edev->caps & EVDEV_SEAT_TOUCH) + _touch_release(edev->seat); + + libinput_device_unref(edev->device); + eina_stringshare_del(edev->output_name); + free(edev); +} + +void +_evdev_keyboard_destroy(Elput_Keyboard *kbd) +{ + free((char *)kbd->names.rules); + free((char *)kbd->names.model); + free((char *)kbd->names.layout); + free((char *)kbd->names.variant); + free((char *)kbd->names.options); + + if (kbd->state) xkb_state_unref(kbd->state); + if (kbd->info) _keyboard_info_destroy(kbd->info, kbd->external_map); + + xkb_context_unref(kbd->context); + xkb_keymap_unref(kbd->pending_map); + + free(kbd); +} + +void +_evdev_pointer_destroy(Elput_Pointer *ptr) +{ + /* FIXME: destroy any resources inside pointer structure */ + free(ptr); +} + +void +_evdev_touch_destroy(Elput_Touch *touch) +{ + /* FIXME: destroy any resources inside touch structure */ + free(touch); +} diff --git a/src/lib/elput/elput_input.c b/src/lib/elput/elput_input.c index c001e52..499efcf 100644 --- a/src/lib/elput/elput_input.c +++ b/src/lib/elput/elput_input.c @@ -24,6 +24,95 @@ const struct libinput_interface _input_interface = _cb_close_restricted, }; +static Elput_Seat * +_udev_seat_create(Elput_Manager *em, const char *name) +{ + Elput_Seat *eseat; + + eseat = calloc(1, sizeof(Elput_Seat)); + if (!eseat) return NULL; + + eseat->name = eina_stringshare_add(name); + em->input.seats = eina_list_append(em->input.seats, eseat); + + return eseat; +} + +static void +_udev_seat_destroy(Elput_Seat *eseat) +{ + Elput_Device *edev; + + EINA_LIST_FREE(eseat->devices, edev) + _evdev_device_destroy(edev); + + if (eseat->kbd) _evdev_keyboard_destroy(eseat->kbd); + if (eseat->ptr) _evdev_pointer_destroy(eseat->ptr); + if (eseat->touch) _evdev_touch_destroy(eseat->touch); + + eina_stringshare_del(eseat->name); + free(eseat); +} + +static Elput_Seat * +_udev_seat_named_get(Elput_Manager *em, const char *name) +{ + Elput_Seat *eseat; + Eina_List *l; + + EINA_LIST_FOREACH(em->input.seats, l, eseat) + if (!strcmp(eseat->name, name)) return eseat; + + return _udev_seat_create(em, name); +} + +static Elput_Seat * +_udev_seat_get(Elput_Manager *em, struct libinput_device *device) +{ + struct libinput_seat *lseat; + const char *name; + + lseat = libinput_device_get_seat(device); + name = libinput_seat_get_logical_name(lseat); + + return _udev_seat_named_get(em, name); +} + +static void +_device_add(Elput_Manager *em, struct libinput_device *dev) +{ + Elput_Seat *eseat; + Elput_Device *edev; + const char *oname; + + eseat = _udev_seat_get(em, dev); + if (!eseat) return; + + edev = _evdev_device_create(eseat, dev); + if (!edev) return; + + oname = libinput_device_get_output_name(dev); + eina_stringshare_replace(&edev->output_name, oname); + + eseat->devices = eina_list_append(eseat->devices, edev); +} + +static void +_device_remove(Elput_Manager *em, struct libinput_device *device) +{ + Elput_Seat *eseat; + Elput_Device *edev; + + edev = libinput_device_get_user_data(device); + if (!edev) return; + + eseat = _udev_seat_get(em, device); + if (eseat) + eseat->devices = eina_list_remove(eseat->devices, edev); + + _evdev_device_destroy(edev); +} + static int _udev_process_event(struct libinput_event *event) { @@ -40,11 +129,11 @@ _udev_process_event(struct libinput_event *event) { case LIBINPUT_EVENT_DEVICE_ADDED: DBG("Input Device Added: %s", libinput_device_get_name(dev)); - /* TODO: add device */ + _device_add(em, dev); break; case LIBINPUT_EVENT_DEVICE_REMOVED: DBG("Input Device Removed: %s", libinput_device_get_name(dev)); - /* TODO: remove device */ + _device_remove(em, dev); break; default: ret = 0; @@ -58,7 +147,7 @@ static void _process_event(struct libinput_event *event) { if (_udev_process_event(event)) return; - /* TODO: evdev processing */ + if (_evdev_event_process(event)) return; } static void @@ -136,12 +225,15 @@ udev_err: EAPI void elput_input_shutdown(Elput_Manager *manager) { + Elput_Seat *seat; + EINA_SAFETY_ON_NULL_RETURN(manager); EINA_SAFETY_ON_NULL_RETURN(&manager->input); if (manager->input.hdlr) ecore_main_fd_handler_del(manager->input.hdlr); - /* TODO */ + EINA_LIST_FREE(manager->input.seats, seat) + _udev_seat_destroy(seat); libinput_unref(manager->input.lib); } diff --git a/src/lib/elput/elput_private.h b/src/lib/elput/elput_private.h index 6d88943..32ab797 100644 --- a/src/lib/elput/elput_private.h +++ b/src/lib/elput/elput_private.h @@ -12,6 +12,7 @@ # include "Eldbus.h" # include <Elput.h> +# include <sys/mman.h> # include <fcntl.h> # include <unistd.h> # include <linux/vt.h> @@ -19,6 +20,7 @@ # include <linux/major.h> # include <linux/input.h> # include <libinput.h> +# include <xkbcommon/xkbcommon.h> # ifdef HAVE_SYSTEMD # include <systemd/sd-login.h> @@ -56,6 +58,13 @@ extern int _elput_log_dom; # endif # define CRIT(...) EINA_LOG_DOM_CRIT(_elput_log_dom, __VA_ARGS__) +typedef enum _Elput_Device_Capability +{ + EVDEV_SEAT_POINTER = (1 << 0), + EVDEV_SEAT_KEYBOARD = (1 << 1), + EVDEV_SEAT_TOUCH = (1 << 2) +} Elput_Device_Capability; + typedef struct _Elput_Interface { Eina_Bool (*connect)(Elput_Manager **manager, const char *seat, unsigned int tty, Eina_Bool sync); @@ -75,6 +84,129 @@ typedef struct _Elput_Input Eina_Bool suspended : 1; } Elput_Input; +typedef struct _Elput_Keyboard_Info +{ + int refs; + + struct + { + int fd; + size_t size; + char *area; + struct xkb_keymap *map; + } keymap; + + struct + { + xkb_mod_index_t shift; + xkb_mod_index_t caps; + xkb_mod_index_t ctrl; + xkb_mod_index_t alt; + xkb_mod_index_t altgr; + xkb_mod_index_t super; + } mods; +} Elput_Keyboard_Info; + +struct _Elput_Keyboard +{ + struct + { + unsigned int depressed; + unsigned int latched; + unsigned int locked; + unsigned int group; + } mods; + + struct + { + unsigned int key; + unsigned int timestamp; + } grab; + + Elput_Keyboard_Info *info; + + struct xkb_state *state; + struct xkb_keymap *pending_map; + struct xkb_context *context; + struct xkb_rule_names names; + + Elput_Seat *seat; + + Eina_Bool external_map : 1; +}; + +struct _Elput_Pointer +{ + double x, y; + int buttons; + unsigned int timestamp; + + int minx, miny; + int maxx, maxy; + int hotx, hoty; + + struct + { + double x, y; + unsigned int button; + unsigned int timestamp; + } grab; + + struct + { + unsigned int threshold; + unsigned int last_button, prev_button; + unsigned int last_time, prev_time; + Eina_Bool double_click : 1; + Eina_Bool triple_click : 1; + } mouse; + + Elput_Seat *seat; +}; + +struct _Elput_Touch +{ + double x, y; + int slot; + unsigned int points; + + struct + { + int id; + double x, y; + unsigned int timestamp; + } grab; + + Elput_Seat *seat; +}; + +struct _Elput_Seat +{ + const char *name; + + struct + { + int kbd, ptr, touch; + } count; + + Elput_Keyboard *kbd; + Elput_Pointer *ptr; + Elput_Touch *touch; + + Eina_List *devices; +}; + +struct _Elput_Device +{ + Elput_Seat *seat; + + const char *path; + const char *output_name; + struct libinput_device *device; + + Elput_Device_Capability caps; +}; + struct _Elput_Manager { Elput_Interface *interface; @@ -96,6 +228,13 @@ struct _Elput_Manager Eina_Bool sync : 1; }; +int _evdev_event_process(struct libinput_event *event); +Elput_Device *_evdev_device_create(Elput_Seat *seat, struct libinput_device *device); +void _evdev_device_destroy(Elput_Device *edev); +void _evdev_keyboard_destroy(Elput_Keyboard *kbd); +void _evdev_pointer_destroy(Elput_Pointer *ptr); +void _evdev_touch_destroy(Elput_Touch *touch); + extern Elput_Interface _logind_interface; #endif --