diff options
Diffstat (limited to 'rootston')
-rw-r--r-- | rootston/config.c | 6 | ||||
-rw-r--r-- | rootston/cursor.c | 2 | ||||
-rw-r--r-- | rootston/desktop.c | 29 | ||||
-rw-r--r-- | rootston/input.c | 129 | ||||
-rw-r--r-- | rootston/keyboard.c | 125 | ||||
-rw-r--r-- | rootston/meson.build | 7 | ||||
-rw-r--r-- | rootston/output.c | 32 | ||||
-rw-r--r-- | rootston/pointer.c | 23 | ||||
-rw-r--r-- | rootston/roots_cursor.c | 366 | ||||
-rw-r--r-- | rootston/seat.c | 607 | ||||
-rw-r--r-- | rootston/tablet_tool.c | 24 | ||||
-rw-r--r-- | rootston/touch.c | 26 | ||||
-rw-r--r-- | rootston/wl_shell.c | 13 | ||||
-rw-r--r-- | rootston/xcursor.c | 16 | ||||
-rw-r--r-- | rootston/xdg_shell_v6.c | 15 | ||||
-rw-r--r-- | rootston/xwayland.c | 38 |
16 files changed, 1157 insertions, 301 deletions
diff --git a/rootston/config.c b/rootston/config.c index dc7a4b1d..815040bf 100644 --- a/rootston/config.c +++ b/rootston/config.c @@ -13,6 +13,7 @@ #include <wlr/types/wlr_box.h> #include "rootston/config.h" #include "rootston/input.h" +#include "rootston/keyboard.h" #include "rootston/ini.h" static void usage(const char *name, int ret) { @@ -288,6 +289,7 @@ static int config_ini_handler(void *user, const char *section, const char *name, if (!found) { dc = calloc(1, sizeof(struct device_config)); dc->name = strdup(device_name); + dc->seat = strdup("seat0"); wl_list_insert(&config->devices, &dc->link); } @@ -297,6 +299,9 @@ static int config_ini_handler(void *user, const char *section, const char *name, } else if (strcmp(name, "geometry") == 0) { free(dc->mapped_box); dc->mapped_box = parse_geometry(value); + } else if (strcmp(name, "seat") == 0) { + free(dc->seat); + dc->seat = strdup(value); } else { wlr_log(L_ERROR, "got unknown device config: %s", name); } @@ -386,6 +391,7 @@ void roots_config_destroy(struct roots_config *config) { struct device_config *dc, *dtmp = NULL; wl_list_for_each_safe(dc, dtmp, &config->devices, link) { free(dc->name); + free(dc->seat); free(dc->mapped_output); free(dc->mapped_box); free(dc); diff --git a/rootston/cursor.c b/rootston/cursor.c index b153e8c8..64277464 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -18,6 +18,8 @@ #include "rootston/input.h" #include "rootston/desktop.h" #include "rootston/view.h" +#include "rootston/keyboard.h" +#include "rootston/seat.h" const struct roots_input_event *get_input_event(struct roots_input *input, uint32_t serial) { diff --git a/rootston/desktop.c b/rootston/desktop.c index b36a6932..c5242599 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -14,15 +14,19 @@ #include <wlr/util/log.h> #include <server-decoration-protocol.h> #include "rootston/server.h" -#include "rootston/server.h" +#include "rootston/seat.h" +// TODO replace me with a signal void view_destroy(struct roots_view *view) { struct roots_desktop *desktop = view->desktop; struct roots_input *input = desktop->server->input; - if (input->active_view == view) { - input->active_view = NULL; - input->mode = ROOTS_CURSOR_PASSTHROUGH; + struct roots_seat *seat; + wl_list_for_each(seat, &input->seats, link) { + if (seat->focus == view) { + seat->focus = NULL; + seat->cursor->mode = ROOTS_CURSOR_PASSTHROUGH; + } } for (size_t i = 0; i < desktop->views->length; ++i) { @@ -89,15 +93,9 @@ bool view_center(struct roots_view *view) { view_get_size(view, &size); struct roots_desktop *desktop = view->desktop; - struct wlr_cursor *cursor = desktop->server->input->cursor; struct wlr_output *output = - wlr_output_layout_output_at(desktop->layout, cursor->x, cursor->y); - - if (!output) { - output = wlr_output_layout_get_center_output(desktop->layout); - } - + wlr_output_layout_get_center_output(desktop->layout); if (!output) { // empty layout return false; @@ -121,10 +119,16 @@ void view_setup(struct roots_view *view) { view_center(view); struct roots_input *input = view->desktop->server->input; - set_view_focus(input, view->desktop, view); + // TODO what seat gets focus? the one with the last input event? + struct roots_seat *seat; + wl_list_for_each(seat, &input->seats, link) { + roots_seat_focus_view(seat, view); + } } void view_teardown(struct roots_view *view) { + // TODO replace me with a signal + /* struct wlr_list *views = view->desktop->views; if (views->length < 2 || views->items[views->length-1] != view) { return; @@ -133,6 +137,7 @@ void view_teardown(struct roots_view *view) { struct roots_view *prev_view = views->items[views->length-2]; struct roots_input *input = prev_view->desktop->server->input; set_view_focus(input, prev_view->desktop, prev_view); + */ } struct roots_view *view_at(struct roots_desktop *desktop, double lx, double ly, diff --git a/rootston/input.c b/rootston/input.c index 5d367a5e..92148b65 100644 --- a/rootston/input.c +++ b/rootston/input.c @@ -8,6 +8,8 @@ #include "rootston/server.h" #include "rootston/config.h" #include "rootston/input.h" +#include "rootston/keyboard.h" +#include "rootston/seat.h" static const char *device_type(enum wlr_input_device_type type) { switch (type) { @@ -25,47 +27,47 @@ static const char *device_type(enum wlr_input_device_type type) { return NULL; } +static struct roots_seat *input_get_seat(struct roots_input *input, char *name) { + struct roots_seat *seat = NULL; + wl_list_for_each(seat, &input->seats, link) { + if (strcmp(seat->seat->name, name) == 0) { + return seat; + } + } + + seat = roots_seat_create(input, name); + return seat; +} + static void input_add_notify(struct wl_listener *listener, void *data) { struct wlr_input_device *device = data; struct roots_input *input = wl_container_of(listener, input, input_add); - wlr_log(L_DEBUG, "New input device: %s (%d:%d) %s", device->name, - device->vendor, device->product, device_type(device->type)); - switch (device->type) { - case WLR_INPUT_DEVICE_KEYBOARD: - keyboard_add(device, input); - break; - case WLR_INPUT_DEVICE_POINTER: - pointer_add(device, input); - break; - case WLR_INPUT_DEVICE_TOUCH: - touch_add(device, input); - break; - case WLR_INPUT_DEVICE_TABLET_TOOL: - tablet_tool_add(device, input); - break; - default: - break; + + char *seat_name = "seat0"; + struct device_config *dc = config_get_device(input->config, device); + if (dc) { + seat_name = dc->seat; } + + struct roots_seat *seat = input_get_seat(input, seat_name); + if (!seat) { + wlr_log(L_ERROR, "could not create roots seat"); + return; + } + + wlr_log(L_DEBUG, "New input device: %s (%d:%d) %s seat:%s", device->name, + device->vendor, device->product, device_type(device->type), seat_name); + + roots_seat_add_device(seat, device); } static void input_remove_notify(struct wl_listener *listener, void *data) { struct wlr_input_device *device = data; struct roots_input *input = wl_container_of(listener, input, input_remove); - switch (device->type) { - case WLR_INPUT_DEVICE_KEYBOARD: - keyboard_remove(device, input); - break; - case WLR_INPUT_DEVICE_POINTER: - pointer_remove(device, input); - break; - case WLR_INPUT_DEVICE_TOUCH: - touch_remove(device, input); - break; - case WLR_INPUT_DEVICE_TABLET_TOOL: - tablet_tool_remove(device, input); - break; - default: - break; + + struct roots_seat *seat; + wl_list_for_each(seat, &input->seats, link) { + roots_seat_remove_device(seat, device); } } @@ -82,66 +84,27 @@ struct roots_input *input_create(struct roots_server *server, input->config = config; input->server = server; - input->xcursor_theme = wlr_xcursor_theme_load("default", 16); - if (input->xcursor_theme == NULL) { - wlr_log(L_ERROR, "Cannot load xcursor theme"); - free(input); - return NULL; - } - - struct wlr_xcursor *xcursor = get_default_xcursor(input->xcursor_theme); - if (xcursor == NULL) { - wlr_log(L_ERROR, "Cannot load xcursor from theme"); - wlr_xcursor_theme_destroy(input->xcursor_theme); - free(input); - return NULL; - } - - if (server->desktop->xwayland != NULL) { - struct wlr_xcursor_image *xcursor_image = xcursor->images[0]; - wlr_xwayland_set_cursor(server->desktop->xwayland, - xcursor_image->buffer, xcursor_image->width, xcursor_image->width, - xcursor_image->height, xcursor_image->hotspot_x, - xcursor_image->hotspot_y); - } - - input->wl_seat = wlr_seat_create(server->wl_display, "seat0"); - if (input->wl_seat == NULL) { - wlr_log(L_ERROR, "Cannot create seat"); - wlr_xcursor_theme_destroy(input->xcursor_theme); - free(input); - return NULL; - } - wlr_seat_set_capabilities(input->wl_seat, WL_SEAT_CAPABILITY_KEYBOARD - | WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_TOUCH); - - wl_list_init(&input->keyboards); - wl_list_init(&input->pointers); - wl_list_init(&input->touch); - wl_list_init(&input->tablet_tools); + wl_list_init(&input->seats); input->input_add.notify = input_add_notify; wl_signal_add(&server->backend->events.input_add, &input->input_add); input->input_remove.notify = input_remove_notify; wl_signal_add(&server->backend->events.input_remove, &input->input_remove); - input->cursor = wlr_cursor_create(); - cursor_initialize(input); - - struct wlr_xcursor_image *image = xcursor->images[0]; - wlr_cursor_set_image(input->cursor, image->buffer, image->width, - image->width, image->height, image->hotspot_x, image->hotspot_y); - - wlr_cursor_attach_output_layout(input->cursor, server->desktop->layout); - wlr_cursor_map_to_region(input->cursor, config->cursor.mapped_box); - cursor_load_config(config, input->cursor, - input, server->desktop); - - wl_list_init(&input->drag_icons); - return input; } void input_destroy(struct roots_input *input) { // TODO } + +struct roots_seat *input_seat_from_wlr_seat(struct roots_input *input, + struct wlr_seat *wlr_seat) { + struct roots_seat *seat = NULL; + wl_list_for_each(seat, &input->seats, link) { + if (seat->seat == wlr_seat) { + return seat; + } + } + return seat; +} diff --git a/rootston/keyboard.c b/rootston/keyboard.c index bde912aa..9692ea7d 100644 --- a/rootston/keyboard.c +++ b/rootston/keyboard.c @@ -10,6 +10,8 @@ #include <wlr/util/log.h> #include <xkbcommon/xkbcommon.h> #include "rootston/input.h" +#include "rootston/seat.h" +#include "rootston/keyboard.h" static ssize_t keyboard_pressed_keysym_index(struct roots_keyboard *keyboard, xkb_keysym_t keysym) { @@ -37,7 +39,7 @@ static void keyboard_binding_execute(struct roots_keyboard *keyboard, } else if (strcmp(command, "next_window") == 0) { if (server->desktop->views->length > 0) { struct roots_view *view = server->desktop->views->items[0]; - set_view_focus(keyboard->input, server->desktop, view); + roots_seat_focus_view(keyboard->seat, view); } } else if (strncmp(exec_prefix, command, strlen(exec_prefix)) == 0) { const char *shell_cmd = command + strlen(exec_prefix); @@ -60,7 +62,7 @@ static void keyboard_binding_execute(struct roots_keyboard *keyboard, * should be propagated to clients. */ static bool keyboard_keysym_press(struct roots_keyboard *keyboard, - xkb_keysym_t keysym, uint32_t modifiers) { + xkb_keysym_t keysym) { ssize_t i = keyboard_pressed_keysym_index(keyboard, keysym); if (i < 0) { i = keyboard_pressed_keysym_index(keyboard, XKB_KEY_NoSymbol); @@ -84,10 +86,11 @@ static bool keyboard_keysym_press(struct roots_keyboard *keyboard, } if (keysym == XKB_KEY_Escape) { - wlr_seat_pointer_end_grab(keyboard->input->wl_seat); - wlr_seat_keyboard_end_grab(keyboard->input->wl_seat); + wlr_seat_pointer_end_grab(keyboard->seat->seat); + wlr_seat_keyboard_end_grab(keyboard->seat->seat); } + uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard); struct wl_list *bindings = &keyboard->input->server->config->bindings; struct binding_config *bc; wl_list_for_each(bc, bindings, link) { @@ -120,29 +123,31 @@ static void keyboard_keysym_release(struct roots_keyboard *keyboard, keyboard->pressed_keysyms[i] = XKB_KEY_NoSymbol; } } - /* - * Process keypresses from the keyboard as if modifiers didn't change keysyms. + * Process keypresses from the keyboard as xkb sees them. * - * This avoids the xkb keysym translation based on modifiers considered pressed - * in the state and uses the list of modifiers saved on the rootston side. + * This uses the xkb keysyms translation based on pressed modifiers and clears + * the consumed modifiers from the list of modifiers passed to keybind + * detection. * - * This will trigger the keybind: [Alt]+[Shift]+2 + * (On US layout) this will trigger: [Alt]+[at] */ -static bool keyboard_keysyms_simple(struct roots_keyboard *keyboard, +static bool keyboard_keysyms_xkb(struct roots_keyboard *keyboard, uint32_t keycode, enum wlr_key_state state) { uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard); const xkb_keysym_t *syms; - xkb_layout_index_t layout_index = xkb_state_key_get_layout( - keyboard->device->keyboard->xkb_state, keycode); - int syms_len = xkb_keymap_key_get_syms_by_level( - keyboard->device->keyboard->keymap, keycode, layout_index, 0, &syms); + int syms_len = xkb_state_key_get_syms(keyboard->device->keyboard->xkb_state, + keycode, &syms); + uint32_t consumed = xkb_state_key_get_consumed_mods2( + keyboard->device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); + + modifiers = modifiers & ~consumed; bool handled = false; for (int i = 0; i < syms_len; i++) { if (state) { - bool keysym_handled = keyboard_keysym_press(keyboard, - syms[i], modifiers); + bool keysym_handled = + keyboard_keysym_press(keyboard, syms[i]); handled = handled || keysym_handled; } else { // WLR_KEY_RELEASED keyboard_keysym_release(keyboard, syms[i]); @@ -151,32 +156,26 @@ static bool keyboard_keysyms_simple(struct roots_keyboard *keyboard, return handled; } - /* - * Process keypresses from the keyboard as xkb sees them. + * Process keypresses from the keyboard as if modifiers didn't change keysyms. * - * This uses the xkb keysyms translation based on pressed modifiers and clears - * the consumed modifiers from the list of modifiers passed to keybind - * detection. + * This avoids the xkb keysym translation based on modifiers considered pressed + * in the state and uses the list of modifiers saved on the rootston side. * - * (On US layout) this will trigger: [Alt]+[at] + * This will trigger the keybind: [Alt]+[Shift]+2 */ -static bool keyboard_keysyms_xkb(struct roots_keyboard *keyboard, +static bool keyboard_keysyms_simple(struct roots_keyboard *keyboard, uint32_t keycode, enum wlr_key_state state) { - uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard); const xkb_keysym_t *syms; - int syms_len = xkb_state_key_get_syms(keyboard->device->keyboard->xkb_state, - keycode, &syms); - uint32_t consumed = xkb_state_key_get_consumed_mods2( - keyboard->device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); - - modifiers = modifiers & ~consumed; + xkb_layout_index_t layout_index = xkb_state_key_get_layout( + keyboard->device->keyboard->xkb_state, keycode); + int syms_len = xkb_keymap_key_get_syms_by_level( + keyboard->device->keyboard->keymap, keycode, layout_index, 0, &syms); bool handled = false; for (int i = 0; i < syms_len; i++) { if (state) { - bool keysym_handled = keyboard_keysym_press(keyboard, - syms[i], modifiers); + bool keysym_handled = keyboard_keysym_press(keyboard, syms[i]); handled = handled || keysym_handled; } else { // WLR_KEY_RELEASED keyboard_keysym_release(keyboard, syms[i]); @@ -186,10 +185,8 @@ static bool keyboard_keysyms_xkb(struct roots_keyboard *keyboard, return handled; } -static void keyboard_key_notify(struct wl_listener *listener, void *data) { - struct wlr_event_keyboard_key *event = data; - struct roots_keyboard *keyboard = wl_container_of(listener, keyboard, key); - +void roots_keyboard_handle_key(struct roots_keyboard *keyboard, + struct wlr_event_keyboard_key *event) { uint32_t keycode = event->keycode + 8; bool handled = keyboard_keysyms_xkb(keyboard, keycode, event->state); @@ -201,17 +198,16 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) { } if (!handled) { - wlr_seat_set_keyboard(keyboard->input->wl_seat, keyboard->device); - wlr_seat_keyboard_notify_key(keyboard->input->wl_seat, event->time_msec, + wlr_seat_set_keyboard(keyboard->seat->seat, keyboard->device); + wlr_seat_keyboard_notify_key(keyboard->seat->seat, event->time_msec, event->keycode, event->state); } } -static void keyboard_modifiers_notify(struct wl_listener *listener, void *data) { - struct roots_keyboard *keyboard = - wl_container_of(listener, keyboard, modifiers); - struct wlr_seat *seat = keyboard->input->wl_seat; - wlr_seat_set_keyboard(seat, keyboard->device); +void roots_keyboard_handle_modifiers(struct roots_keyboard *r_keyboard, + struct wlr_event_keyboard_modifiers *event) { + struct wlr_seat *seat = r_keyboard->seat->seat; + wlr_seat_set_keyboard(seat, r_keyboard->device); wlr_seat_keyboard_notify_modifiers(seat); } @@ -237,27 +233,19 @@ static void keyboard_config_merge(struct keyboard_config *config, } } -void keyboard_add(struct wlr_input_device *device, struct roots_input *input) { +struct roots_keyboard *roots_keyboard_create(struct wlr_input_device *device, + struct roots_input *input) { struct roots_keyboard *keyboard = calloc(sizeof(struct roots_keyboard), 1); if (keyboard == NULL) { - return; + return NULL; } device->data = keyboard; keyboard->device = device; keyboard->input = input; - keyboard->key.notify = keyboard_key_notify; - wl_signal_add(&device->keyboard->events.key, &keyboard->key); - - keyboard->modifiers.notify = keyboard_modifiers_notify; - wl_signal_add(&device->keyboard->events.modifiers, &keyboard->modifiers); - - wl_list_insert(&input->keyboards, &keyboard->link); - - struct keyboard_config config; - memset(&config, 0, sizeof(config)); - keyboard_config_merge(&config, config_get_keyboard(input->config, device)); - keyboard_config_merge(&config, config_get_keyboard(input->config, NULL)); + struct keyboard_config *config = calloc(1, sizeof(struct keyboard_config)); + keyboard_config_merge(config, config_get_keyboard(input->config, device)); + keyboard_config_merge(config, config_get_keyboard(input->config, NULL)); struct keyboard_config env_config = { .rules = getenv("XKB_DEFAULT_RULES"), @@ -266,29 +254,30 @@ void keyboard_add(struct wlr_input_device *device, struct roots_input *input) { .variant = getenv("XKB_DEFAULT_VARIANT"), .options = getenv("XKB_DEFAULT_OPTIONS"), }; - keyboard_config_merge(&config, &env_config); + keyboard_config_merge(config, &env_config); + keyboard->config = config; struct xkb_rule_names rules; memset(&rules, 0, sizeof(rules)); - rules.rules = config.rules; - rules.model = config.model; - rules.layout = config.layout; - rules.variant = config.variant; - rules.options = config.options; + rules.rules = config->rules; + rules.model = config->model; + rules.layout = config->layout; + rules.variant = config->variant; + rules.options = config->options; struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (context == NULL) { wlr_log(L_ERROR, "Cannot create XKB context"); - return; + return NULL; } wlr_keyboard_set_keymap(device->keyboard, xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS)); xkb_context_unref(context); + + return keyboard; } -void keyboard_remove(struct wlr_input_device *device, struct roots_input *input) { - struct roots_keyboard *keyboard = device->data; - wl_list_remove(&keyboard->key.link); - wl_list_remove(&keyboard->modifiers.link); +void roots_keyboard_destroy(struct roots_keyboard *keyboard) { wl_list_remove(&keyboard->link); + free(keyboard->config); free(keyboard); } diff --git a/rootston/meson.build b/rootston/meson.build index d84422d8..062f56fc 100644 --- a/rootston/meson.build +++ b/rootston/meson.build @@ -1,15 +1,14 @@ sources = [ 'config.c', - 'cursor.c', + #'cursor.c', + 'roots_cursor.c', 'desktop.c', 'ini.c', 'input.c', 'keyboard.c', 'main.c', 'output.c', - 'pointer.c', - 'tablet_tool.c', - 'touch.c', + 'seat.c', 'xcursor.c', 'xdg_shell_v6.c', 'wl_shell.c', diff --git a/rootston/output.c b/rootston/output.c index baa7b6cc..329c29be 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -151,15 +151,18 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { } struct roots_drag_icon *drag_icon = NULL; - wl_list_for_each(drag_icon, &server->input->drag_icons, link) { - if (!drag_icon->mapped) { - continue; + struct roots_seat *seat = NULL; + wl_list_for_each(seat, &server->input->seats, link) { + wl_list_for_each(drag_icon, &seat->drag_icons, link) { + if (!drag_icon->mapped) { + continue; + } + struct wlr_surface *icon = drag_icon->surface; + struct wlr_cursor *cursor = seat->cursor->cursor; + double icon_x = cursor->x + drag_icon->sx; + double icon_y = cursor->y + drag_icon->sy; + render_surface(icon, desktop, wlr_output, &now, icon_x, icon_y, 0); } - struct wlr_surface *icon = drag_icon->surface; - struct wlr_cursor *cursor = server->input->cursor; - double icon_x = cursor->x + drag_icon->sx; - double icon_y = cursor->y + drag_icon->sy; - render_surface(icon, desktop, wlr_output, &now, icon_x, icon_y, 0); } wlr_renderer_end(server->renderer); @@ -224,14 +227,11 @@ void output_add_notify(struct wl_listener *listener, void *data) { wlr_output_layout_add_auto(desktop->layout, wlr_output); } - cursor_load_config(config, input->cursor, input, desktop); - - struct wlr_xcursor *xcursor = get_default_xcursor(input->xcursor_theme); - struct wlr_xcursor_image *image = xcursor->images[0]; - wlr_cursor_set_image(input->cursor, image->buffer, image->width, - image->width, image->height, image->hotspot_x, image->hotspot_y); - - wlr_cursor_warp(input->cursor, NULL, input->cursor->x, input->cursor->y); + struct roots_seat *seat; + wl_list_for_each(seat, &input->seats, link) { + roots_seat_configure_cursor(seat); + roots_seat_configure_xcursor(seat); + } } void output_remove_notify(struct wl_listener *listener, void *data) { diff --git a/rootston/pointer.c b/rootston/pointer.c deleted file mode 100644 index 299ecdfc..00000000 --- a/rootston/pointer.c +++ /dev/null @@ -1,23 +0,0 @@ -#include <stdlib.h> -#include <wayland-server.h> -#include <wlr/types/wlr_input_device.h> -#include <wlr/types/wlr_pointer.h> -#include "rootston/input.h" - -void pointer_add(struct wlr_input_device *device, struct roots_input *input) { - struct roots_pointer *pointer = calloc(sizeof(struct roots_pointer), 1); - device->data = pointer; - pointer->device = device; - pointer->input = input; - wl_list_insert(&input->pointers, &pointer->link); - wlr_cursor_attach_input_device(input->cursor, device); - cursor_load_config(input->server->config, input->cursor, - input, input->server->desktop); -} - -void pointer_remove(struct wlr_input_device *device, struct roots_input *input) { - struct roots_pointer *pointer = device->data; - wlr_cursor_detach_input_device(input->cursor, device); - wl_list_remove(&pointer->link); - free(pointer); -} diff --git a/rootston/roots_cursor.c b/rootston/roots_cursor.c new file mode 100644 index 00000000..c8abe098 --- /dev/null +++ b/rootston/roots_cursor.c @@ -0,0 +1,366 @@ +#define _XOPEN_SOURCE 700 +#include <stdlib.h> +#include <math.h> +#ifdef __linux__ +#include <linux/input-event-codes.h> +#elif __FreeBSD__ +#include <dev/evdev/input-event-codes.h> +#endif +#include <wlr/util/log.h> +#include "rootston/xcursor.h" +#include "rootston/cursor.h" + +struct roots_cursor *roots_cursor_create(struct roots_seat *seat) { + struct roots_cursor *cursor = calloc(1, sizeof(struct roots_cursor)); + if (!cursor) { + return NULL; + } + cursor->cursor = wlr_cursor_create(); + if (!cursor->cursor) { + return NULL; + } + + return cursor; +} + +void roots_cursor_destroy(struct roots_cursor *cursor) { + // TODO +} + +static void cursor_set_xcursor_image(struct roots_cursor *cursor, + struct wlr_xcursor_image *image) { + wlr_cursor_set_image(cursor->cursor, image->buffer, image->width, + image->width, image->height, image->hotspot_x, image->hotspot_y); +} + +static void roots_cursor_update_position(struct roots_cursor *cursor, uint32_t time) { + struct roots_desktop *desktop = cursor->seat->input->server->desktop; + struct roots_seat *seat = cursor->seat; + struct roots_view *view; + struct wlr_surface *surface; + double sx, sy; + switch (cursor->mode) { + case ROOTS_CURSOR_PASSTHROUGH: + view = view_at(desktop, cursor->cursor->x, cursor->cursor->y, + &surface, &sx, &sy); + bool set_compositor_cursor = !view && cursor->cursor_client; + if (view) { + struct wl_client *view_client = + wl_resource_get_client(view->wlr_surface->resource); + set_compositor_cursor = view_client != cursor->cursor_client; + } + if (set_compositor_cursor) { + struct wlr_xcursor *xcursor = get_default_xcursor(cursor->xcursor_theme); + cursor_set_xcursor_image(cursor, xcursor->images[0]); + cursor->cursor_client = NULL; + } + if (view) { + wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy); + wlr_seat_pointer_notify_motion(seat->seat, time, sx, sy); + } else { + wlr_seat_pointer_clear_focus(seat->seat); + } + break; + case ROOTS_CURSOR_MOVE: + if (seat->focus) { + double dx = cursor->cursor->x - cursor->offs_x; + double dy = cursor->cursor->y - cursor->offs_y; + view_move(seat->focus, cursor->view_x + dx, + cursor->view_y + dy); + } + break; + case ROOTS_CURSOR_RESIZE: + if (seat->focus) { + double dx = cursor->cursor->x - cursor->offs_x; + double dy = cursor->cursor->y - cursor->offs_y; + double active_x = seat->focus->x; + double active_y = seat->focus->y; + int width = cursor->view_width; + int height = cursor->view_height; + if (cursor->resize_edges & ROOTS_CURSOR_RESIZE_EDGE_TOP) { + active_y = cursor->view_y + dy; + height -= dy; + if (height < 0) { + active_y += height; + } + } else if (cursor->resize_edges & ROOTS_CURSOR_RESIZE_EDGE_BOTTOM) { + height += dy; + } + if (cursor->resize_edges & ROOTS_CURSOR_RESIZE_EDGE_LEFT) { + active_x = cursor->view_x + dx; + width -= dx; + if (width < 0) { + active_x += width; + } + } else if (cursor->resize_edges & ROOTS_CURSOR_RESIZE_EDGE_RIGHT) { + width += dx; + } + + if (width < 0) { + width = 0; + } + if (height < 0) { + height = 0; + } + + if (active_x != seat->focus->x || + active_y != seat->focus->y) { + view_move_resize(seat->focus, active_x, active_y, + width, height); + } else { + view_resize(seat->focus, width, height); + } + } + break; + case ROOTS_CURSOR_ROTATE: + if (seat->focus) { + struct roots_view *view = seat->focus; + int ox = view->x + view->wlr_surface->current->width/2, + oy = view->y + view->wlr_surface->current->height/2; + int ux = cursor->offs_x - ox, + uy = cursor->offs_y - oy; + int vx = cursor->cursor->x - ox, + vy = cursor->cursor->y - oy; + float angle = atan2(vx*uy - vy*ux, vx*ux + vy*uy); + int steps = 12; + angle = round(angle/M_PI*steps) / (steps/M_PI); + view->rotation = cursor->view_rotation + angle; + } + break; + } + +} + +static void roots_cursor_press_button(struct roots_cursor *cursor, + struct wlr_input_device *device, uint32_t time, uint32_t button, + uint32_t state) { + struct roots_seat *seat = cursor->seat; + struct roots_desktop *desktop = seat->input->server->desktop; + struct wlr_surface *surface; + double sx, sy; + struct roots_view *view = view_at(desktop, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + + if (state == WLR_BUTTON_PRESSED && view && roots_seat_has_meta_pressed(seat)) { + roots_seat_focus_view(seat, view); + + uint32_t edges; + switch (button) { + case BTN_LEFT: + roots_seat_begin_move(seat, view); + break; + case BTN_RIGHT: + edges = 0; + if (sx < view->wlr_surface->current->width/2) { + edges |= ROOTS_CURSOR_RESIZE_EDGE_LEFT; + } else { + edges |= ROOTS_CURSOR_RESIZE_EDGE_RIGHT; + } + if (sy < view->wlr_surface->current->height/2) { + edges |= ROOTS_CURSOR_RESIZE_EDGE_TOP; + } else { + edges |= ROOTS_CURSOR_RESIZE_EDGE_BOTTOM; + } + roots_seat_begin_resize(seat, view, edges); + break; + case BTN_MIDDLE: + roots_seat_begin_rotate(seat, view); + break; + } + return; + } + + uint32_t serial = + wlr_seat_pointer_notify_button(seat->seat, time, button, state); + + int i; + switch (state) { + case WLR_BUTTON_RELEASED: + seat->cursor->mode = ROOTS_CURSOR_PASSTHROUGH; + roots_cursor_update_position(cursor, time); + break; + case WLR_BUTTON_PRESSED: + i = cursor->input_events_idx; + cursor->input_events[i].serial = serial; + cursor->input_events[i].cursor = cursor->cursor; + cursor->input_events[i].device = device; + cursor->input_events_idx = (i + 1) + % (sizeof(cursor->input_events) / sizeof(cursor->input_events[0])); + roots_seat_focus_view(seat, view); + break; + } +} + +void roots_cursor_handle_motion(struct roots_cursor *cursor, + struct wlr_event_pointer_motion *event) { + wlr_cursor_move(cursor->cursor, event->device, + event->delta_x, event->delta_y); + roots_cursor_update_position(cursor, event->time_msec); +} + +void roots_cursor_handle_motion_absolute(struct roots_cursor *cursor, + struct wlr_event_pointer_motion_absolute *event) { + wlr_cursor_warp_absolute(cursor->cursor, event->device, + event->x_mm / event->width_mm, event->y_mm / event->height_mm); + roots_cursor_update_position(cursor, event->time_msec); +} + +void roots_cursor_handle_button(struct roots_cursor *cursor, + struct wlr_event_pointer_button *event) { + roots_cursor_press_button(cursor, event->device, event->time_msec, + event->button, event->state); +} + +void roots_cursor_handle_axis(struct roots_cursor *cursor, + struct wlr_event_pointer_axis *event) { + wlr_seat_pointer_notify_axis(cursor->seat->seat, event->time_msec, + event->orientation, event->delta); +} + +void roots_cursor_handle_touch_down(struct roots_cursor *cursor, + struct wlr_event_touch_down *event) { + struct roots_touch_point *point = + calloc(1, sizeof(struct roots_touch_point)); + if (!point) { + wlr_log(L_ERROR, "could not allocate memory for touch point"); + return; + } + + point->device = event->device->data; + point->slot = event->slot; + point->x = event->x_mm / event->width_mm; + point->y = event->y_mm / event->height_mm; + wlr_cursor_warp_absolute(cursor->cursor, event->device, point->x, point->y); + roots_cursor_update_position(cursor, event->time_msec); + wl_list_insert(&cursor->touch_points, &point->link); + roots_cursor_press_button(cursor, event->device, + event->time_msec, BTN_LEFT, 1); +} + +void roots_cursor_handle_touch_up(struct roots_cursor *cursor, + struct wlr_event_touch_up *event) { + struct roots_touch_point *point; + wl_list_for_each(point, &cursor->touch_points, link) { + if (point->slot == event->slot) { + wl_list_remove(&point->link); + free(point); + break; + } + } + roots_cursor_press_button(cursor, event->device, + event->time_msec, BTN_LEFT, 0); +} + +void roots_cursor_handle_touch_motion(struct roots_cursor *cursor, + struct wlr_event_touch_motion *event) { + struct roots_touch_point *point; + wl_list_for_each(point, &cursor->touch_points, link) { + if (point->slot == event->slot) { + point->x = event->x_mm / event->width_mm; + point->y = event->y_mm / event->height_mm; + wlr_cursor_warp_absolute(cursor->cursor, event->device, + point->x, point->y); + roots_cursor_update_position(cursor, event->time_msec); + break; + } + } +} + +void roots_cursor_handle_tool_axis(struct roots_cursor *cursor, + struct wlr_event_tablet_tool_axis *event) { + if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) && + (event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { + wlr_cursor_warp_absolute(cursor->cursor, event->device, + event->x_mm / event->width_mm, event->y_mm / event->height_mm); + roots_cursor_update_position(cursor, event->time_msec); + } +} + +void roots_cursor_handle_tool_tip(struct roots_cursor *cursor, + struct wlr_event_tablet_tool_tip *event) { + roots_cursor_press_button(cursor, event->device, + event->time_msec, BTN_LEFT, event->state); +} + +void roots_cursor_handle_request_set_cursor(struct roots_cursor *cursor, + struct wlr_seat_pointer_request_set_cursor_event *event) { + struct wlr_surface *focused_surface = + event->seat_client->seat->pointer_state.focused_surface; + bool has_focused = focused_surface != NULL && focused_surface->resource != NULL; + struct wl_client *focused_client = NULL; + if (has_focused) { + focused_client = wl_resource_get_client(focused_surface->resource); + } + if (event->seat_client->client != focused_client || + cursor->mode != ROOTS_CURSOR_PASSTHROUGH) { + wlr_log(L_DEBUG, "Denying request to set cursor from unfocused client"); + return; + } + + wlr_log(L_DEBUG, "Setting client cursor"); + wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x, + event->hotspot_y); + cursor->cursor_client = event->seat_client->client; +} + +static void handle_drag_icon_commit(struct wl_listener *listener, void *data) { + struct roots_drag_icon *drag_icon = + wl_container_of(listener, drag_icon, surface_commit); + drag_icon->sx += drag_icon->surface->current->sx; + drag_icon->sy += drag_icon->surface->current->sy; +} + +static void handle_drag_icon_destroy(struct wl_listener *listener, void *data) { + struct roots_drag_icon *drag_icon = + wl_container_of(listener, drag_icon, surface_destroy); + wl_list_remove(&drag_icon->link); + wl_list_remove(&drag_icon->surface_destroy.link); + wl_list_remove(&drag_icon->surface_commit.link); + free(drag_icon); +} + +void roots_cursor_handle_pointer_grab_begin(struct roots_cursor *cursor, + struct wlr_seat_pointer_grab *grab) { + struct roots_seat *seat = cursor->seat; + if (grab->interface == &wlr_data_device_pointer_drag_interface) { + struct wlr_drag *drag = grab->data; + if (drag->icon) { + struct roots_drag_icon *iter_icon; + wl_list_for_each(iter_icon, &seat->drag_icons, link) { + if (iter_icon->surface == drag->icon) { + // already in the list + return; + } + } + + struct roots_drag_icon *drag_icon = + calloc(1, sizeof(struct roots_drag_icon)); + drag_icon->mapped = true; + drag_icon->surface = drag->icon; + wl_list_insert(&seat->drag_icons, &drag_icon->link); + + wl_signal_add(&drag->icon->events.destroy, + &drag_icon->surface_destroy); + drag_icon->surface_destroy.notify = handle_drag_icon_destroy; + + wl_signal_add(&drag->icon->events.commit, + &drag_icon->surface_commit); + drag_icon->surface_commit.notify = handle_drag_icon_commit; + } + } +} + +void roots_cursor_handle_pointer_grab_end(struct roots_cursor *cursor, + struct wlr_seat_pointer_grab *grab) { + if (grab->interface == &wlr_data_device_pointer_drag_interface) { + struct wlr_drag *drag = grab->data; + struct roots_drag_icon *icon; + wl_list_for_each(icon, &cursor->seat->drag_icons, link) { + if (icon->surface == drag->icon) { + icon->mapped = false; + } + } + } + + roots_cursor_update_position(cursor, 0); +} diff --git a/rootston/seat.c b/rootston/seat.c new file mode 100644 index 00000000..ce8ad10b --- /dev/null +++ b/rootston/seat.c @@ -0,0 +1,607 @@ +#include <wayland-server.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include <wlr/util/log.h> + +#include "rootston/xcursor.h" +#include "rootston/input.h" +#include "rootston/seat.h" +#include "rootston/keyboard.h" +#include "rootston/cursor.h" + +static void handle_keyboard_key(struct wl_listener *listener, void *data) { + struct roots_keyboard *keyboard = + wl_container_of(listener, keyboard, keyboard_key); + struct wlr_event_keyboard_key *event = data; + roots_keyboard_handle_key(keyboard, event); +} + +static void handle_keyboard_modifiers(struct wl_listener *listener, + void *data) { + struct roots_keyboard *keyboard = + wl_container_of(listener, keyboard, keyboard_modifiers); + struct wlr_event_keyboard_modifiers *event = data; + roots_keyboard_handle_modifiers(keyboard, event); +} + +static void handle_cursor_motion(struct wl_listener *listener, void *data) { + struct roots_cursor *cursor = + wl_container_of(listener, cursor, motion); + struct wlr_event_pointer_motion *event = data; + roots_cursor_handle_motion(cursor, event); +} + +static void handle_cursor_motion_absolute(struct wl_listener *listener, void *data) { + struct roots_cursor *cursor = + wl_container_of(listener, cursor, motion_absolute); + struct wlr_event_pointer_motion_absolute *event = data; + roots_cursor_handle_motion_absolute(cursor, event); +} + +static void handle_cursor_button(struct wl_listener *listener, void *data) { + struct roots_cursor *cursor = + wl_container_of(listener, cursor, button); + struct wlr_event_pointer_button *event = data; + roots_cursor_handle_button(cursor, event); +} + +static void handle_cursor_axis(struct wl_listener *listener, void *data) { + struct roots_cursor *cursor = + wl_container_of(listener, cursor, axis); + struct wlr_event_pointer_axis *event = data; + roots_cursor_handle_axis(cursor, event); +} + +static void handle_touch_down(struct wl_listener *listener, void *data) { + struct roots_cursor *cursor = + wl_container_of(listener, cursor, touch_down); + struct wlr_event_touch_down *event = data; + roots_cursor_handle_touch_down(cursor, event); +} + +static void handle_touch_up(struct wl_listener *listener, void *data) { + struct roots_cursor *cursor = + wl_container_of(listener, cursor, touch_up); + struct wlr_event_touch_up *event = data; + roots_cursor_handle_touch_up(cursor, event); +} + +static void handle_touch_motion(struct wl_listener *listener, void *data) { + struct roots_cursor *cursor = + wl_container_of(listener, cursor, touch_motion); + struct wlr_event_touch_motion *event = data; + roots_cursor_handle_touch_motion(cursor, event); +} + +static void handle_tool_axis(struct wl_listener *listener, void *data) { + struct roots_cursor *cursor = + wl_container_of(listener, cursor, tool_axis); + struct wlr_event_tablet_tool_axis *event = data; + roots_cursor_handle_tool_axis(cursor, event); +} + +static void handle_tool_tip(struct wl_listener *listener, void *data) { + struct roots_cursor *cursor = + wl_container_of(listener, cursor, tool_tip); + struct wlr_event_tablet_tool_tip *event = data; + roots_cursor_handle_tool_tip(cursor, event); +} + +static void handle_request_set_cursor(struct wl_listener *listener, + void *data) { + struct roots_cursor *cursor = + wl_container_of(listener, cursor, request_set_cursor); + struct wlr_seat_pointer_request_set_cursor_event *event = data; + roots_cursor_handle_request_set_cursor(cursor, event); +} + +static void handle_pointer_grab_begin(struct wl_listener *listener, + void *data) { + struct roots_cursor *cursor = + wl_container_of(listener, cursor, pointer_grab_begin); + struct wlr_seat_pointer_grab *grab = data; + roots_cursor_handle_pointer_grab_begin(cursor, grab); +} + +static void handle_pointer_grab_end(struct wl_listener *listener, + void *data) { + struct roots_cursor *cursor = + wl_container_of(listener, cursor, pointer_grab_end); + struct wlr_seat_pointer_grab *grab = data; + roots_cursor_handle_pointer_grab_end(cursor, grab); +} + +static void seat_reset_device_mappings(struct roots_seat *seat, struct wlr_input_device *device) { + struct wlr_cursor *cursor = seat->cursor->cursor; + struct roots_config *config = seat->input->config; + + wlr_cursor_map_input_to_output(cursor, device, NULL); + struct device_config *dconfig; + if ((dconfig = config_get_device(config, device))) { + wlr_cursor_map_input_to_region(cursor, device, dconfig->mapped_box); + } +} + +static void seat_set_device_output_mappings(struct roots_seat *seat, + struct wlr_input_device *device, struct wlr_output *output) { + struct wlr_cursor *cursor = seat->cursor->cursor; + struct roots_config *config = seat->input->config; + struct device_config *dconfig; + dconfig = config_get_device(config, device); + if (dconfig && dconfig->mapped_output && + strcmp(dconfig->mapped_output, output->name) == 0) { + wlr_cursor_map_input_to_output(cursor, device, output); + } +} + +void roots_seat_configure_cursor(struct roots_seat *seat) { + struct roots_config *config = seat->input->config; + struct roots_desktop *desktop = seat->input->server->desktop; + struct wlr_cursor *cursor = seat->cursor->cursor; + + struct roots_pointer *pointer; + struct roots_touch *touch; + struct roots_tablet_tool *tablet_tool; + struct roots_output *output; + + // reset mappings + wlr_cursor_map_to_output(cursor, NULL); + wl_list_for_each(pointer, &seat->pointers, link) { + seat_reset_device_mappings(seat, pointer->device); + } + wl_list_for_each(touch, &seat->touch, link) { + seat_reset_device_mappings(seat, touch->device); + } + wl_list_for_each(tablet_tool, &seat->tablet_tools, link) { + seat_reset_device_mappings(seat, tablet_tool->device); + } + + // configure device to output mappings + const char *mapped_output = config->cursor.mapped_output; + wl_list_for_each(output, &desktop->outputs, link) { + if (mapped_output && strcmp(mapped_output, output->wlr_output->name) == 0) { + wlr_cursor_map_to_output(cursor, output->wlr_output); + } + + wl_list_for_each(pointer, &seat->pointers, link) { + seat_set_device_output_mappings(seat, pointer->device, output->wlr_output); + } + wl_list_for_each(tablet_tool, &seat->tablet_tools, link) { + seat_set_device_output_mappings(seat, tablet_tool->device, output->wlr_output); + } + wl_list_for_each(touch, &seat->touch, link) { + seat_set_device_output_mappings(seat, touch->device, output->wlr_output); + } + } +} + +static void roots_seat_init_cursor(struct roots_seat *seat) { + seat->cursor = roots_cursor_create(seat); + if (!seat->cursor) { + return; + } + seat->cursor->seat = seat; + struct wlr_cursor *wlr_cursor = seat->cursor->cursor; + struct roots_desktop *desktop = seat->input->server->desktop; + wlr_cursor_attach_output_layout(wlr_cursor, desktop->layout); + + seat->cursor->xcursor_theme = wlr_xcursor_theme_load("default", 16); + if (seat->cursor->xcursor_theme == NULL) { + wlr_log(L_ERROR, "Cannot load xcursor theme"); + roots_cursor_destroy(seat->cursor); + seat->cursor = NULL; + return; + } + + struct wlr_xcursor *xcursor = get_default_xcursor(seat->cursor->xcursor_theme); + if (xcursor == NULL) { + wlr_log(L_ERROR, "Cannot load xcursor from theme"); + wlr_xcursor_theme_destroy(seat->cursor->xcursor_theme); + roots_cursor_destroy(seat->cursor); + seat->cursor = NULL; + return; + } + + struct wlr_xcursor_image *image = xcursor->images[0]; + wlr_cursor_set_image(seat->cursor->cursor, image->buffer, image->width, + image->width, image->height, image->hotspot_x, image->hotspot_y); + + // XXX: xwayland will always have the theme of the last created seat + if (seat->input->server->desktop->xwayland != NULL) { + wlr_xwayland_set_cursor(seat->input->server->desktop->xwayland, + image->buffer, image->width, image->width, + image->height, image->hotspot_x, + image->hotspot_y); + } + + wl_list_init(&seat->cursor->touch_points); + + roots_seat_configure_cursor(seat); + + // add input signals + wl_signal_add(&wlr_cursor->events.motion, &seat->cursor->motion); + seat->cursor->motion.notify = handle_cursor_motion; + + wl_signal_add(&wlr_cursor->events.motion_absolute, + &seat->cursor->motion_absolute); + seat->cursor->motion_absolute.notify = handle_cursor_motion_absolute; + + wl_signal_add(&wlr_cursor->events.button, &seat->cursor->button); + seat->cursor->button.notify = handle_cursor_button; + + wl_signal_add(&wlr_cursor->events.axis, &seat->cursor->axis); + seat->cursor->axis.notify = handle_cursor_axis; + + wl_signal_add(&wlr_cursor->events.touch_down, &seat->cursor->touch_down); + seat->cursor->touch_down.notify = handle_touch_down; + + wl_signal_add(&wlr_cursor->events.touch_up, &seat->cursor->touch_up); + seat->cursor->touch_up.notify = handle_touch_up; + + wl_signal_add(&wlr_cursor->events.touch_motion, &seat->cursor->touch_motion); + seat->cursor->touch_motion.notify = handle_touch_motion; + + wl_signal_add(&wlr_cursor->events.tablet_tool_axis, &seat->cursor->tool_axis); + seat->cursor->tool_axis.notify = handle_tool_axis; + + wl_signal_add(&wlr_cursor->events.tablet_tool_tip, &seat->cursor->tool_tip); + seat->cursor->tool_tip.notify = handle_tool_tip; + + wl_signal_add(&seat->seat->events.request_set_cursor, + &seat->cursor->request_set_cursor); + seat->cursor->request_set_cursor.notify = handle_request_set_cursor; + + wl_signal_add(&seat->seat->events.pointer_grab_begin, + &seat->cursor->pointer_grab_begin); + seat->cursor->pointer_grab_begin.notify = handle_pointer_grab_begin; + + wl_signal_add(&seat->seat->events.pointer_grab_end, + &seat->cursor->pointer_grab_end); + seat->cursor->pointer_grab_end.notify = handle_pointer_grab_end; +} + +struct roots_seat *roots_seat_create(struct roots_input *input, char *name) { + struct roots_seat *seat = calloc(1, sizeof(struct roots_seat)); + if (!seat) { + return NULL; + } + + wl_list_init(&seat->keyboards); + wl_list_init(&seat->pointers); + wl_list_init(&seat->touch); + wl_list_init(&seat->tablet_tools); + wl_list_init(&seat->drag_icons); + + seat->input = input; + + seat->seat = wlr_seat_create(input->server->wl_display, name); + if (!seat->seat) { + free(seat); + roots_cursor_destroy(seat->cursor); + return NULL; + } + + roots_seat_init_cursor(seat); + if (!seat->cursor) { + wlr_seat_destroy(seat->seat); + free(seat); + return NULL; + } + + wlr_seat_set_capabilities(seat->seat, + WL_SEAT_CAPABILITY_KEYBOARD | + WL_SEAT_CAPABILITY_POINTER | + WL_SEAT_CAPABILITY_TOUCH); + + wl_list_insert(&input->seats, &seat->link); + + return seat; +} + +void roots_seat_destroy(struct roots_seat *seat) { + // TODO +} + +static void seat_add_keyboard(struct roots_seat *seat, struct wlr_input_device *device) { + assert(device->type == WLR_INPUT_DEVICE_KEYBOARD); + struct roots_keyboard *keyboard = roots_keyboard_create(device, seat->input); + keyboard->seat = seat; + + wl_list_insert(&seat->keyboards, &keyboard->link); + + keyboard->keyboard_key.notify = handle_keyboard_key; + wl_signal_add(&keyboard->device->keyboard->events.key, + &keyboard->keyboard_key); + + keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; + wl_signal_add(&keyboard->device->keyboard->events.modifiers, + &keyboard->keyboard_modifiers); +} + +static void seat_add_pointer(struct roots_seat *seat, struct wlr_input_device *device) { + struct roots_pointer *pointer = calloc(sizeof(struct roots_pointer), 1); + if (!pointer) { + wlr_log(L_ERROR, "could not allocate pointer for seat"); + return; + } + + device->data = pointer; + pointer->device = device; + pointer->seat = seat; + wl_list_insert(&seat->pointers, &pointer->link); + wlr_cursor_attach_input_device(seat->cursor->cursor, device); + roots_seat_configure_cursor(seat); +} + +static void seat_add_touch(struct roots_seat *seat, struct wlr_input_device *device) { + struct roots_touch *touch = calloc(sizeof(struct roots_touch), 1); + if (!touch) { + wlr_log(L_ERROR, "could not allocate touch for seat"); + return; + } + + device->data = touch; + touch->device = device; + touch->seat = seat; + wl_list_insert(&seat->touch, &touch->link); + wlr_cursor_attach_input_device(seat->cursor->cursor, device); + roots_seat_configure_cursor(seat); +} + +static void seat_add_tablet_pad(struct roots_seat *seat, struct wlr_input_device *device) { + // TODO +} + +static void seat_add_tablet_tool(struct roots_seat *seat, struct wlr_input_device *device) { + struct roots_tablet_tool *tablet_tool = calloc(sizeof(struct roots_tablet_tool), 1); + if (!tablet_tool) { + wlr_log(L_ERROR, "could not allocate tablet_tool for seat"); + return; + } + + device->data = tablet_tool; + tablet_tool->device = device; + tablet_tool->seat = seat; + wl_list_insert(&seat->tablet_tools, &tablet_tool->link); + wlr_cursor_attach_input_device(seat->cursor->cursor, device); + roots_seat_configure_cursor(seat); +} + +void roots_seat_add_device(struct roots_seat *seat, + struct wlr_input_device *device) { + switch (device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + seat_add_keyboard(seat, device); + break; + case WLR_INPUT_DEVICE_POINTER: + seat_add_pointer(seat, device); + break; + case WLR_INPUT_DEVICE_TOUCH: + seat_add_touch(seat, device); + break; + case WLR_INPUT_DEVICE_TABLET_PAD: + seat_add_tablet_pad(seat, device); + break; + case WLR_INPUT_DEVICE_TABLET_TOOL: + seat_add_tablet_tool(seat, device); + break; + } +} + +static void seat_remove_keyboard(struct roots_seat *seat, + struct wlr_input_device *device) { + struct roots_keyboard *keyboard; + wl_list_for_each(keyboard, &seat->keyboards, link) { + if (keyboard->device == device) { + roots_keyboard_destroy(keyboard); + return; + } + } +} + +static void seat_remove_pointer(struct roots_seat *seat, + struct wlr_input_device *device) { + struct roots_pointer *pointer; + wl_list_for_each(pointer, &seat->pointers, link) { + if (pointer->device == device) { + wl_list_remove(&pointer->link); + wlr_cursor_detach_input_device(seat->cursor->cursor, device); + free(pointer); + return; + } + } +} + +static void seat_remove_touch(struct roots_seat *seat, + struct wlr_input_device *device) { + struct roots_touch *touch; + wl_list_for_each(touch, &seat->touch, link) { + if (touch->device == device) { + wl_list_remove(&touch->link); + wlr_cursor_detach_input_device(seat->cursor->cursor, device); + free(touch); + return; + } + } +} + +static void seat_remove_tablet_pad(struct roots_seat *seat, + struct wlr_input_device *device) { + // TODO +} + +static void seat_remove_tablet_tool(struct roots_seat *seat, + struct wlr_input_device *device) { + struct roots_tablet_tool *tablet_tool; + wl_list_for_each(tablet_tool, &seat->tablet_tools, link) { + if (tablet_tool->device == device) { + wl_list_remove(&tablet_tool->link); + wlr_cursor_detach_input_device(seat->cursor->cursor, device); + free(tablet_tool); + return; + } + } +} + +void roots_seat_remove_device(struct roots_seat *seat, + struct wlr_input_device *device) { + switch (device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + seat_remove_keyboard(seat, device); + break; + case WLR_INPUT_DEVICE_POINTER: + seat_remove_pointer(seat, device); + break; + case WLR_INPUT_DEVICE_TOUCH: + seat_remove_touch(seat, device); + break; + case WLR_INPUT_DEVICE_TABLET_PAD: + seat_remove_tablet_pad(seat, device); + break; + case WLR_INPUT_DEVICE_TABLET_TOOL: + seat_remove_tablet_tool(seat, device); + break; + } +} + +void roots_seat_configure_xcursor(struct roots_seat *seat) { + struct wlr_xcursor *xcursor = get_default_xcursor(seat->cursor->xcursor_theme); + struct wlr_xcursor_image *image = xcursor->images[0]; + wlr_cursor_set_image(seat->cursor->cursor, image->buffer, image->width, + image->width, image->height, image->hotspot_x, image->hotspot_y); + + wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, + seat->cursor->cursor->y); +} + +bool roots_seat_has_meta_pressed(struct roots_seat *seat) { + struct roots_keyboard *keyboard; + wl_list_for_each(keyboard, &seat->keyboards, link) { + if (!keyboard->config->meta_key) { + continue; + } + + uint32_t modifiers = + wlr_keyboard_get_modifiers(keyboard->device->keyboard); + if ((modifiers ^ keyboard->config->meta_key) == 0) { + return true; + } + } + + return false; +} + +void roots_seat_focus_view(struct roots_seat *seat, struct roots_view *view) { + struct roots_desktop *desktop = seat->input->server->desktop; + if (seat->focus == view) { + return; + } + + // unfocus the old view if it is not focused by some other seat + // TODO probably should be an input function + if (seat->focus) { + bool has_other_focus = false; + struct roots_seat *iter_seat; + wl_list_for_each(iter_seat, &seat->input->seats, link) { + if (iter_seat == seat) { + continue; + } + if (iter_seat->focus == seat->focus) { + has_other_focus = true; + break; + } + } + + if (!has_other_focus) { + view_activate(seat->focus, false); + } + } + + if (!view) { + seat->focus = NULL; + seat->cursor->mode = ROOTS_CURSOR_PASSTHROUGH; + return; + } + + seat->focus = view; + + if (view->type == ROOTS_XWAYLAND_VIEW && + view->xwayland_surface->override_redirect) { + return; + } + + size_t index = 0; + for (size_t i = 0; i < desktop->views->length; ++i) { + struct roots_view *_view = desktop->views->items[i]; + if (_view == view) { + index = i; + break; + } + } + + view_activate(view, true); + // TODO: list_swap + wlr_list_del(desktop->views, index); + wlr_list_add(desktop->views, view); + wlr_seat_keyboard_notify_enter(seat->seat, view->wlr_surface); +} + +static void seat_set_xcursor_image(struct roots_seat *seat, struct + wlr_xcursor_image *image) { + wlr_cursor_set_image(seat->cursor->cursor, image->buffer, image->width, + image->width, image->height, image->hotspot_x, image->hotspot_y); +} + +void roots_seat_begin_move(struct roots_seat *seat, struct roots_view *view) { + struct roots_cursor *cursor = seat->cursor; + cursor->mode = ROOTS_CURSOR_MOVE; + cursor->offs_x = cursor->cursor->x; + cursor->offs_y = cursor->cursor->y; + cursor->view_x = view->x; + cursor->view_y = view->y; + wlr_seat_pointer_clear_focus(seat->seat); + + struct wlr_xcursor *xcursor = get_move_xcursor(seat->cursor->xcursor_theme); + if (xcursor != NULL) { + struct wlr_xcursor_image *image = xcursor->images[0]; + seat_set_xcursor_image(seat, image); + } +} + +void roots_seat_begin_resize(struct roots_seat *seat, struct roots_view *view, + uint32_t edges) { + struct roots_cursor *cursor = seat->cursor; + cursor->mode = ROOTS_CURSOR_RESIZE; + cursor->offs_x = cursor->cursor->x; + cursor->offs_y = cursor->cursor->y; + cursor->view_x = view->x; + cursor->view_y = view->y; + struct wlr_box size; + view_get_size(view, &size); + cursor->view_width = size.width; + cursor->view_height = size.height; + cursor->resize_edges = edges; + wlr_seat_pointer_clear_focus(seat->seat); + + struct wlr_xcursor *xcursor = get_resize_xcursor(cursor->xcursor_theme, edges); + if (xcursor != NULL) { + seat_set_xcursor_image(seat, xcursor->images[0]); + } + +} + +void roots_seat_begin_rotate(struct roots_seat *seat, struct roots_view *view) { + struct roots_cursor *cursor = seat->cursor; + cursor->mode = ROOTS_CURSOR_ROTATE; + cursor->offs_x = cursor->cursor->x; + cursor->offs_y = cursor->cursor->y; + cursor->view_rotation = view->rotation; + wlr_seat_pointer_clear_focus(seat->seat); + + struct wlr_xcursor *xcursor = get_rotate_xcursor(cursor->xcursor_theme); + if (xcursor != NULL) { + seat_set_xcursor_image(seat, xcursor->images[0]); + } +} diff --git a/rootston/tablet_tool.c b/rootston/tablet_tool.c deleted file mode 100644 index a612e683..00000000 --- a/rootston/tablet_tool.c +++ /dev/null @@ -1,24 +0,0 @@ -#include <stdlib.h> -#include <wayland-server.h> -#include <wlr/types/wlr_input_device.h> -#include <wlr/types/wlr_pointer.h> -#include <wlr/util/log.h> -#include "rootston/input.h" - -void tablet_tool_add(struct wlr_input_device *device, struct roots_input *input) { - struct roots_tablet_tool *tool = calloc(sizeof(struct roots_tablet_tool), 1); - device->data = tool; - tool->device = device; - tool->input = input; - wl_list_insert(&input->tablet_tools, &tool->link); - wlr_cursor_attach_input_device(input->cursor, device); - cursor_load_config(input->server->config, input->cursor, - input, input->server->desktop); -} - -void tablet_tool_remove(struct wlr_input_device *device, struct roots_input *input) { - struct roots_tablet_tool *tablet_tool = device->data; - wlr_cursor_detach_input_device(input->cursor, device); - wl_list_remove(&tablet_tool->link); - free(tablet_tool); -} diff --git a/rootston/touch.c b/rootston/touch.c deleted file mode 100644 index f6d9b11a..00000000 --- a/rootston/touch.c +++ /dev/null @@ -1,26 +0,0 @@ -#include <stdlib.h> -#include <wayland-server.h> -#include <wlr/types/wlr_input_device.h> -#include <wlr/types/wlr_pointer.h> -#include "rootston/input.h" - -// TODO: we'll likely want touch events to both control the cursor *and* be -// submitted directly to the seat. - -void touch_add(struct wlr_input_device *device, struct roots_input *input) { - struct roots_touch *touch = calloc(sizeof(struct roots_touch), 1); - device->data = touch; - touch->device = device; - touch->input = input; - wl_list_insert(&input->touch, &touch->link); - wlr_cursor_attach_input_device(input->cursor, device); - cursor_load_config(input->server->config, input->cursor, - input, input->server->desktop); -} - -void touch_remove(struct wlr_input_device *device, struct roots_input *input) { - struct roots_touch *touch = device->data; - wlr_cursor_detach_input_device(input->cursor, device); - wl_list_remove(&touch->link); - free(touch); -} diff --git a/rootston/wl_shell.c b/rootston/wl_shell.c index e38eb697..81b9e640 100644 --- a/rootston/wl_shell.c +++ b/rootston/wl_shell.c @@ -29,11 +29,11 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct roots_view *view = roots_surface->view; struct roots_input *input = view->desktop->server->input; struct wlr_wl_shell_surface_move_event *e = data; - const struct roots_input_event *event = get_input_event(input, e->serial); - if (!event || input->mode != ROOTS_CURSOR_PASSTHROUGH) { + struct roots_seat *seat = input_seat_from_wlr_seat(input, e->seat->seat); + if (!seat || seat->cursor->mode != ROOTS_CURSOR_PASSTHROUGH) { return; } - view_begin_move(input, event->cursor, view); + roots_seat_begin_move(seat, view); } static void handle_request_resize(struct wl_listener *listener, void *data) { @@ -42,11 +42,12 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct roots_view *view = roots_surface->view; struct roots_input *input = view->desktop->server->input; struct wlr_wl_shell_surface_resize_event *e = data; - const struct roots_input_event *event = get_input_event(input, e->serial); - if (!event || input->mode != ROOTS_CURSOR_PASSTHROUGH) { + struct roots_seat *seat = input_seat_from_wlr_seat(input, e->seat->seat); + // TODO verify input event + if (!seat || seat->cursor->mode != ROOTS_CURSOR_PASSTHROUGH) { return; } - view_begin_resize(input, event->cursor, view, e->edges); + roots_seat_begin_resize(seat, view, e->edges); } static void handle_surface_commit(struct wl_listener *listener, void *data) { diff --git a/rootston/xcursor.c b/rootston/xcursor.c index 43cbfc51..8697cdc3 100644 --- a/rootston/xcursor.c +++ b/rootston/xcursor.c @@ -1,14 +1,6 @@ #include <wlr/types/wlr_cursor.h> #include "rootston/input.h" -struct wlr_xcursor *get_default_xcursor(struct wlr_xcursor_theme *theme) { - return wlr_xcursor_theme_get_cursor(theme, "left_ptr"); -} - -struct wlr_xcursor *get_move_xcursor(struct wlr_xcursor_theme *theme) { - return wlr_xcursor_theme_get_cursor(theme, "grabbing"); -} - static const char *get_resize_xcursor_name(uint32_t edges) { if (edges & ROOTS_CURSOR_RESIZE_EDGE_TOP) { if (edges & ROOTS_CURSOR_RESIZE_EDGE_RIGHT) { @@ -32,6 +24,14 @@ static const char *get_resize_xcursor_name(uint32_t edges) { return "se-resize"; // fallback } +struct wlr_xcursor *get_default_xcursor(struct wlr_xcursor_theme *theme) { + return wlr_xcursor_theme_get_cursor(theme, "left_ptr"); +} + +struct wlr_xcursor *get_move_xcursor(struct wlr_xcursor_theme *theme) { + return wlr_xcursor_theme_get_cursor(theme, "grabbing"); +} + struct wlr_xcursor *get_resize_xcursor(struct wlr_xcursor_theme *theme, uint32_t edges) { return wlr_xcursor_theme_get_cursor(theme, get_resize_xcursor_name(edges)); diff --git a/rootston/xdg_shell_v6.c b/rootston/xdg_shell_v6.c index ca33c582..ed952e18 100644 --- a/rootston/xdg_shell_v6.c +++ b/rootston/xdg_shell_v6.c @@ -98,11 +98,12 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct roots_view *view = roots_xdg_surface->view; struct roots_input *input = view->desktop->server->input; struct wlr_xdg_toplevel_v6_move_event *e = data; - const struct roots_input_event *event = get_input_event(input, e->serial); - if (!event || input->mode != ROOTS_CURSOR_PASSTHROUGH) { + struct roots_seat *seat = input_seat_from_wlr_seat(input, e->seat->seat); + // TODO verify event serial + if (!seat || seat->cursor->mode != ROOTS_CURSOR_PASSTHROUGH) { return; } - view_begin_move(input, event->cursor, view); + roots_seat_begin_move(seat, view); } static void handle_request_resize(struct wl_listener *listener, void *data) { @@ -111,11 +112,13 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct roots_view *view = roots_xdg_surface->view; struct roots_input *input = view->desktop->server->input; struct wlr_xdg_toplevel_v6_resize_event *e = data; - const struct roots_input_event *event = get_input_event(input, e->serial); - if (!event || input->mode != ROOTS_CURSOR_PASSTHROUGH) { + // TODO verify event serial + struct roots_seat *seat = input_seat_from_wlr_seat(input, e->seat->seat); + assert(seat); + if (!seat || seat->cursor->mode != ROOTS_CURSOR_PASSTHROUGH) { return; } - view_begin_resize(input, event->cursor, view, e->edges); + roots_seat_begin_resize(seat, view, e->edges); } static void handle_commit(struct wl_listener *listener, void *data) { diff --git a/rootston/xwayland.c b/rootston/xwayland.c index e3fc1c84..92ba5e60 100644 --- a/rootston/xwayland.c +++ b/rootston/xwayland.c @@ -110,26 +110,16 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { xwayland_surface, event->x, event->y, event->width, event->height); } -// XXX Needs deep refactoring to get this better. We need to select the correct -// seat based on seat pointer focus, but interactive moving and resizing is not -// yet seat aware. Even then, we can only guess because X11 events don't give us -// enough wayland info to know for sure. -static struct wlr_cursor *guess_cursor_for_view(struct roots_view *view) { +static struct roots_seat *guess_seat_for_view(struct roots_view *view) { + // the best we can do is to pick the first seat that has the surface focused + // for the pointer struct roots_input *input = view->desktop->server->input; - size_t len = sizeof(input->input_events) / sizeof(*input->input_events); - for (size_t i = 0; i < len; i++) { - struct wlr_cursor *cursor = input->input_events[i].cursor; - if (cursor) { - int width = view->xwayland_surface->surface->current->width; - int height = view->xwayland_surface->surface->current->height; - if (cursor->x > view->x && cursor->y > view->y && - cursor->x < view->x + width && - cursor->y < view->y + height) { - return cursor; - } + struct roots_seat *seat; + wl_list_for_each(seat, &input->seats, link) { + if (seat->seat->pointer_state.focused_surface == view->wlr_surface) { + return seat; } } - return NULL; } @@ -137,28 +127,26 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct roots_xwayland_surface *roots_surface = wl_container_of(listener, roots_surface, request_move); struct roots_view *view = roots_surface->view; - struct roots_input *input = view->desktop->server->input; - struct wlr_cursor *cursor = guess_cursor_for_view(view); + struct roots_seat *seat = guess_seat_for_view(view); - if (!cursor || input->mode != ROOTS_CURSOR_PASSTHROUGH) { + if (!seat || seat->cursor->mode != ROOTS_CURSOR_PASSTHROUGH) { return; } - view_begin_move(input, cursor, view); + roots_seat_begin_move(seat, view); } static void handle_request_resize(struct wl_listener *listener, void *data) { struct roots_xwayland_surface *roots_surface = wl_container_of(listener, roots_surface, request_resize); struct roots_view *view = roots_surface->view; - struct roots_input *input = view->desktop->server->input; - struct wlr_cursor *cursor = guess_cursor_for_view(view); + struct roots_seat *seat = guess_seat_for_view(view); struct wlr_xwayland_resize_event *e = data; - if (!cursor || input->mode != ROOTS_CURSOR_PASSTHROUGH) { + if (!seat || seat->cursor->mode != ROOTS_CURSOR_PASSTHROUGH) { return; } - view_begin_resize(input, cursor, view, e->edges); + roots_seat_begin_resize(seat, view, e->edges); } static void handle_map_notify(struct wl_listener *listener, void *data) { |