/* this file contains code copied from weston; the copyright notice is below */ /* * Copyright © 2013 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "elput_private.h" #include static int _cb_open_restricted(const char *path, int flags, void *data) { Elput_Manager *em = data; int ret = -1; Elput_Async_Open *ao; int p[2]; if (!em->input.thread) return em->interface->open(em, path, flags); if (!em->interface->open_async) return ret; if (ecore_thread_check(em->input.thread)) return ret; ao = calloc(1, sizeof(Elput_Async_Open)); if (!ao) return ret; if (pipe2(p, O_CLOEXEC) < 0) { free(ao); return ret; } ao->manager = em; ao->path = strdup(path); ao->flags = flags; em->input.pipe = p[1]; ecore_thread_feedback(em->input.thread, ao); while (!ecore_thread_check(em->input.thread)) { int avail, fd; fd_set rfds, wfds, exfds; struct timeval tv, *t; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&exfds); FD_SET(p[0], &rfds); tv.tv_sec = 0; tv.tv_usec = 300; t = &tv; avail = select(p[0] + 1, &rfds, &wfds, &exfds, t); if (avail > 0) { if (read(p[0], &fd, sizeof(int)) < 1) ret = -1; else ret = fd; break; } if (avail < 0) break; } close(p[0]); return ret; } static void _cb_close_restricted(int fd, void *data) { Elput_Manager *em; em = data; elput_manager_close(em, fd); } const struct libinput_interface _input_interface = { _cb_open_restricted, _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->manager = em; eseat->refs = 1; eseat->name = eina_stringshare_add(name); em->input.seats = eina_list_append(em->input.seats, eseat); return eseat; } void _udev_seat_destroy(Elput_Seat *eseat) { Elput_Device *edev; eseat->refs--; if (eseat->refs) return; EINA_LIST_FREE(eseat->devices, edev) _evdev_device_destroy(edev); if (eseat->kbd) _evdev_keyboard_destroy(eseat->kbd); eseat->kbd = NULL; if (eseat->ptr) _evdev_pointer_destroy(eseat->ptr); eseat->ptr = NULL; if (eseat->touch) _evdev_touch_destroy(eseat->touch); eseat->touch = NULL; if (eseat->manager->input.seats) eseat->manager->input.seats = eina_list_remove(eseat->manager->input.seats, eseat); if (eseat->refs) return; 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; if (!name) name = "seat0"; EINA_LIST_FOREACH(em->input.seats, l, eseat) if (!strcmp(eseat->name, name)) return eseat; eseat = _udev_seat_create(em, name); if (!eseat) return NULL; return eseat; } 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_physical_name(lseat); return _udev_seat_named_get(em, name); } static void _device_event_cb_free(void *data EINA_UNUSED, void *event) { Elput_Event_Device_Change *ev; ev = event; ev->device->refs--; if (ev->type == ELPUT_DEVICE_REMOVED) { Elput_Seat *seat; seat = ev->device->seat; if (seat) seat->devices = eina_list_remove(seat->devices, ev->device); _evdev_device_destroy(ev->device); } free(ev); } static void _device_event_send(Elput_Device *edev, Elput_Device_Change_Type type) { Elput_Event_Device_Change *ev; ev = calloc(1, sizeof(Elput_Event_Device_Change)); if (!ev) return; ev->device = edev; ev->type = type; edev->refs++; ecore_event_add(ELPUT_EVENT_DEVICE_CHANGE, ev, _device_event_cb_free, NULL); } static void _device_add(Elput_Manager *em, struct libinput_device *dev) { Elput_Seat *eseat; Elput_Device *edev; eseat = _udev_seat_get(em, dev); if (!eseat) return; edev = _evdev_device_create(eseat, dev); if (!edev) return; eseat->devices = eina_list_append(eseat->devices, edev); DBG("Input Device Added: %s", libinput_device_get_name(dev)); if (edev->caps & ELPUT_DEVICE_CAPS_KEYBOARD) DBG("\tDevice added as Keyboard device"); if (edev->caps & ELPUT_DEVICE_CAPS_POINTER) { DBG("\tDevice added as Pointer device"); switch (em->input.rotation) { case 0: edev->swap = EINA_FALSE; edev->invert_x = EINA_FALSE; edev->invert_y = EINA_FALSE; break; case 90: edev->swap = EINA_TRUE; edev->invert_x = EINA_FALSE; edev->invert_y = EINA_TRUE; break; case 180: edev->swap = EINA_FALSE; edev->invert_x = EINA_TRUE; edev->invert_y = EINA_TRUE; break; case 270: edev->swap = EINA_TRUE; edev->invert_x = EINA_TRUE; edev->invert_y = EINA_FALSE; break; default: break; } } if (edev->caps & ELPUT_DEVICE_CAPS_TOUCH) { DBG("\tDevice added as Touch device"); } _device_event_send(edev, ELPUT_DEVICE_ADDED); } static void _device_remove(Elput_Manager *em EINA_UNUSED, struct libinput_device *device) { Elput_Device *edev; edev = libinput_device_get_user_data(device); if (!edev) return; DBG("Input Device Removed: %s", libinput_device_get_name(device)); _device_event_send(edev, ELPUT_DEVICE_REMOVED); } static int _udev_process_event(struct libinput_event *event) { Elput_Manager *em; struct libinput *lib; struct libinput_device *dev; int ret = 1; lib = libinput_event_get_context(event); dev = libinput_event_get_device(event); em = libinput_get_user_data(lib); switch (libinput_event_get_type(event)) { case LIBINPUT_EVENT_DEVICE_ADDED: _device_add(em, dev); break; case LIBINPUT_EVENT_DEVICE_REMOVED: _device_remove(em, dev); break; default: ret = 0; break; } return ret; } static void _process_event(Elput_Manager *em, struct libinput_event *event) { if (_udev_process_event(event)) return; if (!em->only_gesture_events) { if (_evdev_event_process(event)) return; } if (_gesture_event_process(event)) return; } static void _process_events(Elput_Manager *em) { struct libinput_event *event; Elput_Input *ei = &em->input; while ((ei->lib) && (event = libinput_get_event(ei->lib))) { _process_event(em, event); libinput_event_destroy(event); } } static Eina_Bool _cb_input_dispatch(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED) { Elput_Manager *em; em = data; if ((em->input.lib) && (libinput_dispatch(em->input.lib) != 0)) WRN("libinput failed to dispatch events"); _process_events(em); return EINA_TRUE; } static void _elput_input_init_cancel(void *data, Ecore_Thread *eth EINA_UNUSED) { Elput_Manager *manager = data; manager->input.thread = NULL; if (manager->input.current_pending) { eldbus_pending_cancel(manager->input.current_pending); if (manager->input.pipe >= 0) close(manager->input.pipe); } if (manager->del) elput_manager_disconnect(manager); } static void _elput_input_init_end(void *data, Ecore_Thread *eth EINA_UNUSED) { Elput_Manager *manager = data; manager->input.thread = NULL; if (!manager->input.lib) return; manager->input.hdlr = ecore_main_fd_handler_add(libinput_get_fd(manager->input.lib), ECORE_FD_READ, _cb_input_dispatch, manager, NULL, NULL); if (manager->input.hdlr) _process_events(manager); else { ERR("Could not create input fd handler"); libinput_unref(manager->input.lib); manager->input.lib = NULL; } if ((manager->pending_ptr_x) || (manager->pending_ptr_y)) { elput_input_pointer_xy_set(manager, NULL, manager->pending_ptr_x, manager->pending_ptr_y); manager->pending_ptr_x = 0; manager->pending_ptr_y = 0; } } static void _elput_input_init_notify(void *data EINA_UNUSED, Ecore_Thread *eth EINA_UNUSED, void *msg_data) { Elput_Async_Open *ao = msg_data; ao->manager->interface->open_async(ao->manager, ao->path, ao->flags); free(ao->path); free(ao); } static void _elput_input_init_thread(void *data, Ecore_Thread *eth EINA_UNUSED) { Elput_Manager *manager = data; struct udev *udev; udev = udev_new(); manager->input.lib = libinput_udev_create_context(&_input_interface, manager, udev); if (!manager->input.lib) { ERR("libinput could not create udev context"); return; } udev_unref(udev); if (libinput_udev_assign_seat(manager->input.lib, manager->seat)) { ERR("libinput could not assign udev seat"); libinput_unref(manager->input.lib); manager->input.lib = NULL; } } void _elput_input_enable(Elput_Manager *manager) { if (!manager->input.hdlr) { manager->input.hdlr = ecore_main_fd_handler_add(libinput_get_fd(manager->input.lib), ECORE_FD_READ, _cb_input_dispatch, &manager->input, NULL, NULL); } if (manager->input.suspended) { if (libinput_resume(manager->input.lib) != 0) return; manager->input.suspended = EINA_FALSE; _process_events(manager); } } void _elput_input_disable(Elput_Manager *manager) { Elput_Seat *seat; Eina_List *l; EINA_LIST_FOREACH(manager->input.seats, l, seat) seat->pending_motion = 1; if (manager->input.lib) libinput_suspend(manager->input.lib); _process_events(manager); manager->input.suspended = EINA_TRUE; } EAPI Eina_Bool elput_input_init(Elput_Manager *manager) { EINA_SAFETY_ON_NULL_RETURN_VAL(manager, EINA_FALSE); EINA_SAFETY_ON_TRUE_RETURN_VAL(!!manager->input.hdlr, EINA_TRUE); memset(&manager->input, 0, sizeof(Elput_Input)); manager->input.thread = ecore_thread_feedback_run(_elput_input_init_thread, _elput_input_init_notify, _elput_input_init_end, _elput_input_init_cancel, manager, 1); return !!manager->input.thread; } EAPI void elput_input_shutdown(Elput_Manager *manager) { Elput_Seat *seat; Eina_List *l, *ll; EINA_SAFETY_ON_NULL_RETURN(manager); ecore_main_fd_handler_del(manager->input.hdlr); EINA_LIST_FOREACH_SAFE(manager->input.seats, l, ll, seat) _udev_seat_destroy(seat); if (manager->input.thread) ecore_thread_cancel(manager->input.thread); else { libinput_unref(manager->input.lib); manager->input.lib = NULL; } } EAPI void elput_input_pointer_xy_get(Elput_Manager *manager, const char *seat, int *x, int *y) { Elput_Seat *eseat; Eina_List *l; if (x) *x = 0; if (y) *y = 0; EINA_SAFETY_ON_NULL_RETURN(manager); /* if no seat name is passed in, just use default seat name */ if (!seat) seat = "seat0"; EINA_LIST_FOREACH(manager->input.seats, l, eseat) { if (!eina_streq(eseat->name, seat)) continue; if (x) *x = eseat->pointer.x; if (y) *y = eseat->pointer.y; return; } } EAPI void elput_input_pointer_xy_set(Elput_Manager *manager, const char *seat, int x, int y) { Elput_Seat *eseat; Elput_Device *edev; Eina_List *l, *ll; EINA_SAFETY_ON_NULL_RETURN(manager); /* if no seat name is passed in, just use default seat name */ if (!seat) seat = "seat0"; if (eina_list_count(manager->input.seats) < 1) { manager->pending_ptr_x = x; manager->pending_ptr_y = y; return; } EINA_LIST_FOREACH(manager->input.seats, l, eseat) { if (!eseat->ptr) continue; if ((eseat->name) && (strcmp(eseat->name, seat))) continue; eseat->pointer.x = x; eseat->pointer.y = y; eseat->ptr->timestamp = ecore_loop_time_get(); EINA_LIST_FOREACH(eseat->devices, ll, edev) { if (!libinput_device_has_capability(edev->device, LIBINPUT_DEVICE_CAP_POINTER)) continue; _evdev_pointer_motion_send(edev); break; } } } EAPI Eina_Bool elput_input_pointer_left_handed_set(Elput_Manager *manager, const char *seat, Eina_Bool left) { Elput_Seat *eseat; Elput_Device *edev; Eina_List *l, *ll; EINA_SAFETY_ON_NULL_RETURN_VAL(manager, EINA_FALSE); /* if no seat name is passed in, just use default seat name */ if (!seat) seat = "seat0"; EINA_LIST_FOREACH(manager->input.seats, l, eseat) { if ((eseat->name) && (strcmp(eseat->name, seat))) continue; EINA_LIST_FOREACH(eseat->devices, ll, edev) { if (!libinput_device_has_capability(edev->device, LIBINPUT_DEVICE_CAP_POINTER)) continue; if (edev->left_handed == left) continue; if (libinput_device_config_left_handed_set(edev->device, (int)left) != LIBINPUT_CONFIG_STATUS_SUCCESS) { WRN("Failed to set left handed mode for device: %s", libinput_device_get_name(edev->device)); continue; } else edev->left_handed = !!left; } } return EINA_TRUE; } EAPI void elput_input_pointer_max_set(Elput_Manager *manager, int maxw, int maxh) { EINA_SAFETY_ON_NULL_RETURN(manager); manager->input.pointer_w = maxw; manager->input.pointer_h = maxh; } EAPI Eina_Bool elput_input_pointer_rotation_set(Elput_Manager *manager, int rotation) { Elput_Seat *eseat; Elput_Device *edev; Eina_List *l, *ll; EINA_SAFETY_ON_NULL_RETURN_VAL(manager, EINA_FALSE); if ((rotation % 90 != 0) || (rotation / 90 > 3) || (rotation < 0)) return EINA_FALSE; manager->input.rotation = rotation; EINA_LIST_FOREACH(manager->input.seats, l, eseat) { EINA_LIST_FOREACH(eseat->devices, ll, edev) { if (!(edev->caps & ELPUT_DEVICE_CAPS_POINTER)) continue; switch (rotation) { case 0: edev->swap = EINA_FALSE; edev->invert_x = EINA_FALSE; edev->invert_y = EINA_FALSE; break; case 90: edev->swap = EINA_TRUE; edev->invert_x = EINA_FALSE; edev->invert_y = EINA_TRUE; break; case 180: edev->swap = EINA_FALSE; edev->invert_x = EINA_TRUE; edev->invert_y = EINA_TRUE; break; case 270: edev->swap = EINA_TRUE; edev->invert_x = EINA_TRUE; edev->invert_y = EINA_FALSE; break; default: break; } } } return EINA_TRUE; } EAPI void elput_input_devices_calibrate(Elput_Manager *manager, int w, int h) { Elput_Seat *eseat; Elput_Device *edev; Eina_List *l, *ll; EINA_SAFETY_ON_NULL_RETURN(manager); manager->output_w = w; manager->output_h = h; EINA_LIST_FOREACH(manager->input.seats, l, eseat) { EINA_LIST_FOREACH(eseat->devices, ll, edev) { edev->ow = w; edev->oh = h; _evdev_device_calibrate(edev); } } } EAPI Eina_Bool elput_input_key_remap_enable(Elput_Manager *manager, Eina_Bool enable) { Elput_Seat *eseat; Elput_Device *edev; Eina_List *l, *ll; EINA_SAFETY_ON_NULL_RETURN_VAL(manager, EINA_FALSE); EINA_LIST_FOREACH(manager->input.seats, l, eseat) { EINA_LIST_FOREACH(eseat->devices, ll, edev) { if (!(edev->caps & ELPUT_DEVICE_CAPS_KEYBOARD)) continue; edev->key_remap = enable; if ((!enable) && (edev->key_remap_hash)) { eina_hash_free(edev->key_remap_hash); edev->key_remap_hash = NULL; } } } return EINA_TRUE; } EAPI Eina_Bool elput_input_key_remap_set(Elput_Manager *manager, int *from_keys, int *to_keys, int num) { Elput_Seat *eseat; Elput_Device *edev; Eina_List *l, *ll; int i = 0; EINA_SAFETY_ON_NULL_RETURN_VAL(manager, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(from_keys, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(to_keys, EINA_FALSE); EINA_SAFETY_ON_TRUE_RETURN_VAL((num <= 0), EINA_FALSE); EINA_LIST_FOREACH(manager->input.seats, l, eseat) { EINA_LIST_FOREACH(eseat->devices, ll, edev) { if (!(edev->caps & ELPUT_DEVICE_CAPS_KEYBOARD)) continue; if (!edev->key_remap) continue; if (!edev->key_remap_hash) edev->key_remap_hash = eina_hash_int32_new(NULL); if (!edev->key_remap_hash) continue; for (i = 0; i < num; i++) { if ((!from_keys[i]) || (!to_keys[i])) continue; } for (i = 0; i < num; i++) eina_hash_add(edev->key_remap_hash, &from_keys[i], (void *)(intptr_t)to_keys[i]); } } return EINA_TRUE; } EAPI void elput_input_keyboard_info_set(Elput_Manager *manager, void *context, void *keymap, int group) { Eina_List *l; Elput_Seat *seat; EINA_SAFETY_ON_NULL_RETURN(manager); EINA_SAFETY_ON_FALSE_RETURN((!!context) == (!!keymap)); if ((manager->cached.context == context) && (manager->cached.keymap == keymap)) return; if (context) xkb_context_ref(context); if (keymap) xkb_keymap_ref(keymap); if (manager->cached.context) xkb_context_unref(manager->cached.context); if (manager->cached.keymap) xkb_keymap_unref(manager->cached.keymap); manager->cached.context = context; manager->cached.keymap = keymap; manager->cached.group = group; EINA_LIST_FOREACH(manager->input.seats, l, seat) _keyboard_keymap_update(seat); } EAPI void elput_input_keyboard_group_set(Elput_Manager *manager, int group) { Eina_List *l; Elput_Seat *seat; EINA_SAFETY_ON_NULL_RETURN(manager); if (manager->cached.group == group) return; manager->cached.group = group; EINA_LIST_FOREACH(manager->input.seats, l, seat) _keyboard_group_update(seat); } EAPI void elput_input_pointer_accel_profile_set(Elput_Manager *manager, const char *seat, uint32_t profile) { Elput_Seat *eseat; Elput_Device *edev; Eina_List *l, *ll; EINA_SAFETY_ON_NULL_RETURN(manager); /* if no seat name is passed in, just use default seat name */ if (!seat) seat = "seat0"; EINA_LIST_FOREACH(manager->input.seats, l, eseat) { if ((eseat->name) && (strcmp(eseat->name, seat))) continue; EINA_LIST_FOREACH(eseat->devices, ll, edev) { if (!libinput_device_has_capability(edev->device, LIBINPUT_DEVICE_CAP_POINTER)) continue; if (libinput_device_config_accel_set_profile(edev->device, profile) != LIBINPUT_CONFIG_STATUS_SUCCESS) { WRN("Failed to set acceleration profile for device: %s", libinput_device_get_name(edev->device)); continue; } } } } EAPI void elput_input_pointer_accel_speed_set(Elput_Manager *manager, const char *seat, double speed) { Elput_Seat *eseat; Elput_Device *edev; Eina_List *l, *ll; EINA_SAFETY_ON_NULL_RETURN(manager); /* if no seat name is passed in, just use default seat name */ if (!seat) seat = "seat0"; EINA_LIST_FOREACH(manager->input.seats, l, eseat) { if ((eseat->name) && (strcmp(eseat->name, seat))) continue; EINA_LIST_FOREACH(eseat->devices, ll, edev) { if (!libinput_device_has_capability(edev->device, LIBINPUT_DEVICE_CAP_POINTER)) continue; if (!libinput_device_config_accel_is_available(edev->device)) continue; if (libinput_device_config_accel_set_speed(edev->device, speed) != LIBINPUT_CONFIG_STATUS_SUCCESS) { WRN("Failed to set acceleration speed for device: %s", libinput_device_get_name(edev->device)); continue; } } } } EAPI void elput_input_touch_tap_to_click_enabled_set(Elput_Manager *manager, const char *seat, Eina_Bool enabled) { Elput_Seat *eseat; Elput_Device *edev; Eina_List *l, *ll; enum libinput_config_tap_state state; EINA_SAFETY_ON_NULL_RETURN(manager); state = enabled ? LIBINPUT_CONFIG_TAP_ENABLED : LIBINPUT_CONFIG_TAP_DISABLED; /* if no seat name is passed in, just use default seat name */ if (!seat) seat = "seat0"; EINA_LIST_FOREACH(manager->input.seats, l, eseat) { if ((eseat->name) && (strcmp(eseat->name, seat))) continue; EINA_LIST_FOREACH(eseat->devices, ll, edev) { if (!libinput_device_has_capability(edev->device, LIBINPUT_DEVICE_CAP_POINTER)) continue; if (libinput_device_config_tap_set_enabled(edev->device, state) != LIBINPUT_CONFIG_STATUS_SUCCESS) { WRN("Failed to %s tap-to-click on device: %s", enabled ? "enable" : "disable", libinput_device_get_name(edev->device)); continue; } } } } EAPI Elput_Seat * elput_device_seat_get(const Elput_Device *dev) { EINA_SAFETY_ON_NULL_RETURN_VAL(dev, NULL); return dev->seat; } EAPI Elput_Device_Caps elput_device_caps_get(const Elput_Device *dev) { EINA_SAFETY_ON_NULL_RETURN_VAL(dev, 0); return dev->caps; } EAPI Eina_Stringshare * elput_device_output_name_get(Elput_Device *device) { EINA_SAFETY_ON_NULL_RETURN_VAL(device, NULL); return device->output_name; } EAPI const Eina_List * elput_seat_devices_get(const Elput_Seat *seat) { EINA_SAFETY_ON_NULL_RETURN_VAL(seat, NULL); return seat->devices; } EAPI Eina_Stringshare * elput_seat_name_get(const Elput_Seat *seat) { EINA_SAFETY_ON_NULL_RETURN_VAL(seat, NULL); return seat->name; } EAPI Elput_Manager * elput_seat_manager_get(const Elput_Seat *seat) { EINA_SAFETY_ON_NULL_RETURN_VAL(seat, NULL); return seat->manager; }