aboutsummaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
authorLibravatar Florian Franzen <Florian.Franzen@gmail.com>2022-04-23 10:27:47 +0200
committerLibravatar Simon Ser <contact@emersion.fr>2022-05-30 12:20:43 +0200
commitcab2189aa64d04ba79dc2cbf19400435b47cdbd2 (patch)
tree450ac51fbc75c73ed1dc6728bc05b08366ace785 /sway
parentAdd a Hindi (हिन्दी) translation to the README (diff)
downloadsway-cab2189aa64d04ba79dc2cbf19400435b47cdbd2.tar.gz
sway-cab2189aa64d04ba79dc2cbf19400435b47cdbd2.tar.zst
sway-cab2189aa64d04ba79dc2cbf19400435b47cdbd2.zip
sway: add bindgesture command
Co-authored-by: Michael Weiser <michael.weiser@gmx.de>
Diffstat (limited to 'sway')
-rw-r--r--sway/commands.c3
-rw-r--r--sway/commands/gesture.c166
-rw-r--r--sway/commands/mode.c3
-rw-r--r--sway/config.c7
-rw-r--r--sway/input/cursor.c89
-rw-r--r--sway/input/seat.c56
-rw-r--r--sway/input/seatop_default.c310
-rw-r--r--sway/meson.build1
-rw-r--r--sway/sway.5.scd61
9 files changed, 644 insertions, 52 deletions
diff --git a/sway/commands.c b/sway/commands.c
index 5a1fd32e..0ced71ec 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -46,6 +46,7 @@ static const struct cmd_handler handlers[] = {
46 { "assign", cmd_assign }, 46 { "assign", cmd_assign },
47 { "bar", cmd_bar }, 47 { "bar", cmd_bar },
48 { "bindcode", cmd_bindcode }, 48 { "bindcode", cmd_bindcode },
49 { "bindgesture", cmd_bindgesture },
49 { "bindswitch", cmd_bindswitch }, 50 { "bindswitch", cmd_bindswitch },
50 { "bindsym", cmd_bindsym }, 51 { "bindsym", cmd_bindsym },
51 { "client.background", cmd_client_noop }, 52 { "client.background", cmd_client_noop },
@@ -92,6 +93,7 @@ static const struct cmd_handler handlers[] = {
92 { "titlebar_border_thickness", cmd_titlebar_border_thickness }, 93 { "titlebar_border_thickness", cmd_titlebar_border_thickness },
93 { "titlebar_padding", cmd_titlebar_padding }, 94 { "titlebar_padding", cmd_titlebar_padding },
94 { "unbindcode", cmd_unbindcode }, 95 { "unbindcode", cmd_unbindcode },
96 { "unbindgesture", cmd_unbindgesture },
95 { "unbindswitch", cmd_unbindswitch }, 97 { "unbindswitch", cmd_unbindswitch },
96 { "unbindsym", cmd_unbindsym }, 98 { "unbindsym", cmd_unbindsym },
97 { "workspace", cmd_workspace }, 99 { "workspace", cmd_workspace },
@@ -407,6 +409,7 @@ struct cmd_results *config_command(char *exec, char **new_block) {
407 && handler->handle != cmd_bindsym 409 && handler->handle != cmd_bindsym
408 && handler->handle != cmd_bindcode 410 && handler->handle != cmd_bindcode
409 && handler->handle != cmd_bindswitch 411 && handler->handle != cmd_bindswitch
412 && handler->handle != cmd_bindgesture
410 && handler->handle != cmd_set 413 && handler->handle != cmd_set
411 && handler->handle != cmd_for_window 414 && handler->handle != cmd_for_window
412 && (*argv[i] == '\"' || *argv[i] == '\'')) { 415 && (*argv[i] == '\"' || *argv[i] == '\'')) {
diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c
new file mode 100644
index 00000000..d4442cc3
--- /dev/null
+++ b/sway/commands/gesture.c
@@ -0,0 +1,166 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h"
3
4#include "gesture.h"
5#include "log.h"
6#include "stringop.h"
7#include "sway/commands.h"
8
9void free_gesture_binding(struct sway_gesture_binding *binding) {
10 if (!binding) {
11 return;
12 }
13 free(binding->input);
14 free(binding->command);
15 free(binding);
16}
17
18/**
19 * Returns true if the bindings have the same gesture type, direction, etc
20 */
21static bool binding_gesture_equal(struct sway_gesture_binding *binding_a,
22 struct sway_gesture_binding *binding_b) {
23 if (strcmp(binding_a->input, binding_b->input) != 0) {
24 return false;
25 }
26
27 if (!gesture_equal(&binding_a->gesture, &binding_b->gesture)) {
28 return false;
29 }
30
31 if ((binding_a->flags & BINDING_EXACT) !=
32 (binding_b->flags & BINDING_EXACT)) {
33 return false;
34 }
35 return true;
36}
37
38/**
39 * Add gesture binding to config
40 */
41static struct cmd_results *gesture_binding_add(
42 struct sway_gesture_binding *binding,
43 const char *gesturecombo, bool warn) {
44 list_t *mode_bindings = config->current_mode->gesture_bindings;
45 // overwrite the binding if it already exists
46 bool overwritten = false;
47 for (int i = 0; i < mode_bindings->length; ++i) {
48 struct sway_gesture_binding *config_binding = mode_bindings->items[i];
49 if (binding_gesture_equal(binding, config_binding)) {
50 sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`",
51 gesturecombo, binding->command, config_binding->command);
52 if (warn) {
53 config_add_swaynag_warning("Overwriting binding"
54 "'%s' to `%s` from `%s`",
55 gesturecombo, binding->command,
56 config_binding->command);
57 }
58 free_gesture_binding(config_binding);
59 mode_bindings->items[i] = binding;
60 overwritten = true;
61 }
62 }
63
64 if (!overwritten) {
65 list_add(mode_bindings, binding);
66 sway_log(SWAY_DEBUG, "bindgesture - Bound %s to command `%s`",
67 gesturecombo, binding->command);
68 }
69
70 return cmd_results_new(CMD_SUCCESS, NULL);
71}
72
73/**
74 * Remove gesture binding from config
75 */
76static struct cmd_results *gesture_binding_remove(
77 struct sway_gesture_binding *binding, const char *gesturecombo) {
78 list_t *mode_bindings = config->current_mode->gesture_bindings;
79 for (int i = 0; i < mode_bindings->length; ++i) {
80 struct sway_gesture_binding *config_binding = mode_bindings->items[i];
81 if (binding_gesture_equal(binding, config_binding)) {
82 free_gesture_binding(config_binding);
83 free_gesture_binding(binding);
84 list_del(mode_bindings, i);
85 sway_log(SWAY_DEBUG, "unbindgesture - Unbound %s gesture",
86 gesturecombo);
87 return cmd_results_new(CMD_SUCCESS, NULL);
88 }
89 }
90
91 free_gesture_binding(binding);
92 return cmd_results_new(CMD_FAILURE, "Could not find gesture binding `%s`",
93 gesturecombo);
94}
95
96/**
97 * Parse and execute bindgesture or unbindgesture command.
98 */
99static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, bool unbind) {
100 int minargs = 2;
101 char *bindtype = "bindgesture";
102 if (unbind) {
103 minargs--;
104 bindtype = "unbindgesture";
105 }
106
107 struct cmd_results *error = NULL;
108 if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) {
109 return error;
110 }
111 struct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding));
112 if (!binding) {
113 return cmd_results_new(CMD_FAILURE, "Unable to allocate binding");
114 }
115 binding->input = strdup("*");
116
117 bool warn = true;
118
119 // Handle flags
120 while (argc > 0) {
121 if (strcmp("--exact", argv[0]) == 0) {
122 binding->flags |= BINDING_EXACT;
123 } else if (strcmp("--no-warn", argv[0]) == 0) {
124 warn = false;
125 } else if (strncmp("--input-device=", argv[0],
126 strlen("--input-device=")) == 0) {
127 free(binding->input);
128 binding->input = strdup(argv[0] + strlen("--input-device="));
129 } else {
130 break;
131 }
132 argv++;
133 argc--;
134 }
135
136 if (argc < minargs) {
137 free(binding);
138 return cmd_results_new(CMD_FAILURE,
139 "Invalid %s command (expected at least %d "
140 "non-option arguments, got %d)", bindtype, minargs, argc);
141 }
142
143 char* errmsg = NULL;
144 if ((errmsg = gesture_parse(argv[0], &binding->gesture))) {
145 free(binding);
146 struct cmd_results *final = cmd_results_new(CMD_FAILURE,
147 "Invalid %s command (%s)",
148 bindtype, errmsg);
149 free(errmsg);
150 return final;
151 }
152
153 if (unbind) {
154 return gesture_binding_remove(binding, argv[0]);
155 }
156 binding->command = join_args(argv + 1, argc - 1);
157 return gesture_binding_add(binding, argv[0], warn);
158}
159
160struct cmd_results *cmd_bindgesture(int argc, char **argv) {
161 return cmd_bind_or_unbind_gesture(argc, argv, false);
162}
163
164struct cmd_results *cmd_unbindgesture(int argc, char **argv) {
165 return cmd_bind_or_unbind_gesture(argc, argv, true);
166}
diff --git a/sway/commands/mode.c b/sway/commands/mode.c
index e23e4ee4..7263efcb 100644
--- a/sway/commands/mode.c
+++ b/sway/commands/mode.c
@@ -11,10 +11,12 @@
11// Must be in order for the bsearch 11// Must be in order for the bsearch
12static const struct cmd_handler mode_handlers[] = { 12static const struct cmd_handler mode_handlers[] = {
13 { "bindcode", cmd_bindcode }, 13 { "bindcode", cmd_bindcode },
14 { "bindgesture", cmd_bindgesture },
14 { "bindswitch", cmd_bindswitch }, 15 { "bindswitch", cmd_bindswitch },
15 { "bindsym", cmd_bindsym }, 16 { "bindsym", cmd_bindsym },
16 { "set", cmd_set }, 17 { "set", cmd_set },
17 { "unbindcode", cmd_unbindcode }, 18 { "unbindcode", cmd_unbindcode },
19 { "unbindgesture", cmd_unbindgesture },
18 { "unbindswitch", cmd_unbindswitch }, 20 { "unbindswitch", cmd_unbindswitch },
19 { "unbindsym", cmd_unbindsym }, 21 { "unbindsym", cmd_unbindsym },
20}; 22};
@@ -59,6 +61,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) {
59 mode->keycode_bindings = create_list(); 61 mode->keycode_bindings = create_list();
60 mode->mouse_bindings = create_list(); 62 mode->mouse_bindings = create_list();
61 mode->switch_bindings = create_list(); 63 mode->switch_bindings = create_list();
64 mode->gesture_bindings = create_list();
62 mode->pango = pango; 65 mode->pango = pango;
63 list_add(config->modes, mode); 66 list_add(config->modes, mode);
64 } 67 }
diff --git a/sway/config.c b/sway/config.c
index e4745a5c..8220ece0 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -82,6 +82,12 @@ static void free_mode(struct sway_mode *mode) {
82 } 82 }
83 list_free(mode->switch_bindings); 83 list_free(mode->switch_bindings);
84 } 84 }
85 if (mode->gesture_bindings) {
86 for (int i = 0; i < mode->gesture_bindings->length; i++) {
87 free_gesture_binding(mode->gesture_bindings->items[i]);
88 }
89 list_free(mode->gesture_bindings);
90 }
85 free(mode); 91 free(mode);
86} 92}
87 93
@@ -222,6 +228,7 @@ static void config_defaults(struct sway_config *config) {
222 if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; 228 if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup;
223 if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; 229 if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup;
224 if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; 230 if (!(config->current_mode->switch_bindings = create_list())) goto cleanup;
231 if (!(config->current_mode->gesture_bindings = create_list())) goto cleanup;
225 list_add(config->modes, config->current_mode); 232 list_add(config->modes, config->current_mode);
226 233
227 config->floating_mod = 0; 234 config->floating_mod = 0;
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 0b2f03a2..e87594ee 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -928,14 +928,28 @@ static void handle_request_pointer_set_cursor(struct wl_listener *listener,
928 event->hotspot_y, focused_client); 928 event->hotspot_y, focused_client);
929} 929}
930 930
931static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) {
932 struct sway_cursor *cursor = wl_container_of(
933 listener, cursor, hold_begin);
934 struct wlr_pointer_hold_begin_event *event = data;
935 cursor_handle_activity_from_device(cursor, &event->pointer->base);
936 seatop_hold_begin(cursor->seat, event);
937}
938
939static void handle_pointer_hold_end(struct wl_listener *listener, void *data) {
940 struct sway_cursor *cursor = wl_container_of(
941 listener, cursor, hold_end);
942 struct wlr_pointer_hold_end_event *event = data;
943 cursor_handle_activity_from_device(cursor, &event->pointer->base);
944 seatop_hold_end(cursor->seat, event);
945}
946
931static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { 947static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) {
932 struct sway_cursor *cursor = wl_container_of( 948 struct sway_cursor *cursor = wl_container_of(
933 listener, cursor, pinch_begin); 949 listener, cursor, pinch_begin);
934 struct wlr_pointer_pinch_begin_event *event = data; 950 struct wlr_pointer_pinch_begin_event *event = data;
935 cursor_handle_activity_from_device(cursor, &event->pointer->base); 951 cursor_handle_activity_from_device(cursor, &event->pointer->base);
936 wlr_pointer_gestures_v1_send_pinch_begin( 952 seatop_pinch_begin(cursor->seat, event);
937 cursor->pointer_gestures, cursor->seat->wlr_seat,
938 event->time_msec, event->fingers);
939} 953}
940 954
941static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { 955static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) {
@@ -943,10 +957,7 @@ static void handle_pointer_pinch_update(struct wl_listener *listener, void *data
943 listener, cursor, pinch_update); 957 listener, cursor, pinch_update);
944 struct wlr_pointer_pinch_update_event *event = data; 958 struct wlr_pointer_pinch_update_event *event = data;
945 cursor_handle_activity_from_device(cursor, &event->pointer->base); 959 cursor_handle_activity_from_device(cursor, &event->pointer->base);
946 wlr_pointer_gestures_v1_send_pinch_update( 960 seatop_pinch_update(cursor->seat, event);
947 cursor->pointer_gestures, cursor->seat->wlr_seat,
948 event->time_msec, event->dx, event->dy,
949 event->scale, event->rotation);
950} 961}
951 962
952static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { 963static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) {
@@ -954,9 +965,7 @@ static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) {
954 listener, cursor, pinch_end); 965 listener, cursor, pinch_end);
955 struct wlr_pointer_pinch_end_event *event = data; 966 struct wlr_pointer_pinch_end_event *event = data;
956 cursor_handle_activity_from_device(cursor, &event->pointer->base); 967 cursor_handle_activity_from_device(cursor, &event->pointer->base);
957 wlr_pointer_gestures_v1_send_pinch_end( 968 seatop_pinch_end(cursor->seat, event);
958 cursor->pointer_gestures, cursor->seat->wlr_seat,
959 event->time_msec, event->cancelled);
960} 969}
961 970
962static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { 971static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) {
@@ -964,9 +973,7 @@ static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data)
964 listener, cursor, swipe_begin); 973 listener, cursor, swipe_begin);
965 struct wlr_pointer_swipe_begin_event *event = data; 974 struct wlr_pointer_swipe_begin_event *event = data;
966 cursor_handle_activity_from_device(cursor, &event->pointer->base); 975 cursor_handle_activity_from_device(cursor, &event->pointer->base);
967 wlr_pointer_gestures_v1_send_swipe_begin( 976 seatop_swipe_begin(cursor->seat, event);
968 cursor->pointer_gestures, cursor->seat->wlr_seat,
969 event->time_msec, event->fingers);
970} 977}
971 978
972static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { 979static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) {
@@ -974,9 +981,7 @@ static void handle_pointer_swipe_update(struct wl_listener *listener, void *data
974 listener, cursor, swipe_update); 981 listener, cursor, swipe_update);
975 struct wlr_pointer_swipe_update_event *event = data; 982 struct wlr_pointer_swipe_update_event *event = data;
976 cursor_handle_activity_from_device(cursor, &event->pointer->base); 983 cursor_handle_activity_from_device(cursor, &event->pointer->base);
977 wlr_pointer_gestures_v1_send_swipe_update( 984 seatop_swipe_update(cursor->seat, event);
978 cursor->pointer_gestures, cursor->seat->wlr_seat,
979 event->time_msec, event->dx, event->dy);
980} 985}
981 986
982static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { 987static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) {
@@ -984,29 +989,7 @@ static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) {
984 listener, cursor, swipe_end); 989 listener, cursor, swipe_end);
985 struct wlr_pointer_swipe_end_event *event = data; 990 struct wlr_pointer_swipe_end_event *event = data;
986 cursor_handle_activity_from_device(cursor, &event->pointer->base); 991 cursor_handle_activity_from_device(cursor, &event->pointer->base);
987 wlr_pointer_gestures_v1_send_swipe_end( 992 seatop_swipe_end(cursor->seat, event);
988 cursor->pointer_gestures, cursor->seat->wlr_seat,
989 event->time_msec, event->cancelled);
990}
991
992static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) {
993 struct sway_cursor *cursor = wl_container_of(
994 listener, cursor, hold_begin);
995 struct wlr_pointer_hold_begin_event *event = data;
996 cursor_handle_activity_from_device(cursor, &event->pointer->base);
997 wlr_pointer_gestures_v1_send_hold_begin(
998 cursor->pointer_gestures, cursor->seat->wlr_seat,
999 event->time_msec, event->fingers);
1000}
1001
1002static void handle_pointer_hold_end(struct wl_listener *listener, void *data) {
1003 struct sway_cursor *cursor = wl_container_of(
1004 listener, cursor, hold_end);
1005 struct wlr_pointer_hold_end_event *event = data;
1006 cursor_handle_activity_from_device(cursor, &event->pointer->base);
1007 wlr_pointer_gestures_v1_send_hold_end(
1008 cursor->pointer_gestures, cursor->seat->wlr_seat,
1009 event->time_msec, event->cancelled);
1010} 993}
1011 994
1012static void handle_image_surface_destroy(struct wl_listener *listener, 995static void handle_image_surface_destroy(struct wl_listener *listener,
@@ -1080,14 +1063,14 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1080 wl_event_source_remove(cursor->hide_source); 1063 wl_event_source_remove(cursor->hide_source);
1081 1064
1082 wl_list_remove(&cursor->image_surface_destroy.link); 1065 wl_list_remove(&cursor->image_surface_destroy.link);
1066 wl_list_remove(&cursor->hold_begin.link);
1067 wl_list_remove(&cursor->hold_end.link);
1083 wl_list_remove(&cursor->pinch_begin.link); 1068 wl_list_remove(&cursor->pinch_begin.link);
1084 wl_list_remove(&cursor->pinch_update.link); 1069 wl_list_remove(&cursor->pinch_update.link);
1085 wl_list_remove(&cursor->pinch_end.link); 1070 wl_list_remove(&cursor->pinch_end.link);
1086 wl_list_remove(&cursor->swipe_begin.link); 1071 wl_list_remove(&cursor->swipe_begin.link);
1087 wl_list_remove(&cursor->swipe_update.link); 1072 wl_list_remove(&cursor->swipe_update.link);
1088 wl_list_remove(&cursor->swipe_end.link); 1073 wl_list_remove(&cursor->swipe_end.link);
1089 wl_list_remove(&cursor->hold_begin.link);
1090 wl_list_remove(&cursor->hold_end.link);
1091 wl_list_remove(&cursor->motion.link); 1074 wl_list_remove(&cursor->motion.link);
1092 wl_list_remove(&cursor->motion_absolute.link); 1075 wl_list_remove(&cursor->motion_absolute.link);
1093 wl_list_remove(&cursor->button.link); 1076 wl_list_remove(&cursor->button.link);
@@ -1131,23 +1114,27 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1131 wl_list_init(&cursor->image_surface_destroy.link); 1114 wl_list_init(&cursor->image_surface_destroy.link);
1132 cursor->image_surface_destroy.notify = handle_image_surface_destroy; 1115 cursor->image_surface_destroy.notify = handle_image_surface_destroy;
1133 1116
1117 // gesture events
1134 cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display); 1118 cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display);
1135 cursor->pinch_begin.notify = handle_pointer_pinch_begin; 1119
1120 wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin);
1121 cursor->hold_begin.notify = handle_pointer_hold_begin;
1122 wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end);
1123 cursor->hold_end.notify = handle_pointer_hold_end;
1124
1136 wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin); 1125 wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin);
1137 cursor->pinch_update.notify = handle_pointer_pinch_update; 1126 cursor->pinch_begin.notify = handle_pointer_pinch_begin;
1138 wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update); 1127 wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update);
1139 cursor->pinch_end.notify = handle_pointer_pinch_end; 1128 cursor->pinch_update.notify = handle_pointer_pinch_update;
1140 wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end); 1129 wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end);
1141 cursor->swipe_begin.notify = handle_pointer_swipe_begin; 1130 cursor->pinch_end.notify = handle_pointer_pinch_end;
1131
1142 wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin); 1132 wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin);
1143 cursor->swipe_update.notify = handle_pointer_swipe_update; 1133 cursor->swipe_begin.notify = handle_pointer_swipe_begin;
1144 wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); 1134 wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update);
1145 cursor->swipe_end.notify = handle_pointer_swipe_end; 1135 cursor->swipe_update.notify = handle_pointer_swipe_update;
1146 wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); 1136 wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end);
1147 cursor->hold_begin.notify = handle_pointer_hold_begin; 1137 cursor->swipe_end.notify = handle_pointer_swipe_end;
1148 wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin);
1149 cursor->hold_end.notify = handle_pointer_hold_end;
1150 wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end);
1151 1138
1152 // input events 1139 // input events
1153 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); 1140 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 11c78154..fe61e0fe 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -1614,6 +1614,62 @@ void seatop_tablet_tool_motion(struct sway_seat *seat,
1614 } 1614 }
1615} 1615}
1616 1616
1617void seatop_hold_begin(struct sway_seat *seat,
1618 struct wlr_pointer_hold_begin_event *event) {
1619 if (seat->seatop_impl->hold_begin) {
1620 seat->seatop_impl->hold_begin(seat, event);
1621 }
1622}
1623
1624void seatop_hold_end(struct sway_seat *seat,
1625 struct wlr_pointer_hold_end_event *event) {
1626 if (seat->seatop_impl->hold_end) {
1627 seat->seatop_impl->hold_end(seat, event);
1628 }
1629}
1630
1631void seatop_pinch_begin(struct sway_seat *seat,
1632 struct wlr_pointer_pinch_begin_event *event) {
1633 if (seat->seatop_impl->pinch_begin) {
1634 seat->seatop_impl->pinch_begin(seat, event);
1635 }
1636}
1637
1638void seatop_pinch_update(struct sway_seat *seat,
1639 struct wlr_pointer_pinch_update_event *event) {
1640 if (seat->seatop_impl->pinch_update) {
1641 seat->seatop_impl->pinch_update(seat, event);
1642 }
1643}
1644
1645void seatop_pinch_end(struct sway_seat *seat,
1646 struct wlr_pointer_pinch_end_event *event) {
1647 if (seat->seatop_impl->pinch_end) {
1648 seat->seatop_impl->pinch_end(seat, event);
1649 }
1650}
1651
1652void seatop_swipe_begin(struct sway_seat *seat,
1653 struct wlr_pointer_swipe_begin_event *event) {
1654 if (seat->seatop_impl->swipe_begin) {
1655 seat->seatop_impl->swipe_begin(seat, event);
1656 }
1657}
1658
1659void seatop_swipe_update(struct sway_seat *seat,
1660 struct wlr_pointer_swipe_update_event *event) {
1661 if (seat->seatop_impl->swipe_update) {
1662 seat->seatop_impl->swipe_update(seat, event);
1663 }
1664}
1665
1666void seatop_swipe_end(struct sway_seat *seat,
1667 struct wlr_pointer_swipe_end_event *event) {
1668 if (seat->seatop_impl->swipe_end) {
1669 seat->seatop_impl->swipe_end(seat, event);
1670 }
1671}
1672
1617void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { 1673void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) {
1618 if (seat->seatop_impl->rebase) { 1674 if (seat->seatop_impl->rebase) {
1619 seat->seatop_impl->rebase(seat, time_msec); 1675 seat->seatop_impl->rebase(seat, time_msec);
diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c
index 15d1ca8b..2684e55a 100644
--- a/sway/input/seatop_default.c
+++ b/sway/input/seatop_default.c
@@ -4,6 +4,7 @@
4#include <wlr/types/wlr_cursor.h> 4#include <wlr/types/wlr_cursor.h>
5#include <wlr/types/wlr_tablet_v2.h> 5#include <wlr/types/wlr_tablet_v2.h>
6#include <wlr/types/wlr_xcursor_manager.h> 6#include <wlr/types/wlr_xcursor_manager.h>
7#include "gesture.h"
7#include "sway/desktop/transaction.h" 8#include "sway/desktop/transaction.h"
8#include "sway/input/cursor.h" 9#include "sway/input/cursor.h"
9#include "sway/input/seat.h" 10#include "sway/input/seat.h"
@@ -20,6 +21,7 @@ struct seatop_default_event {
20 struct sway_node *previous_node; 21 struct sway_node *previous_node;
21 uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; 22 uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP];
22 size_t pressed_button_count; 23 size_t pressed_button_count;
24 struct gesture_tracker gestures;
23}; 25};
24 26
25/*-----------------------------------------\ 27/*-----------------------------------------\
@@ -750,6 +752,304 @@ static void handle_pointer_axis(struct sway_seat *seat,
750 } 752 }
751} 753}
752 754
755/*------------------------------------\
756 * Functions used by gesture support /
757 *----------------------------------*/
758
759/**
760 * Check gesture binding for a specific gesture type and finger count.
761 * Returns true if binding is present, false otherwise
762 */
763static bool gesture_binding_check(list_t *bindings, enum gesture_type type,
764 uint8_t fingers, struct sway_input_device *device) {
765 char *input =
766 device ? input_device_get_identifier(device->wlr_device) : strdup("*");
767
768 for (int i = 0; i < bindings->length; ++i) {
769 struct sway_gesture_binding *binding = bindings->items[i];
770
771 // Check type and finger count
772 if (!gesture_check(&binding->gesture, type, fingers)) {
773 continue;
774 }
775
776 // Check that input matches
777 if (strcmp(binding->input, "*") != 0 &&
778 strcmp(binding->input, input) != 0) {
779 continue;
780 }
781
782 free(input);
783
784 return true;
785 }
786
787 free(input);
788
789 return false;
790}
791
792/**
793 * Return the gesture binding which matches gesture type, finger count
794 * and direction, otherwise return null.
795 */
796static struct sway_gesture_binding* gesture_binding_match(
797 list_t *bindings, struct gesture *gesture, const char *input) {
798 struct sway_gesture_binding *current = NULL;
799
800 // Find best matching binding
801 for (int i = 0; i < bindings->length; ++i) {
802 struct sway_gesture_binding *binding = bindings->items[i];
803 bool exact = binding->flags & BINDING_EXACT;
804
805 // Check gesture matching
806 if (!gesture_match(&binding->gesture, gesture, exact)) {
807 continue;
808 }
809
810 // Check input matching
811 if (strcmp(binding->input, "*") != 0 &&
812 strcmp(binding->input, input) != 0) {
813 continue;
814 }
815
816 // If we already have a match ...
817 if (current) {
818 // ... check if input matching is equivalent
819 if (strcmp(current->input, binding->input) == 0) {
820
821 // ... - do not override an exact binding
822 if (!exact && current->flags & BINDING_EXACT) {
823 continue;
824 }
825
826 // ... - and ensure direction matching is better or equal
827 if (gesture_compare(&current->gesture, &binding->gesture) > 0) {
828 continue;
829 }
830 } else if (strcmp(binding->input, "*") == 0) {
831 // ... do not accept worse input match
832 continue;
833 }
834 }
835
836 // Accept newer or better match
837 current = binding;
838
839 // If exact binding and input is found, quit search
840 if (strcmp(current->input, input) == 0 &&
841 gesture_compare(&current->gesture, gesture) == 0) {
842 break;
843 }
844 } // for all gesture bindings
845
846 return current;
847}
848
849// Wrapper around gesture_tracker_end to use tracker with sway bindings
850static struct sway_gesture_binding* gesture_tracker_end_and_match(
851 struct gesture_tracker *tracker, struct sway_input_device* device) {
852 // Determine name of input that received gesture
853 char *input = device
854 ? input_device_get_identifier(device->wlr_device)
855 : strdup("*");
856
857 // Match tracking result to binding
858 struct gesture *gesture = gesture_tracker_end(tracker);
859 struct sway_gesture_binding *binding = gesture_binding_match(
860 config->current_mode->gesture_bindings, gesture, input);
861 free(gesture);
862 free(input);
863
864 return binding;
865}
866
867// Small wrapper around seat_execute_command to work on gesture bindings
868static void gesture_binding_execute(struct sway_seat *seat,
869 struct sway_gesture_binding *binding) {
870 struct sway_binding *dummy_binding =
871 calloc(1, sizeof(struct sway_binding));
872 dummy_binding->type = BINDING_GESTURE;
873 dummy_binding->command = binding->command;
874
875 char *description = gesture_to_string(&binding->gesture);
876 sway_log(SWAY_DEBUG, "executing gesture binding: %s", description);
877 free(description);
878
879 seat_execute_command(seat, dummy_binding);
880
881 free(dummy_binding);
882}
883
884static void handle_hold_begin(struct sway_seat *seat,
885 struct wlr_pointer_hold_begin_event *event) {
886 // Start tracking gesture if there is a matching binding ...
887 struct sway_input_device *device =
888 event->pointer ? event->pointer->base.data : NULL;
889 list_t *bindings = config->current_mode->gesture_bindings;
890 if (gesture_binding_check(bindings, GESTURE_TYPE_HOLD, event->fingers, device)) {
891 struct seatop_default_event *seatop = seat->seatop_data;
892 gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_HOLD, event->fingers);
893 } else {
894 // ... otherwise forward to client
895 struct sway_cursor *cursor = seat->cursor;
896 wlr_pointer_gestures_v1_send_hold_begin(
897 cursor->pointer_gestures, cursor->seat->wlr_seat,
898 event->time_msec, event->fingers);
899 }
900}
901
902static void handle_hold_end(struct sway_seat *seat,
903 struct wlr_pointer_hold_end_event *event) {
904 // Ensure that gesture is being tracked and was not cancelled
905 struct seatop_default_event *seatop = seat->seatop_data;
906 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) {
907 struct sway_cursor *cursor = seat->cursor;
908 wlr_pointer_gestures_v1_send_hold_end(
909 cursor->pointer_gestures, cursor->seat->wlr_seat,
910 event->time_msec, event->cancelled);
911 return;
912 }
913 if (event->cancelled) {
914 gesture_tracker_cancel(&seatop->gestures);
915 return;
916 }
917
918 // End gesture tracking and execute matched binding
919 struct sway_input_device *device =
920 event->pointer ? event->pointer->base.data : NULL;
921 struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
922 &seatop->gestures, device);
923
924 if (binding) {
925 gesture_binding_execute(seat, binding);
926 }
927}
928
929static void handle_pinch_begin(struct sway_seat *seat,
930 struct wlr_pointer_pinch_begin_event *event) {
931 // Start tracking gesture if there is a matching binding ...
932 struct sway_input_device *device =
933 event->pointer ? event->pointer->base.data : NULL;
934 list_t *bindings = config->current_mode->gesture_bindings;
935 if (gesture_binding_check(bindings, GESTURE_TYPE_PINCH, event->fingers, device)) {
936 struct seatop_default_event *seatop = seat->seatop_data;
937 gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_PINCH, event->fingers);
938 } else {
939 // ... otherwise forward to client
940 struct sway_cursor *cursor = seat->cursor;
941 wlr_pointer_gestures_v1_send_pinch_begin(
942 cursor->pointer_gestures, cursor->seat->wlr_seat,
943 event->time_msec, event->fingers);
944 }
945}
946
947static void handle_pinch_update(struct sway_seat *seat,
948 struct wlr_pointer_pinch_update_event *event) {
949 // Update any ongoing tracking ...
950 struct seatop_default_event *seatop = seat->seatop_data;
951 if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
952 gesture_tracker_update(&seatop->gestures, event->dx, event->dy,
953 event->scale, event->rotation);
954 } else {
955 // ... otherwise forward to client
956 struct sway_cursor *cursor = seat->cursor;
957 wlr_pointer_gestures_v1_send_pinch_update(
958 cursor->pointer_gestures,
959 cursor->seat->wlr_seat,
960 event->time_msec, event->dx, event->dy,
961 event->scale, event->rotation);
962 }
963}
964
965static void handle_pinch_end(struct sway_seat *seat,
966 struct wlr_pointer_pinch_end_event *event) {
967 // Ensure that gesture is being tracked and was not cancelled
968 struct seatop_default_event *seatop = seat->seatop_data;
969 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
970 struct sway_cursor *cursor = seat->cursor;
971 wlr_pointer_gestures_v1_send_pinch_end(
972 cursor->pointer_gestures, cursor->seat->wlr_seat,
973 event->time_msec, event->cancelled);
974 return;
975 }
976 if (event->cancelled) {
977 gesture_tracker_cancel(&seatop->gestures);
978 return;
979 }
980
981 // End gesture tracking and execute matched binding
982 struct sway_input_device *device =
983 event->pointer ? event->pointer->base.data : NULL;
984 struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
985 &seatop->gestures, device);
986
987 if (binding) {
988 gesture_binding_execute(seat, binding);
989 }
990}
991
992static void handle_swipe_begin(struct sway_seat *seat,
993 struct wlr_pointer_swipe_begin_event *event) {
994 // Start tracking gesture if there is a matching binding ...
995 struct sway_input_device *device =
996 event->pointer ? event->pointer->base.data : NULL;
997 list_t *bindings = config->current_mode->gesture_bindings;
998 if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) {
999 struct seatop_default_event *seatop = seat->seatop_data;
1000 gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers);
1001 } else {
1002 // ... otherwise forward to client
1003 struct sway_cursor *cursor = seat->cursor;
1004 wlr_pointer_gestures_v1_send_swipe_begin(
1005 cursor->pointer_gestures, cursor->seat->wlr_seat,
1006 event->time_msec, event->fingers);
1007 }
1008}
1009
1010static void handle_swipe_update(struct sway_seat *seat,
1011 struct wlr_pointer_swipe_update_event *event) {
1012
1013 // Update any ongoing tracking ...
1014 struct seatop_default_event *seatop = seat->seatop_data;
1015 if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
1016 gesture_tracker_update(&seatop->gestures,
1017 event->dx, event->dy, NAN, NAN);
1018 } else {
1019 // ... otherwise forward to client
1020 struct sway_cursor *cursor = seat->cursor;
1021 wlr_pointer_gestures_v1_send_swipe_update(
1022 cursor->pointer_gestures, cursor->seat->wlr_seat,
1023 event->time_msec, event->dx, event->dy);
1024 }
1025}
1026
1027static void handle_swipe_end(struct sway_seat *seat,
1028 struct wlr_pointer_swipe_end_event *event) {
1029 // Ensure gesture is being tracked and was not cancelled
1030 struct seatop_default_event *seatop = seat->seatop_data;
1031 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
1032 struct sway_cursor *cursor = seat->cursor;
1033 wlr_pointer_gestures_v1_send_swipe_end(cursor->pointer_gestures,
1034 cursor->seat->wlr_seat, event->time_msec, event->cancelled);
1035 return;
1036 }
1037 if (event->cancelled) {
1038 gesture_tracker_cancel(&seatop->gestures);
1039 return;
1040 }
1041
1042 // End gesture tracking and execute matched binding
1043 struct sway_input_device *device =
1044 event->pointer ? event->pointer->base.data : NULL;
1045 struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
1046 &seatop->gestures, device);
1047
1048 if (binding) {
1049 gesture_binding_execute(seat, binding);
1050 }
1051}
1052
753/*----------------------------------\ 1053/*----------------------------------\
754 * Functions used by handle_rebase / 1054 * Functions used by handle_rebase /
755 *--------------------------------*/ 1055 *--------------------------------*/
@@ -779,6 +1079,14 @@ static const struct sway_seatop_impl seatop_impl = {
779 .pointer_axis = handle_pointer_axis, 1079 .pointer_axis = handle_pointer_axis,
780 .tablet_tool_tip = handle_tablet_tool_tip, 1080 .tablet_tool_tip = handle_tablet_tool_tip,
781 .tablet_tool_motion = handle_tablet_tool_motion, 1081 .tablet_tool_motion = handle_tablet_tool_motion,
1082 .hold_begin = handle_hold_begin,
1083 .hold_end = handle_hold_end,
1084 .pinch_begin = handle_pinch_begin,
1085 .pinch_update = handle_pinch_update,
1086 .pinch_end = handle_pinch_end,
1087 .swipe_begin = handle_swipe_begin,
1088 .swipe_update = handle_swipe_update,
1089 .swipe_end = handle_swipe_end,
782 .rebase = handle_rebase, 1090 .rebase = handle_rebase,
783 .allow_set_cursor = true, 1091 .allow_set_cursor = true,
784}; 1092};
@@ -789,8 +1097,8 @@ void seatop_begin_default(struct sway_seat *seat) {
789 struct seatop_default_event *e = 1097 struct seatop_default_event *e =
790 calloc(1, sizeof(struct seatop_default_event)); 1098 calloc(1, sizeof(struct seatop_default_event));
791 sway_assert(e, "Unable to allocate seatop_default_event"); 1099 sway_assert(e, "Unable to allocate seatop_default_event");
1100
792 seat->seatop_impl = &seatop_impl; 1101 seat->seatop_impl = &seatop_impl;
793 seat->seatop_data = e; 1102 seat->seatop_data = e;
794
795 seatop_rebase(seat, 0); 1103 seatop_rebase(seat, 0);
796} 1104}
diff --git a/sway/meson.build b/sway/meson.build
index 6762ef2d..5dd9cad2 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -67,6 +67,7 @@ sway_sources = files(
67 'commands/force_focus_wrapping.c', 67 'commands/force_focus_wrapping.c',
68 'commands/fullscreen.c', 68 'commands/fullscreen.c',
69 'commands/gaps.c', 69 'commands/gaps.c',
70 'commands/gesture.c',
70 'commands/hide_edge_borders.c', 71 'commands/hide_edge_borders.c',
71 'commands/inhibit_idle.c', 72 'commands/inhibit_idle.c',
72 'commands/kill.c', 73 'commands/kill.c',
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 2780370f..db7886f0 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -488,6 +488,62 @@ runtime.
488 bindswitch lid:toggle exec echo "Lid moved" 488 bindswitch lid:toggle exec echo "Lid moved"
489``` 489```
490 490
491*bindgesture* [--exact] [--input-device=<device>] [--no-warn] \
492<gesture>[:<fingers>][:directions] <command>
493 Binds _gesture_ to execute the sway command _command_ when detected.
494 Currently supports the _hold_, _pinch_ or _swipe_ gesture. Optionally
495 can be limited to bind to a certain number of _fingers_ or, for a
496 _pinch_ or _swipe_ gesture, to certain _directions_.
497
498[[ *type*
499:[ *fingers*
500:< *direction*
501| hold
502:- 1 - 5
503: none
504| swipe
505: 3 - 5
506: up, down, left, right
507| pinch
508: 2 - 5
509: all above + inward, outward, clockwise, counterclockwise
510
511 The _fingers_ can be limited to any sensible number or left empty to accept
512 any finger counts.
513 Valid directions are _up_, _down_, _left_ and _right_, as well as _inward_,
514 _outward_, _clockwise_, _counterclockwise_ for the _pinch_ gesture.
515 Multiple directions can be combined by a plus.
516
517 If a _input-device_ is given, the binding will only be executed for
518 that input device and will be executed instead of any binding that is
519 generic to all devices. By default, if you overwrite a binding,
520 swaynag will give you a warning. To silence this, use the _--no-warn_ flag.
521
522 The _--exact_ flag can be used to ensure a binding only matches when exactly
523 all specified directions are matched and nothing more. If there is matching
524 binding with _--exact_, it will be preferred.
525
526 The priority for matching bindings is as follows: input device, then
527 exact matches followed by matches with the highest number of matching
528 directions.
529
530 Gestures executed while the pointer is above a bar are not handled by sway.
531 See the respective documentation, e.g. *bindgesture* in *sway-bar*(5).
532
533 Example:
534```
535 # Allow switching between workspaces with left and right swipes
536 bindgesture swipe:right workspace prev
537 bindgesture swipe:left workspace next
538
539 # Allow container movements by pinching them
540 bindgesture pinch:inward+up move up
541 bindgesture pinch:inward+down move down
542 bindgesture pinch:inward+left move left
543 bindgesture pinch:inward+right move right
544
545```
546
491*client.background* <color> 547*client.background* <color>
492 This command is ignored and is only present for i3 compatibility. 548 This command is ignored and is only present for i3 compatibility.
493 549
@@ -792,6 +848,11 @@ The default colors are:
792*unbindswitch* <switch>:<state> 848*unbindswitch* <switch>:<state>
793 Removes a binding for when <switch> changes to <state>. 849 Removes a binding for when <switch> changes to <state>.
794 850
851*unbindgesture* [--exact] [--input-device=<device>] \
852<gesture>[:<fingers>][:directions]
853 Removes a binding for the specified _gesture_, _fingers_
854 and _directions_ combination.
855
795*unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ 856*unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \
796[--to-code] [--input-device=<device>] <key combo> 857[--to-code] [--input-device=<device>] <key combo>
797 Removes the binding for _key combo_ that was previously bound with the 858 Removes the binding for _key combo_ that was previously bound with the