diff --git a/cagebreak.c b/cagebreak.c index 6e16dc2..7794b40 100644 --- a/cagebreak.c +++ b/cagebreak.c @@ -1,4 +1,4 @@ -/* + * * Cagebreak: A Wayland tiling compositor. * * Copyright (C) 2018-2020 Jente Hidskes @@ -49,6 +49,7 @@ #include "message.h" #include "output.h" #include "parse.h" +#include "input_manager.h" #include "seat.h" #include "server.h" #include "xdg_shell.h" @@ -264,6 +265,8 @@ main(int argc, char *argv[]) { wlr_log_init(WLR_ERROR, NULL); #endif + wl_list_init(&server.input_config); + server.modes = malloc(4 * sizeof(char *)); if(!server.modes) { wlr_log(WLR_ERROR, "Error allocating mode array"); @@ -358,6 +361,10 @@ main(int argc, char *argv[]) { goto end; } + server.input = input_manager_create(&server); + fprintf(stderr, "input_manager_create:%d\n",wl_list_length(&server.input->devices)); + + data_control_manager = wlr_data_control_manager_v1_create(server.wl_display); if(!data_control_manager) { diff --git a/input.h b/input.h new file mode 100644 index 0000000..c2ba243 --- /dev/null +++ b/input.h @@ -0,0 +1,14 @@ +#ifndef _LIBINPUT_H +#define _LIBINPUT_H + +#include + +struct cg_input_device; + +void cg_input_configure_libinput_device(struct cg_input_device *device); + +void cg_input_reset_libinput_device(struct cg_input_device *device); + +bool cg_libinput_device_is_builtin(struct cg_input_device *device); + +#endif diff --git a/input_manager.c b/input_manager.c new file mode 100644 index 0000000..8232891 --- /dev/null +++ b/input_manager.c @@ -0,0 +1,267 @@ +/* + * Cagebreak: A Wayland tiling compositor. + * + * Copyright (C) 2020 The Cagebreak Authors + * Copyright (C) 2018-2020 Jente Hidskes + * + * See the LICENSE file accompanying this file. + */ + +#define _POSIX_C_SOURCE 200812L + +#include "config.h" +#include "server.h" +#include "seat.h" +#include "input_manager.h" +#include "input.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void strip_whitespace(char *str) { + static const char whitespace[] = " \f\n\r\t\v"; + size_t len = strlen(str); + size_t start = strspn(str, whitespace); + memmove(str, &str[start], len + 1 - start); + + if (*str) { + for (len -= start + 1; isspace(str[len]); --len) {} + str[len + 1] = '\0'; + } +} + +char *input_device_get_identifier(struct wlr_input_device *device) { + int vendor = device->vendor; + int product = device->product; + char *name = strdup(device->name ? device->name : ""); + strip_whitespace(name); + + char *p = name; + for (; *p; ++p) { + // There are in fact input devices with unprintable characters in its name + if (*p == ' ' || !isprint(*p)) { + *p = '_'; + } + } + + const char *fmt = "%d:%d:%s"; + int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1; + char *identifier = malloc(len); + if (!identifier) { + wlr_log(WLR_ERROR, "Unable to allocate unique input device name"); + return NULL; + } + + snprintf(identifier, len, fmt, vendor, product, name); + free(name); + return identifier; +} + +void input_manager_handle_device_destroy(struct wl_listener *listener, void *data) { + struct cg_input_device *input_device = + wl_container_of(listener, input_device, device_destroy); + + if (!input_device) { + wlr_log(WLR_ERROR, "Could not find cagebreak input device to destroy"); + return; + } + + seat_remove_device(input_device->server->seat, input_device); + + wl_list_remove(&input_device->link); + wl_list_remove(&input_device->device_destroy.link); + free(input_device->identifier); + free(input_device); +} + +static void handle_new_input(struct wl_listener *listener, void *data) { + struct cg_input_manager *input = + wl_container_of(listener, input, new_input); + struct cg_server *server= input->server; + struct wlr_input_device *device = data; + + struct cg_input_device *input_device = + calloc(1, sizeof(struct cg_input_device)); + if (!input_device) { + wlr_log(WLR_ERROR,"Could not allocate input device"); + return; + } + device->data = input_device; + + input_device->wlr_device = device; + input_device->identifier = input_device_get_identifier(device); + input_device->server=server; + input_device->pointer=NULL; + input_device->touch=NULL; + + wl_list_insert(&input->devices, &input_device->link); + + cg_input_configure_libinput_device(input_device); + + wl_signal_add(&device->events.destroy, &input_device->device_destroy); + input_device->device_destroy.notify = input_manager_handle_device_destroy; + + struct cg_seat *seat = server->seat; + seat_add_device(seat, input_device); +} + +void handle_virtual_keyboard(struct wl_listener *listener, void *data) { + struct cg_input_manager *input_manager = + wl_container_of(listener, input_manager, virtual_keyboard_new); + struct wlr_virtual_keyboard_v1 *keyboard = data; + struct wlr_input_device *device = &keyboard->input_device; + + struct cg_seat *seat = input_manager->server->seat; + + struct cg_input_device *input_device = + calloc(1, sizeof(struct cg_input_device)); + if (!input_device) { + wlr_log(WLR_ERROR, "could not allocate input device"); + return; + } + device->data = input_device; + + input_device->is_virtual = true; + input_device->wlr_device = device; + input_device->identifier = input_device_get_identifier(device); + wl_list_insert(&input_manager->devices, &input_device->link); + input_device->server=input_manager->server; + input_device->pointer=NULL; + input_device->touch=NULL; + + wl_signal_add(&device->events.destroy, &input_device->device_destroy); + input_device->device_destroy.notify = input_manager_handle_device_destroy; + + seat_add_device(seat, input_device); +} + +void handle_virtual_pointer(struct wl_listener *listener, void *data) { + struct cg_input_manager *input_manager = + wl_container_of(listener, input_manager, virtual_pointer_new); + struct wlr_virtual_pointer_v1_new_pointer_event *event = data; + struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; + struct wlr_input_device *device = &pointer->input_device; + + struct cg_seat *seat = input_manager->server->seat; + + struct cg_input_device *input_device = + calloc(1, sizeof(struct cg_input_device)); + if (!input_device) { + wlr_log(WLR_ERROR, "could not allocate input device"); + return; + } + device->data = input_device; + + input_device->is_virtual = true; + input_device->wlr_device = device; + input_device->identifier = input_device_get_identifier(device); + wl_list_insert(&input_manager->devices, &input_device->link); + input_device->server=input_manager->server; + input_device->pointer=NULL; + input_device->touch=NULL; + + wl_signal_add(&device->events.destroy, &input_device->device_destroy); + input_device->device_destroy.notify = input_manager_handle_device_destroy; + + seat_add_device(seat, input_device); + + if (event->suggested_output) { + wlr_cursor_map_input_to_output(seat->cursor, device, + event->suggested_output); + } +} + +struct cg_input_manager *input_manager_create(struct cg_server *server) { + struct cg_input_manager *input = + calloc(1, sizeof(struct cg_input_manager)); + if (!input) { + return NULL; + } + + wl_list_init(&input->devices); + input->server=server; + + input->new_input.notify = handle_new_input; + wl_signal_add(&server->backend->events.new_input, &input->new_input); + + input->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create( + server->wl_display); + wl_signal_add(&input->virtual_keyboard->events.new_virtual_keyboard, + &input->virtual_keyboard_new); + input->virtual_keyboard_new.notify = handle_virtual_keyboard; + + input->virtual_pointer = wlr_virtual_pointer_manager_v1_create( + server->wl_display + ); + wl_signal_add(&input->virtual_pointer->events.new_virtual_pointer, + &input->virtual_pointer_new); + input->virtual_pointer_new.notify = handle_virtual_pointer; + + return input; +} + +static bool device_is_touchpad(struct cg_input_device *device) { + if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || + !wlr_input_device_is_libinput(device->wlr_device)) { + return false; + } + + struct libinput_device *libinput_device = + wlr_libinput_get_device_handle(device->wlr_device); + + return libinput_device_config_tap_get_finger_count(libinput_device) > 0; +} + +const char *input_device_get_type(struct cg_input_device *device) { + switch (device->wlr_device->type) { + case WLR_INPUT_DEVICE_POINTER: + if (device_is_touchpad(device)) { + return "touchpad"; + } else { + return "pointer"; + } + case WLR_INPUT_DEVICE_KEYBOARD: + return "keyboard"; + case WLR_INPUT_DEVICE_TOUCH: + return "touch"; + case WLR_INPUT_DEVICE_TABLET_TOOL: + return "tablet_tool"; + case WLR_INPUT_DEVICE_TABLET_PAD: + return "tablet_pad"; + case WLR_INPUT_DEVICE_SWITCH: + return "switch"; + } + return "unknown"; +} + +struct cg_input_config *input_device_get_config(struct cg_input_device *device) { + struct cg_server *server=device->server; + struct cg_input_config *wildcard_config = NULL; + struct cg_input_config *config = NULL; + wl_list_for_each(config, &server->input_config, link) { + if (strcmp(config->identifier, device->identifier) == 0) { + return config; + } else if (strcmp(config->identifier, "*") == 0) { + wildcard_config = config; + } + } + + const char *device_type = input_device_get_type(device); + wl_list_for_each(config, &server->input_config, link) { + if (strncmp(config->identifier,"type:",5)&&strcmp(config->identifier + 5, device_type) == 0) { + return config; + } + } + + return wildcard_config; +} diff --git a/input_manager.h b/input_manager.h new file mode 100644 index 0000000..d0bd3ff --- /dev/null +++ b/input_manager.h @@ -0,0 +1,108 @@ +#ifndef CG_INPUT_MANAGER_H +#define CG_INPUT_MANAGER_H + +#include "seat.h" +#include "server.h" +#include +#include + + +struct cg_input_manager *input_manager_create(struct cg_server *server); +struct cg_input_config *input_device_get_config(struct cg_input_device *device); +void input_manager_handle_device_destroy(struct wl_listener *listener, void *data); + +struct cg_input_manager { + struct wl_list devices; + struct cg_server *server; + + struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; + struct wlr_virtual_pointer_manager_v1 *virtual_pointer; + + struct wl_listener new_input; + struct wl_listener virtual_keyboard_new; + struct wl_listener virtual_pointer_new; +}; + + +struct cg_input_config_mapped_from_region { + double x1, y1; + double x2, y2; + bool mm; +}; + +struct calibration_matrix { + bool configured; + float matrix[6]; +}; + +enum cg_input_config_mapped_to { + MAPPED_TO_DEFAULT, + MAPPED_TO_OUTPUT, + MAPPED_TO_REGION, +}; + +/** + * options for input devices + */ +struct cg_input_config { + char *identifier; + const char *input_type; + + int accel_profile; + struct calibration_matrix calibration_matrix; + int click_method; + int drag; + int drag_lock; + int dwt; + int left_handed; + int middle_emulation; + int natural_scroll; + float pointer_accel; + float scroll_factor; + int repeat_delay; + int repeat_rate; + int scroll_button; + int scroll_method; + int send_events; + int tap; + int tap_button_map; + + char *xkb_layout; + char *xkb_model; + char *xkb_options; + char *xkb_rules; + char *xkb_variant; + char *xkb_file; + + bool xkb_file_is_set; + + int xkb_numlock; + int xkb_capslock; + + struct wl_list link; + struct cg_input_config_mapped_from_region *mapped_from_region; + + enum cg_input_config_mapped_to mapped_to; + char *mapped_to_output; + struct wlr_box *mapped_to_region; + + struct wl_list tools; + + bool capturable; + struct wlr_box region; +}; + +struct cg_input_device { + char *identifier; + struct cg_server *server; + struct wlr_input_device *wlr_device; + struct wl_list link; + struct wl_listener device_destroy; + bool is_virtual; + + /* Only one of the following is non-NULL depending on the type of the input device */ + struct cg_pointer *pointer; + struct cg_touch *touch; +}; + +#endif diff --git a/keybinding.c b/keybinding.c index 005b87a..4eec370 100644 --- a/keybinding.c +++ b/keybinding.c @@ -12,6 +12,7 @@ #include "keybinding.h" #include "message.h" #include "output.h" +#include "input_manager.h" #include "seat.h" #include "server.h" #include "view.h" @@ -870,6 +871,11 @@ keybinding_configure_output(struct cg_server *server, } } +void +keybinding_configure_input_dev(struct cg_server *server, struct cg_input_config *cfg) { + +} + /* Hint: see keybinding.h for details on "data" */ int run_action(enum keybinding_action action, struct cg_server *server, diff --git a/libinput.c b/libinput.c new file mode 100644 index 0000000..d5a3bba --- /dev/null +++ b/libinput.c @@ -0,0 +1,349 @@ +#include +#include +#include +#include +#include +#include +#include +#include "input.h" +#include "output.h" +#include "input_manager.h" + +static void log_status(enum libinput_config_status status) { + if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) { + wlr_log(WLR_ERROR, "Failed to apply libinput config: %s", + libinput_config_status_to_str(status)); + } +} + +static bool set_send_events(struct libinput_device *device, uint32_t mode) { + if (libinput_device_config_send_events_get_mode(device) == mode) { + return false; + } + wlr_log(WLR_DEBUG, "send_events_set_mode(%" PRIu32 ")", mode); + log_status(libinput_device_config_send_events_set_mode(device, mode)); + return true; +} + +static bool set_tap(struct libinput_device *device, + enum libinput_config_tap_state tap) { + if (libinput_device_config_tap_get_finger_count(device) <= 0 || + libinput_device_config_tap_get_enabled(device) == tap) { + return false; + } + wlr_log(WLR_DEBUG, "tap_set_enabled(%d)", tap); + log_status(libinput_device_config_tap_set_enabled(device, tap)); + return true; +} + +static bool set_tap_button_map(struct libinput_device *device, + enum libinput_config_tap_button_map map) { + if (libinput_device_config_tap_get_finger_count(device) <= 0 || + libinput_device_config_tap_get_button_map(device) == map) { + return false; + } + wlr_log(WLR_DEBUG, "tap_set_button_map(%d)", map); + log_status(libinput_device_config_tap_set_button_map(device, map)); + return true; +} + +static bool set_tap_drag(struct libinput_device *device, + enum libinput_config_drag_state drag) { + if (libinput_device_config_tap_get_finger_count(device) <= 0 || + libinput_device_config_tap_get_drag_enabled(device) == drag) { + return false; + } + wlr_log(WLR_DEBUG, "tap_set_drag_enabled(%d)", drag); + log_status(libinput_device_config_tap_set_drag_enabled(device, drag)); + return true; +} + +static bool set_tap_drag_lock(struct libinput_device *device, + enum libinput_config_drag_lock_state lock) { + if (libinput_device_config_tap_get_finger_count(device) <= 0 || + libinput_device_config_tap_get_drag_lock_enabled(device) == lock) { + return false; + } + wlr_log(WLR_DEBUG, "tap_set_drag_lock_enabled(%d)", lock); + log_status(libinput_device_config_tap_set_drag_lock_enabled(device, lock)); + return true; +} + +static bool set_accel_speed(struct libinput_device *device, double speed) { + if (!libinput_device_config_accel_is_available(device) || + libinput_device_config_accel_get_speed(device) == speed) { + return false; + } + wlr_log(WLR_DEBUG, "accel_set_speed(%f)", speed); + log_status(libinput_device_config_accel_set_speed(device, speed)); + return true; +} + +static bool set_accel_profile(struct libinput_device *device, + enum libinput_config_accel_profile profile) { + if (!libinput_device_config_accel_is_available(device) || + libinput_device_config_accel_get_profile(device) == profile) { + return false; + } + wlr_log(WLR_DEBUG, "accel_set_profile(%d)", profile); + log_status(libinput_device_config_accel_set_profile(device, profile)); + return true; +} + +static bool set_natural_scroll(struct libinput_device *d, bool n) { + if (!libinput_device_config_scroll_has_natural_scroll(d) || + libinput_device_config_scroll_get_natural_scroll_enabled(d) == n) { + return false; + } + wlr_log(WLR_DEBUG, "scroll_set_natural_scroll(%d)", n); + log_status(libinput_device_config_scroll_set_natural_scroll_enabled(d, n)); + return true; +} + +static bool set_left_handed(struct libinput_device *device, bool left) { + if (!libinput_device_config_left_handed_is_available(device) || + libinput_device_config_left_handed_get(device) == left) { + return false; + } + wlr_log(WLR_DEBUG, "left_handed_set(%d)", left); + log_status(libinput_device_config_left_handed_set(device, left)); + return true; +} + +static bool set_click_method(struct libinput_device *device, + enum libinput_config_click_method method) { + uint32_t click = libinput_device_config_click_get_methods(device); + if ((click & ~LIBINPUT_CONFIG_CLICK_METHOD_NONE) == 0 || + libinput_device_config_click_get_method(device) == method) { + return false; + } + wlr_log(WLR_DEBUG, "click_set_method(%d)", method); + log_status(libinput_device_config_click_set_method(device, method)); + return true; +} + +static bool set_middle_emulation(struct libinput_device *dev, + enum libinput_config_middle_emulation_state mid) { + if (!libinput_device_config_middle_emulation_is_available(dev) || + libinput_device_config_middle_emulation_get_enabled(dev) == mid) { + return false; + } + wlr_log(WLR_DEBUG, "middle_emulation_set_enabled(%d)", mid); + log_status(libinput_device_config_middle_emulation_set_enabled(dev, mid)); + return true; +} + +static bool set_scroll_method(struct libinput_device *device, + enum libinput_config_scroll_method method) { + uint32_t scroll = libinput_device_config_scroll_get_methods(device); + if ((scroll & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0 || + libinput_device_config_scroll_get_method(device) == method) { + return false; + } + wlr_log(WLR_DEBUG, "scroll_set_method(%d)", method); + log_status(libinput_device_config_scroll_set_method(device, method)); + return true; +} + +static bool set_scroll_button(struct libinput_device *dev, uint32_t button) { + uint32_t scroll = libinput_device_config_scroll_get_methods(dev); + if ((scroll & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0 || + libinput_device_config_scroll_get_button(dev) == button) { + return false; + } + wlr_log(WLR_DEBUG, "scroll_set_button(%" PRIu32 ")", button); + log_status(libinput_device_config_scroll_set_button(dev, button)); + return true; +} + +static bool set_dwt(struct libinput_device *device, bool dwt) { + if (!libinput_device_config_dwt_is_available(device) || + libinput_device_config_dwt_get_enabled(device) == dwt) { + return false; + } + wlr_log(WLR_DEBUG, "dwt_set_enabled(%d)", dwt); + log_status(libinput_device_config_dwt_set_enabled(device, dwt)); + return true; +} + +static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { + if (!libinput_device_config_calibration_has_matrix(dev)) { + return false; + } + bool changed = false; + float current[6]; + libinput_device_config_calibration_get_matrix(dev, current); + for (int i = 0; i < 6; i++) { + if (current[i] != mat[i]) { + changed = true; + break; + } + } + if (changed) { + wlr_log(WLR_DEBUG, "calibration_set_matrix(%f, %f, %f, %f, %f, %f)", + mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); + log_status(libinput_device_config_calibration_set_matrix(dev, mat)); + } + return changed; +} + +void output_get_identifier(char *identifier, size_t len, + struct cg_output *output) { + struct wlr_output *wlr_output = output->wlr_output; + snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, + wlr_output->serial); +} + +struct cg_output *output_by_name_or_id(const char *name_or_id, struct cg_server *server) { + struct cg_output *output=NULL; + wl_list_for_each(output,&server->outputs,link) { + char identifier[128]; + output_get_identifier(identifier, sizeof(identifier), output); + if (strcasecmp(identifier, name_or_id) == 0 + || strcasecmp(output->wlr_output->name, name_or_id) == 0) { + return output; + } + } + return NULL; +} + +void cg_input_configure_libinput_device(struct cg_input_device *input_device) { + struct cg_input_config *ic = input_device_get_config(input_device); + if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) { + return; + } + + struct libinput_device *device = + wlr_libinput_get_device_handle(input_device->wlr_device); + wlr_log(WLR_DEBUG, "cg_input_configure_libinput_device('%s' on '%s')", + ic->identifier, input_device->identifier); + + bool changed = false; + if (ic->mapped_to_output && + !output_by_name_or_id(ic->mapped_to_output,input_device->server)) { + wlr_log(WLR_DEBUG, + "%s '%s' is mapped to offline output '%s'; disabling input", + ic->input_type, ic->identifier, ic->mapped_to_output); + changed |= set_send_events(device, + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + } else if (ic->send_events != INT_MIN) { + changed |= set_send_events(device, ic->send_events); + } else { + // Have to reset to the default mode here, otherwise if ic->send_events + // is unset and a mapped output just came online after being disabled, + // we'd remain stuck sending no events. + changed |= set_send_events(device, + libinput_device_config_send_events_get_default_mode(device)); + } + + if (ic->tap != INT_MIN) { + changed |= set_tap(device, ic->tap); + } + if (ic->tap_button_map != INT_MIN) { + changed |= set_tap_button_map(device, ic->tap_button_map); + } + if (ic->drag != INT_MIN) { + changed |= set_tap_drag(device, ic->drag); + } + if (ic->drag_lock != INT_MIN) { + changed |= set_tap_drag_lock(device, ic->drag_lock); + } + if (ic->pointer_accel != FLT_MIN) { + changed |= set_accel_speed(device, ic->pointer_accel); + } + if (ic->accel_profile != INT_MIN) { + changed |= set_accel_profile(device, ic->accel_profile); + } + if (ic->natural_scroll != INT_MIN) { + changed |= set_natural_scroll(device, ic->natural_scroll); + } + if (ic->left_handed != INT_MIN) { + changed |= set_left_handed(device, ic->left_handed); + } + if (ic->click_method != INT_MIN) { + changed |= set_click_method(device, ic->click_method); + } + if (ic->middle_emulation != INT_MIN) { + changed |= set_middle_emulation(device, ic->middle_emulation); + } + if (ic->scroll_method != INT_MIN) { + changed |= set_scroll_method(device, ic->scroll_method); + } + if (ic->scroll_button != INT_MIN) { + changed |= set_scroll_button(device, ic->scroll_button); + } + if (ic->dwt != INT_MIN) { + changed |= set_dwt(device, ic->dwt); + } + if (ic->calibration_matrix.configured) { + changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix); + } +} + +void cg_input_reset_libinput_device(struct cg_input_device *input_device) { + if (!wlr_input_device_is_libinput(input_device->wlr_device)) { + return; + } + + struct libinput_device *device = + wlr_libinput_get_device_handle(input_device->wlr_device); + wlr_log(WLR_DEBUG, "cg_input_reset_libinput_device(%s)", + input_device->identifier); + bool changed = false; + + changed |= set_send_events(device, + libinput_device_config_send_events_get_default_mode(device)); + changed |= set_tap(device, + libinput_device_config_tap_get_default_enabled(device)); + changed |= set_tap_button_map(device, + libinput_device_config_tap_get_default_button_map(device)); + changed |= set_tap_drag(device, + libinput_device_config_tap_get_default_drag_enabled(device)); + changed |= set_tap_drag_lock(device, + libinput_device_config_tap_get_default_drag_lock_enabled(device)); + changed |= set_accel_speed(device, + libinput_device_config_accel_get_default_speed(device)); + changed |= set_accel_profile(device, + libinput_device_config_accel_get_default_profile(device)); + changed |= set_natural_scroll(device, + libinput_device_config_scroll_get_default_natural_scroll_enabled( + device)); + changed |= set_left_handed(device, + libinput_device_config_left_handed_get_default(device)); + changed |= set_click_method(device, + libinput_device_config_click_get_default_method(device)); + changed |= set_middle_emulation(device, + libinput_device_config_middle_emulation_get_default_enabled(device)); + changed |= set_scroll_method(device, + libinput_device_config_scroll_get_default_method(device)); + changed |= set_scroll_button(device, + libinput_device_config_scroll_get_default_button(device)); + changed |= set_dwt(device, + libinput_device_config_dwt_get_default_enabled(device)); + + float matrix[6]; + libinput_device_config_calibration_get_default_matrix(device, matrix); + changed |= set_calibration_matrix(device, matrix); +} + +bool cg_libinput_device_is_builtin(struct cg_input_device *cg_device) { + if (!wlr_input_device_is_libinput(cg_device->wlr_device)) { + return false; + } + + struct libinput_device *device = + wlr_libinput_get_device_handle(cg_device->wlr_device); + struct udev_device *udev_device = + libinput_device_get_udev_device(device); + if (!udev_device) { + return false; + } + + const char *id_path = udev_device_get_property_value(udev_device, "ID_PATH"); + if (!id_path) { + return false; + } + + const char prefix[] = "platform-"; + return strncmp(id_path, prefix, strlen(prefix)) == 0; +} diff --git a/meson.build b/meson.build index 474a2c2..872ae1b 100644 --- a/meson.build +++ b/meson.build @@ -64,6 +64,8 @@ cairo = dependency('cairo') pango = dependency('pango') pangocairo = dependency('pangocairo') fontconfig = dependency('fontconfig') +libinput = dependency('libinput') +libudev = dependency('libudev') math = cc.find_library('m') wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') @@ -123,6 +125,7 @@ conf_data.set_quoted('CG_VERSION', version) cagebreak_main_file = [ 'cagebreak.c', ] cagebreak_source_strings = [ 'idle_inhibit_v1.c', + 'input_manager.c', 'ipc_server.c', 'keybinding.c', 'workspace.c', @@ -133,6 +136,7 @@ cagebreak_source_strings = [ 'util.c', 'view.c', 'xdg_shell.c', + 'libinput.c', 'server.c', 'message.c', 'pango.c', @@ -186,6 +190,8 @@ cagebreak_dependencies_dict = { 'wlroots': [wlroots,true], 'xkbcommon': [xkbcommon,true], 'fontconfig': [fontconfig,true], + 'libinput': [libinput,true], + 'libudev': [libudev,true], 'pixman': [pixman,true], 'pango': [pango,true], 'cairo': [cairo,true], @@ -201,6 +207,8 @@ reproducible_build_versions = { 'wlroots': '0.14.1', 'xkbcommon': '1.3.0', 'fontconfig': '2.13.94', + 'libinput': '2.13.94', + 'libudev': '2.13.94', 'pixman': '0.40.0', 'pango': '1.48.7', 'cairo': '1.17.4', diff --git a/seat.c b/seat.c index 51450ac..ae2f748 100644 --- a/seat.c +++ b/seat.c @@ -32,6 +32,7 @@ #endif #include "keybinding.h" +#include "input_manager.h" #include "message.h" #include "output.h" #include "seat.h" @@ -112,13 +113,13 @@ static void update_capabilities(const struct cg_seat *seat) { uint32_t caps = 0; - if(!wl_list_empty(&seat->keyboard_groups)) { + if(seat->num_keyboards>0) { caps |= WL_SEAT_CAPABILITY_KEYBOARD; } - if(!wl_list_empty(&seat->pointers)) { + if(seat->num_pointers>0) { caps |= WL_SEAT_CAPABILITY_POINTER; } - if(!wl_list_empty(&seat->touch)) { + if(seat->num_touch>0) { caps |= WL_SEAT_CAPABILITY_TOUCH; } wlr_seat_set_capabilities(seat->seat, caps); @@ -133,20 +134,21 @@ update_capabilities(const struct cg_seat *seat) { } static void -handle_touch_destroy(struct wl_listener *listener, void *_data) { - struct cg_touch *touch = wl_container_of(listener, touch, destroy); - struct cg_seat *seat = touch->seat; - +remove_touch(struct cg_seat *seat, struct cg_touch *touch) { + if(!touch) { + return; + } wl_list_remove(&touch->link); - wlr_cursor_detach_input_device(seat->cursor, touch->device); - wl_list_remove(&touch->destroy.link); + wlr_cursor_detach_input_device(seat->cursor, touch->device->wlr_device); + --seat->num_touch; free(touch); update_capabilities(seat); } static void -handle_new_touch(struct cg_seat *seat, struct wlr_input_device *device) { +new_touch(struct cg_seat *seat, struct cg_input_device *input_device) { + struct wlr_input_device *device=input_device->wlr_device; struct cg_touch *touch = calloc(1, sizeof(struct cg_touch)); if(!touch) { wlr_log(WLR_ERROR, "Cannot allocate touch"); @@ -154,12 +156,10 @@ handle_new_touch(struct cg_seat *seat, struct wlr_input_device *device) { } touch->seat = seat; - touch->device = device; + touch->device = input_device; wlr_cursor_attach_input_device(seat->cursor, device); - - wl_list_insert(&seat->touch, &touch->link); - touch->destroy.notify = handle_touch_destroy; - wl_signal_add(&touch->device->events.destroy, &touch->destroy); + input_device->touch=touch; + ++seat->num_touch; if(device->output_name != NULL) { struct cg_output *output; @@ -174,20 +174,20 @@ handle_new_touch(struct cg_seat *seat, struct wlr_input_device *device) { } static void -handle_pointer_destroy(struct wl_listener *listener, void *_data) { - struct cg_pointer *pointer = wl_container_of(listener, pointer, destroy); - struct cg_seat *seat = pointer->seat; - - wl_list_remove(&pointer->link); - wlr_cursor_detach_input_device(seat->cursor, pointer->device); - wl_list_remove(&pointer->destroy.link); +remove_pointer(struct cg_seat *seat, struct cg_pointer *pointer) { + if(!pointer) { + return; + } + wlr_cursor_detach_input_device(seat->cursor, pointer->device->wlr_device); + --seat->num_pointers; free(pointer); update_capabilities(seat); } static void -handle_new_pointer(struct cg_seat *seat, struct wlr_input_device *device) { +new_pointer(struct cg_seat *seat, struct cg_input_device *input_device) { + struct wlr_input_device *device=input_device->wlr_device; struct cg_pointer *pointer = calloc(1, sizeof(struct cg_pointer)); if(!pointer) { wlr_log(WLR_ERROR, "Cannot allocate pointer"); @@ -195,12 +195,10 @@ handle_new_pointer(struct cg_seat *seat, struct wlr_input_device *device) { } pointer->seat = seat; - pointer->device = device; + pointer->device = input_device; wlr_cursor_attach_input_device(seat->cursor, device); - - wl_list_insert(&seat->pointers, &pointer->link); - pointer->destroy.notify = handle_pointer_destroy; - wl_signal_add(&device->events.destroy, &pointer->destroy); + input_device->pointer=pointer; + ++seat->num_pointers; if(device->output_name != NULL) { struct cg_output *output; @@ -431,10 +429,11 @@ cleanup: } static void -handle_new_keyboard(struct cg_seat *seat, struct wlr_input_device *device) { +new_keyboard(struct cg_seat *seat, struct cg_input_device *input_device) { + struct wlr_input_device *device= input_device->wlr_device; struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if(!context) { - wlr_log(WLR_ERROR, "Unable to create XBK context"); + wlr_log(WLR_ERROR, "Unable to create XKB context"); return; } @@ -460,24 +459,63 @@ handle_new_keyboard(struct cg_seat *seat, struct wlr_input_device *device) { wlr_keyboard_set_repeat_info(device->keyboard, 25, 600); cg_keyboard_group_add(device, seat); + ++seat->num_keyboards; wlr_seat_set_keyboard(seat->seat, device); } -static void -handle_new_input(struct wl_listener *listener, void *data) { - struct cg_seat *seat = wl_container_of(listener, seat, new_input); - struct wlr_input_device *device = data; - - switch(device->type) { +void +seat_add_device(struct cg_seat *seat, struct cg_input_device *device) { + switch(device->wlr_device->type) { case WLR_INPUT_DEVICE_KEYBOARD: - handle_new_keyboard(seat, device); + new_keyboard(seat, device); break; case WLR_INPUT_DEVICE_POINTER: - handle_new_pointer(seat, device); + new_pointer(seat, device); break; case WLR_INPUT_DEVICE_TOUCH: - handle_new_touch(seat, device); + new_touch(seat, device); + break; + case WLR_INPUT_DEVICE_SWITCH: + wlr_log(WLR_DEBUG, "Switch input is not implemented"); + return; + case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET_PAD: + wlr_log(WLR_DEBUG, "Tablet input is not implemented"); + return; + } + + update_capabilities(seat); +} + +void remove_keyboard(struct cg_seat *seat, struct cg_input_device *keyboard) { + if (!keyboard) { + return; + } + struct wlr_seat *wlr_seat = seat->seat; + struct wlr_keyboard *wlr_keyboard = keyboard->wlr_device->keyboard; + if (keyboard->wlr_device->keyboard->group) { + wlr_keyboard_group_remove_keyboard(wlr_keyboard->group,wlr_keyboard); + } + if (wlr_seat_get_keyboard(wlr_seat) == wlr_keyboard) { + wlr_seat_set_keyboard(wlr_seat, NULL); + } + --seat->num_keyboards; +} + +void +seat_remove_device(struct cg_seat *seat, struct cg_input_device *device) { + switch(device->wlr_device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + remove_keyboard(seat, device); + break; + case WLR_INPUT_DEVICE_POINTER: + remove_pointer(seat, device->pointer); + device->pointer=NULL; + break; + case WLR_INPUT_DEVICE_TOUCH: + remove_touch(seat, device->touch); + device->touch=NULL; break; case WLR_INPUT_DEVICE_SWITCH: wlr_log(WLR_DEBUG, "Switch input is not implemented"); @@ -801,15 +839,11 @@ handle_destroy(struct wl_listener *listener, void *_data) { wl_event_source_remove(group->key_repeat_timer); free(group); } - struct cg_pointer *pointer, *pointer_tmp; - wl_list_for_each_safe(pointer, pointer_tmp, &seat->pointers, link) { - handle_pointer_destroy(&pointer->destroy, NULL); + + struct cg_input_device *it, *it_tmp; + wl_list_for_each_safe(it, it_tmp, &seat->server->input->devices, link) { + input_manager_handle_device_destroy(&it->device_destroy,NULL); } - struct cg_touch *touch, *touch_tmp; - wl_list_for_each_safe(touch, touch_tmp, &seat->touch, link) { - handle_touch_destroy(&touch->destroy, NULL); - } - wl_list_remove(&seat->new_input.link); wlr_xcursor_manager_destroy(seat->xcursor_manager); if(seat->cursor) { @@ -898,11 +932,9 @@ seat_create(struct cg_server *server, struct wlr_backend *backend) { &seat->request_set_primary_selection); wl_list_init(&seat->keyboard_groups); - wl_list_init(&seat->pointers); - wl_list_init(&seat->touch); - - seat->new_input.notify = handle_new_input; - wl_signal_add(&backend->events.new_input, &seat->new_input); + seat->num_keyboards=0; + seat->num_pointers=0; + seat->num_touch=0; wl_list_init(&seat->drag_icons); seat->request_start_drag.notify = handle_request_start_drag; diff --git a/seat.h b/seat.h index a7632d7..9331042 100644 --- a/seat.h +++ b/seat.h @@ -7,6 +7,7 @@ struct cg_server; struct cg_view; struct wlr_cursor; struct wlr_input_device; +struct cg_input_device; struct wlr_seat; struct wlr_xcursor_manager; struct wlr_backend; @@ -20,9 +21,10 @@ struct cg_seat { struct wl_listener destroy; struct wl_list keyboard_groups; - struct wl_list pointers; - struct wl_list touch; - struct wl_listener new_input; + + uint16_t num_keyboards; + uint16_t num_pointers; + uint16_t num_touch; struct wlr_cursor *cursor; struct wlr_xcursor_manager *xcursor_manager; @@ -70,17 +72,13 @@ struct cg_keyboard_group { struct cg_pointer { struct wl_list link; // seat::pointers struct cg_seat *seat; - struct wlr_input_device *device; - - struct wl_listener destroy; + struct cg_input_device *device; }; struct cg_touch { struct wl_list link; // seat::touch struct cg_seat *seat; - struct wlr_input_device *device; - - struct wl_listener destroy; + struct cg_input_device *device; }; struct cg_drag_icon { @@ -102,5 +100,9 @@ struct cg_view * seat_get_focus(const struct cg_seat *seat); void seat_set_focus(struct cg_seat *seat, struct cg_view *view); +void +seat_add_device(struct cg_seat *seat, struct cg_input_device *device); +void +seat_remove_device(struct cg_seat *seat, struct cg_input_device *device); #endif diff --git a/server.h b/server.h index 4e594b9..79bb408 100644 --- a/server.h +++ b/server.h @@ -13,12 +13,14 @@ struct keybinding_list; struct wlr_output_layout; struct wlr_idle_inhibit_manager_v1; struct cg_output_config; +struct cg_input_manager; struct cg_server { struct wl_display *wl_display; struct wl_event_loop *event_loop; struct cg_seat *seat; + struct cg_input_manager *input; struct wlr_backend *backend; struct wlr_idle *idle; struct wlr_idle_inhibit_manager_v1 *idle_inhibit_v1; @@ -39,6 +41,7 @@ struct cg_server { struct keybinding_list *keybindings; struct wl_list output_config; + struct wl_list input_config; enum wl_output_transform output_transform;