/* * Copyright © 2013 Intel Corporation * Copyright © 2013 Jonas Ådahl * * 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 "config.h" #include #include #include #include #include #include #include #include #include "libweston-internal.h" #include "launcher-util.h" #include "libinput-seat.h" #include "libinput-device.h" #include "shared/helpers.h" static void process_events(struct udev_input *input); static struct udev_seat * udev_seat_create(struct udev_input *input, const char *seat_name); static void udev_seat_destroy(struct udev_seat *seat); static struct udev_seat * get_udev_seat(struct udev_input *input, struct libinput_device *device) { struct libinput_seat *libinput_seat; const char *seat_name; libinput_seat = libinput_device_get_seat(device); seat_name = libinput_seat_get_logical_name(libinput_seat); return udev_seat_get_named(input, seat_name); } static struct weston_output * output_find_by_head_name(struct weston_compositor *compositor, const char *head_name) { struct weston_output *output; struct weston_head *head; if (!head_name) return NULL; /* Only enabled outputs with connected heads. * This means force-enabled outputs but with disconnected heads * will be ignored; if the touchscreen doesn't have a video signal, * touching it is meaningless. */ wl_list_for_each(output, &compositor->output_list, link) { wl_list_for_each(head, &output->head_list, output_link) { if (!weston_head_is_connected(head)) continue; if (strcmp(head_name, head->name) == 0) return output; } } return NULL; } static void device_added(struct udev_input *input, struct libinput_device *libinput_device) { struct weston_compositor *c; struct evdev_device *device; struct weston_output *output; const char *output_name; struct weston_seat *seat; struct udev_seat *udev_seat; struct weston_pointer *pointer; c = input->compositor; udev_seat = get_udev_seat(input, libinput_device); if (!udev_seat) return; seat = &udev_seat->base; device = evdev_device_create(libinput_device, seat); if (device == NULL) return; if (input->configure_device != NULL) input->configure_device(c, device->device); evdev_device_set_calibration(device); udev_seat = (struct udev_seat *) seat; wl_list_insert(udev_seat->devices_list.prev, &device->link); pointer = weston_seat_get_pointer(seat); if (seat->output && pointer) weston_pointer_clamp(pointer, &pointer->x, &pointer->y); output_name = libinput_device_get_output_name(libinput_device); if (output_name) { device->output_name = strdup(output_name); output = output_find_by_head_name(c, output_name); evdev_device_set_output(device, output); } else if (!wl_list_empty(&c->output_list)) { /* default assignment to an arbitrary output */ output = container_of(c->output_list.next, struct weston_output, link); evdev_device_set_output(device, output); } if (!input->suspended) weston_seat_repick(seat); } static void device_removed(struct udev_input *input, struct libinput_device *libinput_device) { struct evdev_device *device; device = libinput_device_get_user_data(libinput_device); evdev_device_destroy(device); } static void udev_seat_remove_devices(struct udev_seat *seat) { struct evdev_device *device, *next; wl_list_for_each_safe(device, next, &seat->devices_list, link) { evdev_device_destroy(device); } } void udev_input_disable(struct udev_input *input) { if (input->suspended) return; wl_event_source_remove(input->libinput_source); input->libinput_source = NULL; libinput_suspend(input->libinput); process_events(input); input->suspended = 1; } static int udev_input_process_event(struct libinput_event *event) { struct libinput *libinput = libinput_event_get_context(event); struct libinput_device *libinput_device = libinput_event_get_device(event); struct udev_input *input = libinput_get_user_data(libinput); int handled = 1; switch (libinput_event_get_type(event)) { case LIBINPUT_EVENT_DEVICE_ADDED: device_added(input, libinput_device); break; case LIBINPUT_EVENT_DEVICE_REMOVED: device_removed(input, libinput_device); break; default: handled = 0; } return handled; } static void process_event(struct libinput_event *event) { if (udev_input_process_event(event)) return; if (evdev_device_process_event(event)) return; } static void process_events(struct udev_input *input) { struct libinput_event *event; while ((event = libinput_get_event(input->libinput))) { process_event(event); libinput_event_destroy(event); } } static int udev_input_dispatch(struct udev_input *input) { if (libinput_dispatch(input->libinput) != 0) weston_log("libinput: Failed to dispatch libinput\n"); process_events(input); return 0; } static int libinput_source_dispatch(int fd, uint32_t mask, void *data) { struct udev_input *input = data; return udev_input_dispatch(input) != 0; } static int open_restricted(const char *path, int flags, void *user_data) { struct udev_input *input = user_data; struct weston_launcher *launcher = input->compositor->launcher; return weston_launcher_open(launcher, path, flags); } static void close_restricted(int fd, void *user_data) { struct udev_input *input = user_data; struct weston_launcher *launcher = input->compositor->launcher; weston_launcher_close(launcher, fd); } const struct libinput_interface libinput_interface = { open_restricted, close_restricted, }; int udev_input_enable(struct udev_input *input) { struct wl_event_loop *loop; struct weston_compositor *c = input->compositor; int fd; struct udev_seat *seat; int devices_found = 0; loop = wl_display_get_event_loop(c->wl_display); fd = libinput_get_fd(input->libinput); input->libinput_source = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, libinput_source_dispatch, input); if (!input->libinput_source) { return -1; } if (input->suspended) { if (libinput_resume(input->libinput) != 0) { wl_event_source_remove(input->libinput_source); input->libinput_source = NULL; return -1; } input->suspended = 0; process_events(input); } wl_list_for_each(seat, &input->compositor->seat_list, base.link) { evdev_notify_keyboard_focus(&seat->base, &seat->devices_list); if (!wl_list_empty(&seat->devices_list)) devices_found = 1; } if (devices_found == 0 && !c->require_input) { weston_log("warning: no input devices found, but none required " "as per configuration.\n"); return 0; } if (devices_found == 0) { weston_log( "warning: no input devices on entering Weston. " "Possible causes:\n" "\t- no permissions to read /dev/input/event*\n" "\t- seats misconfigured " "(Weston backend option 'seat', " "udev device property ID_SEAT)\n"); return -1; } return 0; } static void libinput_log_func(struct libinput *libinput, enum libinput_log_priority priority, const char *format, va_list args) { weston_vlog(format, args); } int udev_input_init(struct udev_input *input, struct weston_compositor *c, struct udev *udev, const char *seat_id, udev_configure_device_t configure_device) { enum libinput_log_priority priority = LIBINPUT_LOG_PRIORITY_INFO; const char *log_priority = NULL; memset(input, 0, sizeof *input); input->compositor = c; input->configure_device = configure_device; log_priority = getenv("WESTON_LIBINPUT_LOG_PRIORITY"); input->libinput = libinput_udev_create_context(&libinput_interface, input, udev); if (!input->libinput) { return -1; } libinput_log_set_handler(input->libinput, &libinput_log_func); if (log_priority) { if (strcmp(log_priority, "debug") == 0) { priority = LIBINPUT_LOG_PRIORITY_DEBUG; } else if (strcmp(log_priority, "info") == 0) { priority = LIBINPUT_LOG_PRIORITY_INFO; } else if (strcmp(log_priority, "error") == 0) { priority = LIBINPUT_LOG_PRIORITY_ERROR; } } libinput_log_set_priority(input->libinput, priority); if (libinput_udev_assign_seat(input->libinput, seat_id) != 0) { libinput_unref(input->libinput); return -1; } process_events(input); return udev_input_enable(input); } void udev_input_destroy(struct udev_input *input) { struct udev_seat *seat, *next; if (input->libinput_source) wl_event_source_remove(input->libinput_source); wl_list_for_each_safe(seat, next, &input->compositor->seat_list, base.link) udev_seat_destroy(seat); libinput_unref(input->libinput); } static void udev_seat_led_update(struct weston_seat *seat_base, enum weston_led leds) { struct udev_seat *seat = (struct udev_seat *) seat_base; struct evdev_device *device; wl_list_for_each(device, &seat->devices_list, link) evdev_led_update(device, leds); } static void udev_seat_output_changed(struct udev_seat *seat, struct weston_output *output) { struct evdev_device *device; struct weston_output *found; wl_list_for_each(device, &seat->devices_list, link) { /* If we find any input device without an associated output * or an output name to associate with, just tie it with the * output we got here - the default assignment. */ if (!device->output_name) { if (!device->output) evdev_device_set_output(device, output); continue; } /* Update all devices' output associations, may they gain or * lose it. */ found = output_find_by_head_name(output->compositor, device->output_name); evdev_device_set_output(device, found); } } static void notify_output_create(struct wl_listener *listener, void *data) { struct udev_seat *seat = container_of(listener, struct udev_seat, output_create_listener); struct weston_output *output = data; udev_seat_output_changed(seat, output); } static void notify_output_heads_changed(struct wl_listener *listener, void *data) { struct udev_seat *seat = container_of(listener, struct udev_seat, output_heads_listener); struct weston_output *output = data; udev_seat_output_changed(seat, output); } static struct udev_seat * udev_seat_create(struct udev_input *input, const char *seat_name) { struct weston_compositor *c = input->compositor; struct udev_seat *seat; seat = zalloc(sizeof *seat); if (!seat) return NULL; weston_seat_init(&seat->base, c, seat_name); seat->base.led_update = udev_seat_led_update; seat->output_create_listener.notify = notify_output_create; wl_signal_add(&c->output_created_signal, &seat->output_create_listener); seat->output_heads_listener.notify = notify_output_heads_changed; wl_signal_add(&c->output_heads_changed_signal, &seat->output_heads_listener); wl_list_init(&seat->devices_list); return seat; } static void udev_seat_destroy(struct udev_seat *seat) { struct weston_keyboard *keyboard = weston_seat_get_keyboard(&seat->base); if (keyboard) notify_keyboard_focus_out(&seat->base); udev_seat_remove_devices(seat); weston_seat_release(&seat->base); wl_list_remove(&seat->output_create_listener.link); wl_list_remove(&seat->output_heads_listener.link); free(seat); } struct udev_seat * udev_seat_get_named(struct udev_input *input, const char *seat_name) { struct udev_seat *seat; wl_list_for_each(seat, &input->compositor->seat_list, base.link) { if (strcmp(seat->base.seat_name, seat_name) == 0) return seat; } return udev_seat_create(input, seat_name); }