mirror of
https://github.com/swaywm/sway.git
synced 2025-11-24 06:59:51 -05: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
|
|
@ -46,6 +46,7 @@ static const struct cmd_handler handlers[] = {
|
|||
{ "assign", cmd_assign },
|
||||
{ "bar", cmd_bar },
|
||||
{ "bindcode", cmd_bindcode },
|
||||
{ "bindgesture", cmd_bindgesture },
|
||||
{ "bindswitch", cmd_bindswitch },
|
||||
{ "bindsym", cmd_bindsym },
|
||||
{ "client.background", cmd_client_noop },
|
||||
|
|
@ -92,6 +93,7 @@ static const struct cmd_handler handlers[] = {
|
|||
{ "titlebar_border_thickness", cmd_titlebar_border_thickness },
|
||||
{ "titlebar_padding", cmd_titlebar_padding },
|
||||
{ "unbindcode", cmd_unbindcode },
|
||||
{ "unbindgesture", cmd_unbindgesture },
|
||||
{ "unbindswitch", cmd_unbindswitch },
|
||||
{ "unbindsym", cmd_unbindsym },
|
||||
{ "workspace", cmd_workspace },
|
||||
|
|
@ -407,6 +409,7 @@ struct cmd_results *config_command(char *exec, char **new_block) {
|
|||
&& handler->handle != cmd_bindsym
|
||||
&& handler->handle != cmd_bindcode
|
||||
&& handler->handle != cmd_bindswitch
|
||||
&& handler->handle != cmd_bindgesture
|
||||
&& handler->handle != cmd_set
|
||||
&& handler->handle != cmd_for_window
|
||||
&& (*argv[i] == '\"' || *argv[i] == '\'')) {
|
||||
|
|
|
|||
166
sway/commands/gesture.c
Normal file
166
sway/commands/gesture.c
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "sway/config.h"
|
||||
|
||||
#include "gesture.h"
|
||||
#include "log.h"
|
||||
#include "stringop.h"
|
||||
#include "sway/commands.h"
|
||||
|
||||
void free_gesture_binding(struct sway_gesture_binding *binding) {
|
||||
if (!binding) {
|
||||
return;
|
||||
}
|
||||
free(binding->input);
|
||||
free(binding->command);
|
||||
free(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the bindings have the same gesture type, direction, etc
|
||||
*/
|
||||
static bool binding_gesture_equal(struct sway_gesture_binding *binding_a,
|
||||
struct sway_gesture_binding *binding_b) {
|
||||
if (strcmp(binding_a->input, binding_b->input) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gesture_equal(&binding_a->gesture, &binding_b->gesture)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((binding_a->flags & BINDING_EXACT) !=
|
||||
(binding_b->flags & BINDING_EXACT)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add gesture binding to config
|
||||
*/
|
||||
static struct cmd_results *gesture_binding_add(
|
||||
struct sway_gesture_binding *binding,
|
||||
const char *gesturecombo, bool warn) {
|
||||
list_t *mode_bindings = config->current_mode->gesture_bindings;
|
||||
// overwrite the binding if it already exists
|
||||
bool overwritten = false;
|
||||
for (int i = 0; i < mode_bindings->length; ++i) {
|
||||
struct sway_gesture_binding *config_binding = mode_bindings->items[i];
|
||||
if (binding_gesture_equal(binding, config_binding)) {
|
||||
sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`",
|
||||
gesturecombo, binding->command, config_binding->command);
|
||||
if (warn) {
|
||||
config_add_swaynag_warning("Overwriting binding"
|
||||
"'%s' to `%s` from `%s`",
|
||||
gesturecombo, binding->command,
|
||||
config_binding->command);
|
||||
}
|
||||
free_gesture_binding(config_binding);
|
||||
mode_bindings->items[i] = binding;
|
||||
overwritten = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!overwritten) {
|
||||
list_add(mode_bindings, binding);
|
||||
sway_log(SWAY_DEBUG, "bindgesture - Bound %s to command `%s`",
|
||||
gesturecombo, binding->command);
|
||||
}
|
||||
|
||||
return cmd_results_new(CMD_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove gesture binding from config
|
||||
*/
|
||||
static struct cmd_results *gesture_binding_remove(
|
||||
struct sway_gesture_binding *binding, const char *gesturecombo) {
|
||||
list_t *mode_bindings = config->current_mode->gesture_bindings;
|
||||
for (int i = 0; i < mode_bindings->length; ++i) {
|
||||
struct sway_gesture_binding *config_binding = mode_bindings->items[i];
|
||||
if (binding_gesture_equal(binding, config_binding)) {
|
||||
free_gesture_binding(config_binding);
|
||||
free_gesture_binding(binding);
|
||||
list_del(mode_bindings, i);
|
||||
sway_log(SWAY_DEBUG, "unbindgesture - Unbound %s gesture",
|
||||
gesturecombo);
|
||||
return cmd_results_new(CMD_SUCCESS, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
free_gesture_binding(binding);
|
||||
return cmd_results_new(CMD_FAILURE, "Could not find gesture binding `%s`",
|
||||
gesturecombo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and execute bindgesture or unbindgesture command.
|
||||
*/
|
||||
static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, bool unbind) {
|
||||
int minargs = 2;
|
||||
char *bindtype = "bindgesture";
|
||||
if (unbind) {
|
||||
minargs--;
|
||||
bindtype = "unbindgesture";
|
||||
}
|
||||
|
||||
struct cmd_results *error = NULL;
|
||||
if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) {
|
||||
return error;
|
||||
}
|
||||
struct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding));
|
||||
if (!binding) {
|
||||
return cmd_results_new(CMD_FAILURE, "Unable to allocate binding");
|
||||
}
|
||||
binding->input = strdup("*");
|
||||
|
||||
bool warn = true;
|
||||
|
||||
// Handle flags
|
||||
while (argc > 0) {
|
||||
if (strcmp("--exact", argv[0]) == 0) {
|
||||
binding->flags |= BINDING_EXACT;
|
||||
} else if (strcmp("--no-warn", argv[0]) == 0) {
|
||||
warn = false;
|
||||
} else if (strncmp("--input-device=", argv[0],
|
||||
strlen("--input-device=")) == 0) {
|
||||
free(binding->input);
|
||||
binding->input = strdup(argv[0] + strlen("--input-device="));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
|
||||
if (argc < minargs) {
|
||||
free(binding);
|
||||
return cmd_results_new(CMD_FAILURE,
|
||||
"Invalid %s command (expected at least %d "
|
||||
"non-option arguments, got %d)", bindtype, minargs, argc);
|
||||
}
|
||||
|
||||
char* errmsg = NULL;
|
||||
if ((errmsg = gesture_parse(argv[0], &binding->gesture))) {
|
||||
free(binding);
|
||||
struct cmd_results *final = cmd_results_new(CMD_FAILURE,
|
||||
"Invalid %s command (%s)",
|
||||
bindtype, errmsg);
|
||||
free(errmsg);
|
||||
return final;
|
||||
}
|
||||
|
||||
if (unbind) {
|
||||
return gesture_binding_remove(binding, argv[0]);
|
||||
}
|
||||
binding->command = join_args(argv + 1, argc - 1);
|
||||
return gesture_binding_add(binding, argv[0], warn);
|
||||
}
|
||||
|
||||
struct cmd_results *cmd_bindgesture(int argc, char **argv) {
|
||||
return cmd_bind_or_unbind_gesture(argc, argv, false);
|
||||
}
|
||||
|
||||
struct cmd_results *cmd_unbindgesture(int argc, char **argv) {
|
||||
return cmd_bind_or_unbind_gesture(argc, argv, true);
|
||||
}
|
||||
|
|
@ -11,10 +11,12 @@
|
|||
// Must be in order for the bsearch
|
||||
static const struct cmd_handler mode_handlers[] = {
|
||||
{ "bindcode", cmd_bindcode },
|
||||
{ "bindgesture", cmd_bindgesture },
|
||||
{ "bindswitch", cmd_bindswitch },
|
||||
{ "bindsym", cmd_bindsym },
|
||||
{ "set", cmd_set },
|
||||
{ "unbindcode", cmd_unbindcode },
|
||||
{ "unbindgesture", cmd_unbindgesture },
|
||||
{ "unbindswitch", cmd_unbindswitch },
|
||||
{ "unbindsym", cmd_unbindsym },
|
||||
};
|
||||
|
|
@ -59,6 +61,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) {
|
|||
mode->keycode_bindings = create_list();
|
||||
mode->mouse_bindings = create_list();
|
||||
mode->switch_bindings = create_list();
|
||||
mode->gesture_bindings = create_list();
|
||||
mode->pango = pango;
|
||||
list_add(config->modes, mode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,6 +82,12 @@ static void free_mode(struct sway_mode *mode) {
|
|||
}
|
||||
list_free(mode->switch_bindings);
|
||||
}
|
||||
if (mode->gesture_bindings) {
|
||||
for (int i = 0; i < mode->gesture_bindings->length; i++) {
|
||||
free_gesture_binding(mode->gesture_bindings->items[i]);
|
||||
}
|
||||
list_free(mode->gesture_bindings);
|
||||
}
|
||||
free(mode);
|
||||
}
|
||||
|
||||
|
|
@ -222,6 +228,7 @@ static void config_defaults(struct sway_config *config) {
|
|||
if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup;
|
||||
if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup;
|
||||
if (!(config->current_mode->switch_bindings = create_list())) goto cleanup;
|
||||
if (!(config->current_mode->gesture_bindings = create_list())) goto cleanup;
|
||||
list_add(config->modes, config->current_mode);
|
||||
|
||||
config->floating_mod = 0;
|
||||
|
|
|
|||
|
|
@ -928,75 +928,12 @@ static void handle_request_pointer_set_cursor(struct wl_listener *listener,
|
|||
event->hotspot_y, focused_client);
|
||||
}
|
||||
|
||||
static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) {
|
||||
struct sway_cursor *cursor = wl_container_of(
|
||||
listener, cursor, pinch_begin);
|
||||
struct wlr_pointer_pinch_begin_event *event = data;
|
||||
cursor_handle_activity_from_device(cursor, &event->pointer->base);
|
||||
wlr_pointer_gestures_v1_send_pinch_begin(
|
||||
cursor->pointer_gestures, cursor->seat->wlr_seat,
|
||||
event->time_msec, event->fingers);
|
||||
}
|
||||
|
||||
static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) {
|
||||
struct sway_cursor *cursor = wl_container_of(
|
||||
listener, cursor, pinch_update);
|
||||
struct wlr_pointer_pinch_update_event *event = data;
|
||||
cursor_handle_activity_from_device(cursor, &event->pointer->base);
|
||||
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_pointer_pinch_end(struct wl_listener *listener, void *data) {
|
||||
struct sway_cursor *cursor = wl_container_of(
|
||||
listener, cursor, pinch_end);
|
||||
struct wlr_pointer_pinch_end_event *event = data;
|
||||
cursor_handle_activity_from_device(cursor, &event->pointer->base);
|
||||
wlr_pointer_gestures_v1_send_pinch_end(
|
||||
cursor->pointer_gestures, cursor->seat->wlr_seat,
|
||||
event->time_msec, event->cancelled);
|
||||
}
|
||||
|
||||
static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) {
|
||||
struct sway_cursor *cursor = wl_container_of(
|
||||
listener, cursor, swipe_begin);
|
||||
struct wlr_pointer_swipe_begin_event *event = data;
|
||||
cursor_handle_activity_from_device(cursor, &event->pointer->base);
|
||||
wlr_pointer_gestures_v1_send_swipe_begin(
|
||||
cursor->pointer_gestures, cursor->seat->wlr_seat,
|
||||
event->time_msec, event->fingers);
|
||||
}
|
||||
|
||||
static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) {
|
||||
struct sway_cursor *cursor = wl_container_of(
|
||||
listener, cursor, swipe_update);
|
||||
struct wlr_pointer_swipe_update_event *event = data;
|
||||
cursor_handle_activity_from_device(cursor, &event->pointer->base);
|
||||
wlr_pointer_gestures_v1_send_swipe_update(
|
||||
cursor->pointer_gestures, cursor->seat->wlr_seat,
|
||||
event->time_msec, event->dx, event->dy);
|
||||
}
|
||||
|
||||
static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) {
|
||||
struct sway_cursor *cursor = wl_container_of(
|
||||
listener, cursor, swipe_end);
|
||||
struct wlr_pointer_swipe_end_event *event = data;
|
||||
cursor_handle_activity_from_device(cursor, &event->pointer->base);
|
||||
wlr_pointer_gestures_v1_send_swipe_end(
|
||||
cursor->pointer_gestures, cursor->seat->wlr_seat,
|
||||
event->time_msec, event->cancelled);
|
||||
}
|
||||
|
||||
static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) {
|
||||
struct sway_cursor *cursor = wl_container_of(
|
||||
listener, cursor, hold_begin);
|
||||
struct wlr_pointer_hold_begin_event *event = data;
|
||||
cursor_handle_activity_from_device(cursor, &event->pointer->base);
|
||||
wlr_pointer_gestures_v1_send_hold_begin(
|
||||
cursor->pointer_gestures, cursor->seat->wlr_seat,
|
||||
event->time_msec, event->fingers);
|
||||
seatop_hold_begin(cursor->seat, event);
|
||||
}
|
||||
|
||||
static void handle_pointer_hold_end(struct wl_listener *listener, void *data) {
|
||||
|
|
@ -1004,9 +941,55 @@ static void handle_pointer_hold_end(struct wl_listener *listener, void *data) {
|
|||
listener, cursor, hold_end);
|
||||
struct wlr_pointer_hold_end_event *event = data;
|
||||
cursor_handle_activity_from_device(cursor, &event->pointer->base);
|
||||
wlr_pointer_gestures_v1_send_hold_end(
|
||||
cursor->pointer_gestures, cursor->seat->wlr_seat,
|
||||
event->time_msec, event->cancelled);
|
||||
seatop_hold_end(cursor->seat, event);
|
||||
}
|
||||
|
||||
static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) {
|
||||
struct sway_cursor *cursor = wl_container_of(
|
||||
listener, cursor, pinch_begin);
|
||||
struct wlr_pointer_pinch_begin_event *event = data;
|
||||
cursor_handle_activity_from_device(cursor, &event->pointer->base);
|
||||
seatop_pinch_begin(cursor->seat, event);
|
||||
}
|
||||
|
||||
static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) {
|
||||
struct sway_cursor *cursor = wl_container_of(
|
||||
listener, cursor, pinch_update);
|
||||
struct wlr_pointer_pinch_update_event *event = data;
|
||||
cursor_handle_activity_from_device(cursor, &event->pointer->base);
|
||||
seatop_pinch_update(cursor->seat, event);
|
||||
}
|
||||
|
||||
static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) {
|
||||
struct sway_cursor *cursor = wl_container_of(
|
||||
listener, cursor, pinch_end);
|
||||
struct wlr_pointer_pinch_end_event *event = data;
|
||||
cursor_handle_activity_from_device(cursor, &event->pointer->base);
|
||||
seatop_pinch_end(cursor->seat, event);
|
||||
}
|
||||
|
||||
static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) {
|
||||
struct sway_cursor *cursor = wl_container_of(
|
||||
listener, cursor, swipe_begin);
|
||||
struct wlr_pointer_swipe_begin_event *event = data;
|
||||
cursor_handle_activity_from_device(cursor, &event->pointer->base);
|
||||
seatop_swipe_begin(cursor->seat, event);
|
||||
}
|
||||
|
||||
static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) {
|
||||
struct sway_cursor *cursor = wl_container_of(
|
||||
listener, cursor, swipe_update);
|
||||
struct wlr_pointer_swipe_update_event *event = data;
|
||||
cursor_handle_activity_from_device(cursor, &event->pointer->base);
|
||||
seatop_swipe_update(cursor->seat, event);
|
||||
}
|
||||
|
||||
static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) {
|
||||
struct sway_cursor *cursor = wl_container_of(
|
||||
listener, cursor, swipe_end);
|
||||
struct wlr_pointer_swipe_end_event *event = data;
|
||||
cursor_handle_activity_from_device(cursor, &event->pointer->base);
|
||||
seatop_swipe_end(cursor->seat, event);
|
||||
}
|
||||
|
||||
static void handle_image_surface_destroy(struct wl_listener *listener,
|
||||
|
|
@ -1080,14 +1063,14 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
|
|||
wl_event_source_remove(cursor->hide_source);
|
||||
|
||||
wl_list_remove(&cursor->image_surface_destroy.link);
|
||||
wl_list_remove(&cursor->hold_begin.link);
|
||||
wl_list_remove(&cursor->hold_end.link);
|
||||
wl_list_remove(&cursor->pinch_begin.link);
|
||||
wl_list_remove(&cursor->pinch_update.link);
|
||||
wl_list_remove(&cursor->pinch_end.link);
|
||||
wl_list_remove(&cursor->swipe_begin.link);
|
||||
wl_list_remove(&cursor->swipe_update.link);
|
||||
wl_list_remove(&cursor->swipe_end.link);
|
||||
wl_list_remove(&cursor->hold_begin.link);
|
||||
wl_list_remove(&cursor->hold_end.link);
|
||||
wl_list_remove(&cursor->motion.link);
|
||||
wl_list_remove(&cursor->motion_absolute.link);
|
||||
wl_list_remove(&cursor->button.link);
|
||||
|
|
@ -1131,23 +1114,27 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
|
|||
wl_list_init(&cursor->image_surface_destroy.link);
|
||||
cursor->image_surface_destroy.notify = handle_image_surface_destroy;
|
||||
|
||||
// gesture events
|
||||
cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display);
|
||||
cursor->pinch_begin.notify = handle_pointer_pinch_begin;
|
||||
wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin);
|
||||
cursor->pinch_update.notify = handle_pointer_pinch_update;
|
||||
wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update);
|
||||
cursor->pinch_end.notify = handle_pointer_pinch_end;
|
||||
wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end);
|
||||
cursor->swipe_begin.notify = handle_pointer_swipe_begin;
|
||||
wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin);
|
||||
cursor->swipe_update.notify = handle_pointer_swipe_update;
|
||||
wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update);
|
||||
cursor->swipe_end.notify = handle_pointer_swipe_end;
|
||||
wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end);
|
||||
cursor->hold_begin.notify = handle_pointer_hold_begin;
|
||||
|
||||
wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin);
|
||||
cursor->hold_end.notify = handle_pointer_hold_end;
|
||||
cursor->hold_begin.notify = handle_pointer_hold_begin;
|
||||
wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end);
|
||||
cursor->hold_end.notify = handle_pointer_hold_end;
|
||||
|
||||
wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin);
|
||||
cursor->pinch_begin.notify = handle_pointer_pinch_begin;
|
||||
wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update);
|
||||
cursor->pinch_update.notify = handle_pointer_pinch_update;
|
||||
wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end);
|
||||
cursor->pinch_end.notify = handle_pointer_pinch_end;
|
||||
|
||||
wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin);
|
||||
cursor->swipe_begin.notify = handle_pointer_swipe_begin;
|
||||
wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update);
|
||||
cursor->swipe_update.notify = handle_pointer_swipe_update;
|
||||
wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end);
|
||||
cursor->swipe_end.notify = handle_pointer_swipe_end;
|
||||
|
||||
// input events
|
||||
wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
|
||||
|
|
|
|||
|
|
@ -1614,6 +1614,62 @@ void seatop_tablet_tool_motion(struct sway_seat *seat,
|
|||
}
|
||||
}
|
||||
|
||||
void seatop_hold_begin(struct sway_seat *seat,
|
||||
struct wlr_pointer_hold_begin_event *event) {
|
||||
if (seat->seatop_impl->hold_begin) {
|
||||
seat->seatop_impl->hold_begin(seat, event);
|
||||
}
|
||||
}
|
||||
|
||||
void seatop_hold_end(struct sway_seat *seat,
|
||||
struct wlr_pointer_hold_end_event *event) {
|
||||
if (seat->seatop_impl->hold_end) {
|
||||
seat->seatop_impl->hold_end(seat, event);
|
||||
}
|
||||
}
|
||||
|
||||
void seatop_pinch_begin(struct sway_seat *seat,
|
||||
struct wlr_pointer_pinch_begin_event *event) {
|
||||
if (seat->seatop_impl->pinch_begin) {
|
||||
seat->seatop_impl->pinch_begin(seat, event);
|
||||
}
|
||||
}
|
||||
|
||||
void seatop_pinch_update(struct sway_seat *seat,
|
||||
struct wlr_pointer_pinch_update_event *event) {
|
||||
if (seat->seatop_impl->pinch_update) {
|
||||
seat->seatop_impl->pinch_update(seat, event);
|
||||
}
|
||||
}
|
||||
|
||||
void seatop_pinch_end(struct sway_seat *seat,
|
||||
struct wlr_pointer_pinch_end_event *event) {
|
||||
if (seat->seatop_impl->pinch_end) {
|
||||
seat->seatop_impl->pinch_end(seat, event);
|
||||
}
|
||||
}
|
||||
|
||||
void seatop_swipe_begin(struct sway_seat *seat,
|
||||
struct wlr_pointer_swipe_begin_event *event) {
|
||||
if (seat->seatop_impl->swipe_begin) {
|
||||
seat->seatop_impl->swipe_begin(seat, event);
|
||||
}
|
||||
}
|
||||
|
||||
void seatop_swipe_update(struct sway_seat *seat,
|
||||
struct wlr_pointer_swipe_update_event *event) {
|
||||
if (seat->seatop_impl->swipe_update) {
|
||||
seat->seatop_impl->swipe_update(seat, event);
|
||||
}
|
||||
}
|
||||
|
||||
void seatop_swipe_end(struct sway_seat *seat,
|
||||
struct wlr_pointer_swipe_end_event *event) {
|
||||
if (seat->seatop_impl->swipe_end) {
|
||||
seat->seatop_impl->swipe_end(seat, event);
|
||||
}
|
||||
}
|
||||
|
||||
void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) {
|
||||
if (seat->seatop_impl->rebase) {
|
||||
seat->seatop_impl->rebase(seat, time_msec);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ sway_sources = files(
|
|||
'commands/force_focus_wrapping.c',
|
||||
'commands/fullscreen.c',
|
||||
'commands/gaps.c',
|
||||
'commands/gesture.c',
|
||||
'commands/hide_edge_borders.c',
|
||||
'commands/inhibit_idle.c',
|
||||
'commands/kill.c',
|
||||
|
|
|
|||
|
|
@ -488,6 +488,62 @@ runtime.
|
|||
bindswitch lid:toggle exec echo "Lid moved"
|
||||
```
|
||||
|
||||
*bindgesture* [--exact] [--input-device=<device>] [--no-warn] \
|
||||
<gesture>[:<fingers>][:directions] <command>
|
||||
Binds _gesture_ to execute the sway command _command_ when detected.
|
||||
Currently supports the _hold_, _pinch_ or _swipe_ gesture. Optionally
|
||||
can be limited to bind to a certain number of _fingers_ or, for a
|
||||
_pinch_ or _swipe_ gesture, to certain _directions_.
|
||||
|
||||
[[ *type*
|
||||
:[ *fingers*
|
||||
:< *direction*
|
||||
| hold
|
||||
:- 1 - 5
|
||||
: none
|
||||
| swipe
|
||||
: 3 - 5
|
||||
: up, down, left, right
|
||||
| pinch
|
||||
: 2 - 5
|
||||
: all above + inward, outward, clockwise, counterclockwise
|
||||
|
||||
The _fingers_ can be limited to any sensible number or left empty to accept
|
||||
any finger counts.
|
||||
Valid directions are _up_, _down_, _left_ and _right_, as well as _inward_,
|
||||
_outward_, _clockwise_, _counterclockwise_ for the _pinch_ gesture.
|
||||
Multiple directions can be combined by a plus.
|
||||
|
||||
If a _input-device_ is given, the binding will only be executed for
|
||||
that input device and will be executed instead of any binding that is
|
||||
generic to all devices. By default, if you overwrite a binding,
|
||||
swaynag will give you a warning. To silence this, use the _--no-warn_ flag.
|
||||
|
||||
The _--exact_ flag can be used to ensure a binding only matches when exactly
|
||||
all specified directions are matched and nothing more. If there is matching
|
||||
binding with _--exact_, it will be preferred.
|
||||
|
||||
The priority for matching bindings is as follows: input device, then
|
||||
exact matches followed by matches with the highest number of matching
|
||||
directions.
|
||||
|
||||
Gestures executed while the pointer is above a bar are not handled by sway.
|
||||
See the respective documentation, e.g. *bindgesture* in *sway-bar*(5).
|
||||
|
||||
Example:
|
||||
```
|
||||
# Allow switching between workspaces with left and right swipes
|
||||
bindgesture swipe:right workspace prev
|
||||
bindgesture swipe:left workspace next
|
||||
|
||||
# Allow container movements by pinching them
|
||||
bindgesture pinch:inward+up move up
|
||||
bindgesture pinch:inward+down move down
|
||||
bindgesture pinch:inward+left move left
|
||||
bindgesture pinch:inward+right move right
|
||||
|
||||
```
|
||||
|
||||
*client.background* <color>
|
||||
This command is ignored and is only present for i3 compatibility.
|
||||
|
||||
|
|
@ -792,6 +848,11 @@ The default colors are:
|
|||
*unbindswitch* <switch>:<state>
|
||||
Removes a binding for when <switch> changes to <state>.
|
||||
|
||||
*unbindgesture* [--exact] [--input-device=<device>] \
|
||||
<gesture>[:<fingers>][:directions]
|
||||
Removes a binding for the specified _gesture_, _fingers_
|
||||
and _directions_ combination.
|
||||
|
||||
*unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \
|
||||
[--to-code] [--input-device=<device>] <key combo>
|
||||
Removes the binding for _key combo_ that was previously bound with the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue