input: Add basic pointer gesture support

Passing of gesture events added in #4794 allows to use them in clients
but not in sway itself, for example to switch workspaces using
three-finger swipes.

Add support for binding four basic directional swipes to arbitrary sway
commands. Add the necessary handling to default seatops to execute them
at the beginning or end of a swipe gesture. This for now responds to all
swipes, ignoring the number of fingers used.

The former throws up the intrinsic conundrum that we can not know the
kind of swipe that's going to happen because it can change all through
its lifecylce. Therefore we can only execute bindings on release.

Test plan:
- add workspace switching to config like so:

bindsym --whole-window SWIPE_LEFT workspace prev_on_output
bindsym --whole-window SWIPE_RIGHT workspace next_on_output

- start sway and open two workspaces
- switch back and forth using horizontal three-finger swipes, observing
  that vertical swipes won't

Signed-off-by: Michael Weiser <michael.weiser@gmx.de>
This commit is contained in:
Michael Weiser 2020-01-25 18:23:59 +01:00
parent b518870bc1
commit 44a2a60dcd
3 changed files with 195 additions and 6 deletions

View file

@ -14,6 +14,10 @@
#define SWAY_SCROLL_DOWN KEY_MAX + 2 #define SWAY_SCROLL_DOWN KEY_MAX + 2
#define SWAY_SCROLL_LEFT KEY_MAX + 3 #define SWAY_SCROLL_LEFT KEY_MAX + 3
#define SWAY_SCROLL_RIGHT KEY_MAX + 4 #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_cursor {
struct sway_seat *seat; struct sway_seat *seat;

View file

@ -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_UP, SWAY_SCROLL_DOWN, SWAY_SCROLL_LEFT,
SWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA}; SWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA};
return buttons[number - 1]; 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) { } else if (strncmp(name, "BTN_", strlen("BTN_")) == 0) {
// Get event code from name // Get event code from name
int code = libevdev_event_code_from_name(EV_KEY, 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"; name = "SWAY_SCROLL_LEFT";
} else if (button == SWAY_SCROLL_RIGHT) { } else if (button == SWAY_SCROLL_RIGHT) {
name = "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; return name;

View file

@ -20,6 +20,8 @@ struct seatop_default_event {
struct sway_node *previous_node; struct sway_node *previous_node;
uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP];
size_t pressed_button_count; 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, static void handle_swipe_begin(struct sway_seat *seat,
struct wlr_event_pointer_swipe_begin *event) { 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; struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_swipe_begin( struct seatop_default_event *e = seat->seatop_data;
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->fingers); // 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, static void handle_swipe_update(struct sway_seat *seat,
struct wlr_event_pointer_swipe_update *event) { 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; struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_swipe_update( wlr_pointer_gestures_v1_send_swipe_update(
cursor->pointer_gestures, cursor->pointer_gestures,
@ -764,12 +846,82 @@ static void handle_swipe_update(struct sway_seat *seat,
event->time_msec, event->dx, event->dy); 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, static void handle_swipe_end(struct sway_seat *seat,
struct wlr_event_pointer_swipe_end *event) { struct wlr_event_pointer_swipe_end *event) {
struct sway_cursor *cursor = seat->cursor; struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_swipe_end( struct seatop_default_event *e = seat->seatop_data;
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->cancelled); 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);
} }
/*----------------------------------\ /*----------------------------------\