diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 7d66e6997..0727ccfc6 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -14,6 +14,10 @@ #define SWAY_SCROLL_DOWN KEY_MAX + 2 #define SWAY_SCROLL_LEFT KEY_MAX + 3 #define SWAY_SCROLL_RIGHT KEY_MAX + 4 +#define SWAY_SWIPE_UP KEY_MAX + 5 +#define SWAY_SWIPE_DOWN KEY_MAX + 6 +#define SWAY_SWIPE_LEFT KEY_MAX + 7 +#define SWAY_SWIPE_RIGHT KEY_MAX + 8 struct sway_cursor { struct sway_seat *seat; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index ebc67d1fc..83ab5c6f4 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -1217,6 +1217,31 @@ uint32_t get_mouse_bindsym(const char *name, char **error) { SWAY_SCROLL_UP, SWAY_SCROLL_DOWN, SWAY_SCROLL_LEFT, SWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA}; return buttons[number - 1]; + } else if (strncmp(name, "SWIPE_", strlen("SWIPE_")) == 0) { + static const struct { + uint32_t button; + const char *dir; + } swipes[] = { + SWAY_SWIPE_UP, "UP", + SWAY_SWIPE_DOWN, "DOWN", + SWAY_SWIPE_LEFT, "LEFT", + SWAY_SWIPE_RIGHT, "RIGHT", + 0, NULL, + }, *sp; + + const char *dir = &name[strlen("SWIPE_")]; + for (sp = swipes; sp->dir; sp++) { + if (strcmp(sp->dir, dir) == 0) { + break; + } + } + + if (!sp->dir) { + *error = strdup("Unknown swipe type"); + return 0; + } + + return sp->button; } else if (strncmp(name, "BTN_", strlen("BTN_")) == 0) { // Get event code from name int code = libevdev_event_code_from_name(EV_KEY, name); @@ -1278,6 +1303,14 @@ const char *get_mouse_button_name(uint32_t button) { name = "SWAY_SCROLL_LEFT"; } else if (button == SWAY_SCROLL_RIGHT) { name = "SWAY_SCROLL_RIGHT"; + } else if (button == SWAY_SWIPE_UP) { + name = "SWAY_SWIPE_UP"; + } else if (button == SWAY_SWIPE_DOWN) { + name = "SWAY_SWIPE_DOWN"; + } else if (button == SWAY_SWIPE_LEFT) { + name = "SWAY_SWIPE_LEFT"; + } else if (button == SWAY_SWIPE_RIGHT) { + name = "SWAY_SWIPE_RIGHT"; } } return name; diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 4a4b1be68..963726e6d 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -20,6 +20,8 @@ struct seatop_default_event { struct sway_node *previous_node; uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; size_t pressed_button_count; + bool swiping; + double swipe_travel_x, swipe_travel_y; }; /*-----------------------------------------\ @@ -749,14 +751,94 @@ static void handle_pointer_axis(struct sway_seat *seat, static void handle_swipe_begin(struct sway_seat *seat, struct wlr_event_pointer_swipe_begin *event) { + struct sway_input_device *input_device = + event->device ? event->device->data : NULL; struct sway_cursor *cursor = seat->cursor; - wlr_pointer_gestures_v1_send_swipe_begin( - cursor->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->fingers); + struct seatop_default_event *e = seat->seatop_data; + + // Determine what's under the cursor + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_node *node = node_at_coords(seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + struct sway_container *cont = node && node->type == N_CONTAINER ? + node->sway_container : NULL; + enum wlr_edges edge = cont ? find_edge(cont, surface, cursor) : WLR_EDGE_NONE; + bool on_border = edge != WLR_EDGE_NONE; + bool on_titlebar = cont && !on_border && !surface; + bool on_contents = cont && !on_border && surface; + bool on_workspace = node && node->type == N_WORKSPACE; + + bool handled = false; + + // Gather information needed for mouse bindings + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + struct wlr_input_device *device = + input_device ? input_device->wlr_device : NULL; + char *dev_id = device ? input_device_get_identifier(device) : strdup("*"); + + /** + * Handle mouse bindings - if there's any swipe bindings for this + * context. Here we must swallow all swipes if there's even one + * possible binding because we have no way of knowing yet what kind of + * swipe this is going to be. It's in the nature of the thing because + * the user may turn a left into an upwards swipe mid-way. For the same + * reason it makes no sense to execute any binding here. So we defer it + * to swipe release. We can break as soon as it's confirmed that there + * is at least one binding. + */ + struct sway_binding *binding = NULL; + for (uint32_t button = SWAY_SWIPE_UP; button <= SWAY_SWIPE_RIGHT; + button++) { + state_add_button(e, button); + // check for an on-press binding + binding = get_active_mouse_binding(e, + config->current_mode->mouse_bindings, + modifiers, false, on_titlebar, on_border, + on_contents, on_workspace, dev_id); + if (!binding) { + // see if there's an on-release binding instead + binding = get_active_mouse_binding(e, + config->current_mode->mouse_bindings, + modifiers, true, on_titlebar, + on_border, on_contents, on_workspace, + dev_id); + } + + state_erase_button(e, button); + if (binding) { + handled = true; + break; + } + } + + if (handled) { + // mark this as ours and start tracking + e->swiping = true; + e->swipe_travel_x = 0; + e->swipe_travel_y = 0; + } + free(dev_id); + + if (!handled) { + wlr_pointer_gestures_v1_send_swipe_begin( + cursor->pointer_gestures, cursor->seat->wlr_seat, + event->time_msec, event->fingers); + } } static void handle_swipe_update(struct sway_seat *seat, struct wlr_event_pointer_swipe_update *event) { + struct seatop_default_event *e = seat->seatop_data; + + // is this ours? + if (e->swiping) { + e->swipe_travel_x += event->dx; + e->swipe_travel_y += event->dy; + return; + } + struct sway_cursor *cursor = seat->cursor; wlr_pointer_gestures_v1_send_swipe_update( cursor->pointer_gestures, @@ -764,12 +846,82 @@ static void handle_swipe_update(struct sway_seat *seat, event->time_msec, event->dx, event->dy); } +static uint32_t swipe_to_button(struct seatop_default_event *event) { + if (fabs(event->swipe_travel_x) > fabs(event->swipe_travel_y)) { + return event->swipe_travel_x > 0 ? SWAY_SWIPE_RIGHT : SWAY_SWIPE_LEFT; + } + + return event->swipe_travel_y > 0 ? SWAY_SWIPE_DOWN : SWAY_SWIPE_UP; +} + static void handle_swipe_end(struct sway_seat *seat, struct wlr_event_pointer_swipe_end *event) { struct sway_cursor *cursor = seat->cursor; - wlr_pointer_gestures_v1_send_swipe_end( - cursor->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->cancelled); + struct seatop_default_event *e = seat->seatop_data; + + if (!e->swiping) { + wlr_pointer_gestures_v1_send_swipe_end( + cursor->pointer_gestures, cursor->seat->wlr_seat, + event->time_msec, event->cancelled); + return; + } + + e->swiping = false; + + // can happen when changing finger count mid-swipe or swiping off the + // tracking surface (touchpad) + if (event->cancelled) { + return; + } + + struct sway_input_device *input_device = + event->device ? event->device->data : NULL; + + // Determine what's under the cursor + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_node *node = node_at_coords(seat, cursor->cursor->x, + cursor->cursor->y, &surface, &sx, &sy); + struct sway_container *cont = node && node->type == N_CONTAINER ? + node->sway_container : NULL; + enum wlr_edges edge = cont ? find_edge(cont, surface, cursor) : + WLR_EDGE_NONE; + bool on_border = edge != WLR_EDGE_NONE; + bool on_titlebar = cont && !on_border && !surface; + bool on_contents = cont && !on_border && surface; + bool on_workspace = node && node->type == N_WORKSPACE; + + // Gather information needed for mouse bindings + struct wlr_keyboard *keyboard = + wlr_seat_get_keyboard(seat->wlr_seat); + uint32_t modifiers = keyboard ? + wlr_keyboard_get_modifiers(keyboard) : 0; + struct wlr_input_device *device = + input_device ? input_device->wlr_device : NULL; + char *dev_id = device ? input_device_get_identifier(device) : + strdup("*"); + uint32_t button = swipe_to_button(e); + + // Prefer any on-press binding because we did not execute it on gesture + // start because we could not know what gesture it would become. Ignore + // on-release binding on success. + state_add_button(e, button); + struct sway_binding *binding = get_active_mouse_binding(e, + config->current_mode->mouse_bindings, modifiers, false, + on_titlebar, on_border, on_contents, on_workspace, + dev_id); + if (!binding) { + binding = get_active_mouse_binding(e, + config->current_mode->mouse_bindings, + modifiers, true, on_titlebar, on_border, + on_contents, on_workspace, dev_id); + } + state_erase_button(e, button); + if (binding) { + seat_execute_command(seat, binding); + } + + free(dev_id); } /*----------------------------------\