mirror of
https://github.com/swaywm/sway.git
synced 2026-04-23 06:46:27 -04:00
sway: add bindgesture command
Co-authored-by: Michael Weiser <michael.weiser@gmx.de>
This commit is contained in:
parent
a535ed310f
commit
cab2189aa6
17 changed files with 1188 additions and 87 deletions
|
|
@ -4,6 +4,7 @@
|
|||
#include <wlr/types/wlr_cursor.h>
|
||||
#include <wlr/types/wlr_tablet_v2.h>
|
||||
#include <wlr/types/wlr_xcursor_manager.h>
|
||||
#include "gesture.h"
|
||||
#include "sway/desktop/transaction.h"
|
||||
#include "sway/input/cursor.h"
|
||||
#include "sway/input/seat.h"
|
||||
|
|
@ -20,6 +21,7 @@ struct seatop_default_event {
|
|||
struct sway_node *previous_node;
|
||||
uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP];
|
||||
size_t pressed_button_count;
|
||||
struct gesture_tracker gestures;
|
||||
};
|
||||
|
||||
/*-----------------------------------------\
|
||||
|
|
@ -750,6 +752,304 @@ static void handle_pointer_axis(struct sway_seat *seat,
|
|||
}
|
||||
}
|
||||
|
||||
/*------------------------------------\
|
||||
* Functions used by gesture support /
|
||||
*----------------------------------*/
|
||||
|
||||
/**
|
||||
* Check gesture binding for a specific gesture type and finger count.
|
||||
* Returns true if binding is present, false otherwise
|
||||
*/
|
||||
static bool gesture_binding_check(list_t *bindings, enum gesture_type type,
|
||||
uint8_t fingers, struct sway_input_device *device) {
|
||||
char *input =
|
||||
device ? input_device_get_identifier(device->wlr_device) : strdup("*");
|
||||
|
||||
for (int i = 0; i < bindings->length; ++i) {
|
||||
struct sway_gesture_binding *binding = bindings->items[i];
|
||||
|
||||
// Check type and finger count
|
||||
if (!gesture_check(&binding->gesture, type, fingers)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check that input matches
|
||||
if (strcmp(binding->input, "*") != 0 &&
|
||||
strcmp(binding->input, input) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
free(input);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
free(input);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the gesture binding which matches gesture type, finger count
|
||||
* and direction, otherwise return null.
|
||||
*/
|
||||
static struct sway_gesture_binding* gesture_binding_match(
|
||||
list_t *bindings, struct gesture *gesture, const char *input) {
|
||||
struct sway_gesture_binding *current = NULL;
|
||||
|
||||
// Find best matching binding
|
||||
for (int i = 0; i < bindings->length; ++i) {
|
||||
struct sway_gesture_binding *binding = bindings->items[i];
|
||||
bool exact = binding->flags & BINDING_EXACT;
|
||||
|
||||
// Check gesture matching
|
||||
if (!gesture_match(&binding->gesture, gesture, exact)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check input matching
|
||||
if (strcmp(binding->input, "*") != 0 &&
|
||||
strcmp(binding->input, input) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we already have a match ...
|
||||
if (current) {
|
||||
// ... check if input matching is equivalent
|
||||
if (strcmp(current->input, binding->input) == 0) {
|
||||
|
||||
// ... - do not override an exact binding
|
||||
if (!exact && current->flags & BINDING_EXACT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ... - and ensure direction matching is better or equal
|
||||
if (gesture_compare(¤t->gesture, &binding->gesture) > 0) {
|
||||
continue;
|
||||
}
|
||||
} else if (strcmp(binding->input, "*") == 0) {
|
||||
// ... do not accept worse input match
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Accept newer or better match
|
||||
current = binding;
|
||||
|
||||
// If exact binding and input is found, quit search
|
||||
if (strcmp(current->input, input) == 0 &&
|
||||
gesture_compare(¤t->gesture, gesture) == 0) {
|
||||
break;
|
||||
}
|
||||
} // for all gesture bindings
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
// Wrapper around gesture_tracker_end to use tracker with sway bindings
|
||||
static struct sway_gesture_binding* gesture_tracker_end_and_match(
|
||||
struct gesture_tracker *tracker, struct sway_input_device* device) {
|
||||
// Determine name of input that received gesture
|
||||
char *input = device
|
||||
? input_device_get_identifier(device->wlr_device)
|
||||
: strdup("*");
|
||||
|
||||
// Match tracking result to binding
|
||||
struct gesture *gesture = gesture_tracker_end(tracker);
|
||||
struct sway_gesture_binding *binding = gesture_binding_match(
|
||||
config->current_mode->gesture_bindings, gesture, input);
|
||||
free(gesture);
|
||||
free(input);
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
// Small wrapper around seat_execute_command to work on gesture bindings
|
||||
static void gesture_binding_execute(struct sway_seat *seat,
|
||||
struct sway_gesture_binding *binding) {
|
||||
struct sway_binding *dummy_binding =
|
||||
calloc(1, sizeof(struct sway_binding));
|
||||
dummy_binding->type = BINDING_GESTURE;
|
||||
dummy_binding->command = binding->command;
|
||||
|
||||
char *description = gesture_to_string(&binding->gesture);
|
||||
sway_log(SWAY_DEBUG, "executing gesture binding: %s", description);
|
||||
free(description);
|
||||
|
||||
seat_execute_command(seat, dummy_binding);
|
||||
|
||||
free(dummy_binding);
|
||||
}
|
||||
|
||||
static void handle_hold_begin(struct sway_seat *seat,
|
||||
struct wlr_pointer_hold_begin_event *event) {
|
||||
// Start tracking gesture if there is a matching binding ...
|
||||
struct sway_input_device *device =
|
||||
event->pointer ? event->pointer->base.data : NULL;
|
||||
list_t *bindings = config->current_mode->gesture_bindings;
|
||||
if (gesture_binding_check(bindings, GESTURE_TYPE_HOLD, event->fingers, device)) {
|
||||
struct seatop_default_event *seatop = seat->seatop_data;
|
||||
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_HOLD, event->fingers);
|
||||
} else {
|
||||
// ... otherwise forward to client
|
||||
struct sway_cursor *cursor = seat->cursor;
|
||||
wlr_pointer_gestures_v1_send_hold_begin(
|
||||
cursor->pointer_gestures, cursor->seat->wlr_seat,
|
||||
event->time_msec, event->fingers);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_hold_end(struct sway_seat *seat,
|
||||
struct wlr_pointer_hold_end_event *event) {
|
||||
// Ensure that gesture is being tracked and was not cancelled
|
||||
struct seatop_default_event *seatop = seat->seatop_data;
|
||||
if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) {
|
||||
struct sway_cursor *cursor = seat->cursor;
|
||||
wlr_pointer_gestures_v1_send_hold_end(
|
||||
cursor->pointer_gestures, cursor->seat->wlr_seat,
|
||||
event->time_msec, event->cancelled);
|
||||
return;
|
||||
}
|
||||
if (event->cancelled) {
|
||||
gesture_tracker_cancel(&seatop->gestures);
|
||||
return;
|
||||
}
|
||||
|
||||
// End gesture tracking and execute matched binding
|
||||
struct sway_input_device *device =
|
||||
event->pointer ? event->pointer->base.data : NULL;
|
||||
struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
|
||||
&seatop->gestures, device);
|
||||
|
||||
if (binding) {
|
||||
gesture_binding_execute(seat, binding);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_pinch_begin(struct sway_seat *seat,
|
||||
struct wlr_pointer_pinch_begin_event *event) {
|
||||
// Start tracking gesture if there is a matching binding ...
|
||||
struct sway_input_device *device =
|
||||
event->pointer ? event->pointer->base.data : NULL;
|
||||
list_t *bindings = config->current_mode->gesture_bindings;
|
||||
if (gesture_binding_check(bindings, GESTURE_TYPE_PINCH, event->fingers, device)) {
|
||||
struct seatop_default_event *seatop = seat->seatop_data;
|
||||
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_PINCH, event->fingers);
|
||||
} else {
|
||||
// ... otherwise forward to client
|
||||
struct sway_cursor *cursor = seat->cursor;
|
||||
wlr_pointer_gestures_v1_send_pinch_begin(
|
||||
cursor->pointer_gestures, cursor->seat->wlr_seat,
|
||||
event->time_msec, event->fingers);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_pinch_update(struct sway_seat *seat,
|
||||
struct wlr_pointer_pinch_update_event *event) {
|
||||
// Update any ongoing tracking ...
|
||||
struct seatop_default_event *seatop = seat->seatop_data;
|
||||
if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
|
||||
gesture_tracker_update(&seatop->gestures, event->dx, event->dy,
|
||||
event->scale, event->rotation);
|
||||
} else {
|
||||
// ... otherwise forward to client
|
||||
struct sway_cursor *cursor = seat->cursor;
|
||||
wlr_pointer_gestures_v1_send_pinch_update(
|
||||
cursor->pointer_gestures,
|
||||
cursor->seat->wlr_seat,
|
||||
event->time_msec, event->dx, event->dy,
|
||||
event->scale, event->rotation);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_pinch_end(struct sway_seat *seat,
|
||||
struct wlr_pointer_pinch_end_event *event) {
|
||||
// Ensure that gesture is being tracked and was not cancelled
|
||||
struct seatop_default_event *seatop = seat->seatop_data;
|
||||
if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
|
||||
struct sway_cursor *cursor = seat->cursor;
|
||||
wlr_pointer_gestures_v1_send_pinch_end(
|
||||
cursor->pointer_gestures, cursor->seat->wlr_seat,
|
||||
event->time_msec, event->cancelled);
|
||||
return;
|
||||
}
|
||||
if (event->cancelled) {
|
||||
gesture_tracker_cancel(&seatop->gestures);
|
||||
return;
|
||||
}
|
||||
|
||||
// End gesture tracking and execute matched binding
|
||||
struct sway_input_device *device =
|
||||
event->pointer ? event->pointer->base.data : NULL;
|
||||
struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
|
||||
&seatop->gestures, device);
|
||||
|
||||
if (binding) {
|
||||
gesture_binding_execute(seat, binding);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_swipe_begin(struct sway_seat *seat,
|
||||
struct wlr_pointer_swipe_begin_event *event) {
|
||||
// Start tracking gesture if there is a matching binding ...
|
||||
struct sway_input_device *device =
|
||||
event->pointer ? event->pointer->base.data : NULL;
|
||||
list_t *bindings = config->current_mode->gesture_bindings;
|
||||
if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) {
|
||||
struct seatop_default_event *seatop = seat->seatop_data;
|
||||
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers);
|
||||
} else {
|
||||
// ... otherwise forward to client
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_swipe_update(struct sway_seat *seat,
|
||||
struct wlr_pointer_swipe_update_event *event) {
|
||||
|
||||
// Update any ongoing tracking ...
|
||||
struct seatop_default_event *seatop = seat->seatop_data;
|
||||
if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
|
||||
gesture_tracker_update(&seatop->gestures,
|
||||
event->dx, event->dy, NAN, NAN);
|
||||
} else {
|
||||
// ... otherwise forward to client
|
||||
struct sway_cursor *cursor = seat->cursor;
|
||||
wlr_pointer_gestures_v1_send_swipe_update(
|
||||
cursor->pointer_gestures, cursor->seat->wlr_seat,
|
||||
event->time_msec, event->dx, event->dy);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_swipe_end(struct sway_seat *seat,
|
||||
struct wlr_pointer_swipe_end_event *event) {
|
||||
// Ensure gesture is being tracked and was not cancelled
|
||||
struct seatop_default_event *seatop = seat->seatop_data;
|
||||
if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
if (event->cancelled) {
|
||||
gesture_tracker_cancel(&seatop->gestures);
|
||||
return;
|
||||
}
|
||||
|
||||
// End gesture tracking and execute matched binding
|
||||
struct sway_input_device *device =
|
||||
event->pointer ? event->pointer->base.data : NULL;
|
||||
struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
|
||||
&seatop->gestures, device);
|
||||
|
||||
if (binding) {
|
||||
gesture_binding_execute(seat, binding);
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------\
|
||||
* Functions used by handle_rebase /
|
||||
*--------------------------------*/
|
||||
|
|
@ -779,6 +1079,14 @@ static const struct sway_seatop_impl seatop_impl = {
|
|||
.pointer_axis = handle_pointer_axis,
|
||||
.tablet_tool_tip = handle_tablet_tool_tip,
|
||||
.tablet_tool_motion = handle_tablet_tool_motion,
|
||||
.hold_begin = handle_hold_begin,
|
||||
.hold_end = handle_hold_end,
|
||||
.pinch_begin = handle_pinch_begin,
|
||||
.pinch_update = handle_pinch_update,
|
||||
.pinch_end = handle_pinch_end,
|
||||
.swipe_begin = handle_swipe_begin,
|
||||
.swipe_update = handle_swipe_update,
|
||||
.swipe_end = handle_swipe_end,
|
||||
.rebase = handle_rebase,
|
||||
.allow_set_cursor = true,
|
||||
};
|
||||
|
|
@ -789,8 +1097,8 @@ void seatop_begin_default(struct sway_seat *seat) {
|
|||
struct seatop_default_event *e =
|
||||
calloc(1, sizeof(struct seatop_default_event));
|
||||
sway_assert(e, "Unable to allocate seatop_default_event");
|
||||
|
||||
seat->seatop_impl = &seatop_impl;
|
||||
seat->seatop_data = e;
|
||||
|
||||
seatop_rebase(seat, 0);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue