diff options
-rw-r--r-- | common/gesture.c | 350 | ||||
-rw-r--r-- | common/meson.build | 1 | ||||
-rw-r--r-- | include/gesture.h | 104 | ||||
-rw-r--r-- | include/stringop.h | 1 | ||||
-rw-r--r-- | include/sway/commands.h | 2 | ||||
-rw-r--r-- | include/sway/config.h | 18 | ||||
-rw-r--r-- | include/sway/input/cursor.h | 4 | ||||
-rw-r--r-- | include/sway/input/seat.h | 35 | ||||
-rw-r--r-- | sway/commands.c | 3 | ||||
-rw-r--r-- | sway/commands/gesture.c | 166 | ||||
-rw-r--r-- | sway/commands/mode.c | 3 | ||||
-rw-r--r-- | sway/config.c | 7 | ||||
-rw-r--r-- | sway/input/cursor.c | 89 | ||||
-rw-r--r-- | sway/input/seat.c | 56 | ||||
-rw-r--r-- | sway/input/seatop_default.c | 310 | ||||
-rw-r--r-- | sway/meson.build | 1 | ||||
-rw-r--r-- | sway/sway.5.scd | 61 |
17 files changed, 1156 insertions, 55 deletions
diff --git a/common/gesture.c b/common/gesture.c new file mode 100644 index 00000000..8c2efe99 --- /dev/null +++ b/common/gesture.c | |||
@@ -0,0 +1,350 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "gesture.h" | ||
3 | |||
4 | #include <math.h> | ||
5 | #include <stdarg.h> | ||
6 | #include <stdio.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <string.h> | ||
9 | #include "list.h" | ||
10 | #include "log.h" | ||
11 | #include "stringop.h" | ||
12 | |||
13 | const uint8_t GESTURE_FINGERS_ANY = 0; | ||
14 | |||
15 | // Helper to easily allocate and format string | ||
16 | static char *strformat(const char *format, ...) { | ||
17 | va_list args; | ||
18 | va_start(args, format); | ||
19 | int length = vsnprintf(NULL, 0, format, args) + 1; | ||
20 | va_end(args); | ||
21 | |||
22 | char *result = malloc(length); | ||
23 | if (result) { | ||
24 | va_start(args, format); | ||
25 | vsnprintf(result, length, format, args); | ||
26 | va_end(args); | ||
27 | } | ||
28 | |||
29 | return result; | ||
30 | } | ||
31 | |||
32 | char *gesture_parse(const char *input, struct gesture *output) { | ||
33 | // Clear output in case of failure | ||
34 | output->type = GESTURE_TYPE_NONE; | ||
35 | output->fingers = GESTURE_FINGERS_ANY; | ||
36 | output->directions = GESTURE_DIRECTION_NONE; | ||
37 | |||
38 | // Split input type, fingers and directions | ||
39 | list_t *split = split_string(input, ":"); | ||
40 | if (split->length < 1 || split->length > 3) { | ||
41 | return strformat( | ||
42 | "expected <gesture>[:<fingers>][:direction], got %s", | ||
43 | input); | ||
44 | } | ||
45 | |||
46 | // Parse gesture type | ||
47 | if (strcmp(split->items[0], "hold") == 0) { | ||
48 | output->type = GESTURE_TYPE_HOLD; | ||
49 | } else if (strcmp(split->items[0], "pinch") == 0) { | ||
50 | output->type = GESTURE_TYPE_PINCH; | ||
51 | } else if (strcmp(split->items[0], "swipe") == 0) { | ||
52 | output->type = GESTURE_TYPE_SWIPE; | ||
53 | } else { | ||
54 | return strformat("expected hold|pinch|swipe, got %s", | ||
55 | split->items[0]); | ||
56 | } | ||
57 | |||
58 | // Parse optional arguments | ||
59 | if (split->length > 1) { | ||
60 | char *next = split->items[1]; | ||
61 | |||
62 | // Try to parse as finger count (1-9) | ||
63 | if (strlen(next) == 1 && '1' <= next[0] && next[0] <= '9') { | ||
64 | output->fingers = atoi(next); | ||
65 | |||
66 | // Move to next if available | ||
67 | next = split->length == 3 ? split->items[2] : NULL; | ||
68 | } else if (split->length == 3) { | ||
69 | // Fail here if argument can only be finger count | ||
70 | return strformat("expected 1-9, got %s", next); | ||
71 | } | ||
72 | |||
73 | // If there is an argument left, try to parse as direction | ||
74 | if (next) { | ||
75 | list_t *directions = split_string(next, "+"); | ||
76 | |||
77 | for (int i = 0; i < directions->length; ++i) { | ||
78 | const char *item = directions->items[i]; | ||
79 | if (strcmp(item, "any") == 0) { | ||
80 | continue; | ||
81 | } else if (strcmp(item, "up") == 0) { | ||
82 | output->directions |= GESTURE_DIRECTION_UP; | ||
83 | } else if (strcmp(item, "down") == 0) { | ||
84 | output->directions |= GESTURE_DIRECTION_DOWN; | ||
85 | } else if (strcmp(item, "left") == 0) { | ||
86 | output->directions |= GESTURE_DIRECTION_LEFT; | ||
87 | } else if (strcmp(item, "right") == 0) { | ||
88 | output->directions |= GESTURE_DIRECTION_RIGHT; | ||
89 | } else if (strcmp(item, "inward") == 0) { | ||
90 | output->directions |= GESTURE_DIRECTION_INWARD; | ||
91 | } else if (strcmp(item, "outward") == 0) { | ||
92 | output->directions |= GESTURE_DIRECTION_OUTWARD; | ||
93 | } else if (strcmp(item, "clockwise") == 0) { | ||
94 | output->directions |= GESTURE_DIRECTION_CLOCKWISE; | ||
95 | } else if (strcmp(item, "counterclockwise") == 0) { | ||
96 | output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE; | ||
97 | } else { | ||
98 | return strformat("expected direction, got %s", item); | ||
99 | } | ||
100 | } | ||
101 | list_free_items_and_destroy(directions); | ||
102 | } | ||
103 | } // if optional args | ||
104 | |||
105 | list_free_items_and_destroy(split); | ||
106 | |||
107 | return NULL; | ||
108 | } | ||
109 | |||
110 | const char *gesture_type_string(enum gesture_type type) { | ||
111 | switch (type) { | ||
112 | case GESTURE_TYPE_NONE: | ||
113 | return "none"; | ||
114 | case GESTURE_TYPE_HOLD: | ||
115 | return "hold"; | ||
116 | case GESTURE_TYPE_PINCH: | ||
117 | return "pinch"; | ||
118 | case GESTURE_TYPE_SWIPE: | ||
119 | return "swipe"; | ||
120 | } | ||
121 | |||
122 | return NULL; | ||
123 | } | ||
124 | |||
125 | const char *gesture_direction_string(enum gesture_direction direction) { | ||
126 | switch (direction) { | ||
127 | case GESTURE_DIRECTION_NONE: | ||
128 | return "none"; | ||
129 | case GESTURE_DIRECTION_UP: | ||
130 | return "up"; | ||
131 | case GESTURE_DIRECTION_DOWN: | ||
132 | return "down"; | ||
133 | case GESTURE_DIRECTION_LEFT: | ||
134 | return "left"; | ||
135 | case GESTURE_DIRECTION_RIGHT: | ||
136 | return "right"; | ||
137 | case GESTURE_DIRECTION_INWARD: | ||
138 | return "inward"; | ||
139 | case GESTURE_DIRECTION_OUTWARD: | ||
140 | return "outward"; | ||
141 | case GESTURE_DIRECTION_CLOCKWISE: | ||
142 | return "clockwise"; | ||
143 | case GESTURE_DIRECTION_COUNTERCLOCKWISE: | ||
144 | return "counterclockwise"; | ||
145 | } | ||
146 | |||
147 | return NULL; | ||
148 | } | ||
149 | |||
150 | // Helper to turn combination of directions flags into string representation. | ||
151 | static char *gesture_directions_to_string(uint32_t directions) { | ||
152 | char *result = NULL; | ||
153 | |||
154 | for (uint8_t bit = 0; bit < 32; bit++) { | ||
155 | uint32_t masked = directions & (1 << bit); | ||
156 | if (masked) { | ||
157 | const char *name = gesture_direction_string(masked); | ||
158 | |||
159 | if (!name) { | ||
160 | name = "unknown"; | ||
161 | } | ||
162 | |||
163 | if (!result) { | ||
164 | result = strdup(name); | ||
165 | } else { | ||
166 | char *new = strformat("%s+%s", result, name); | ||
167 | free(result); | ||
168 | result = new; | ||
169 | } | ||
170 | } | ||
171 | } | ||
172 | |||
173 | if(!result) { | ||
174 | return strdup("any"); | ||
175 | } | ||
176 | |||
177 | return result; | ||
178 | } | ||
179 | |||
180 | char *gesture_to_string(struct gesture *gesture) { | ||
181 | char *directions = gesture_directions_to_string(gesture->directions); | ||
182 | char *result = strformat("%s:%u:%s", | ||
183 | gesture_type_string(gesture->type), | ||
184 | gesture->fingers, directions); | ||
185 | free(directions); | ||
186 | return result; | ||
187 | } | ||
188 | |||
189 | bool gesture_check(struct gesture *target, enum gesture_type type, uint8_t fingers) { | ||
190 | // Check that gesture type matches | ||
191 | if (target->type != type) { | ||
192 | return false; | ||
193 | } | ||
194 | |||
195 | // Check that finger count matches | ||
196 | if (target->fingers != GESTURE_FINGERS_ANY && target->fingers != fingers) { | ||
197 | return false; | ||
198 | } | ||
199 | |||
200 | return true; | ||
201 | } | ||
202 | |||
203 | bool gesture_match(struct gesture *target, struct gesture *to_match, bool exact) { | ||
204 | // Check type and fingers | ||
205 | if (!gesture_check(target, to_match->type, to_match->fingers)) { | ||
206 | return false; | ||
207 | } | ||
208 | |||
209 | // Enforce exact matches ... | ||
210 | if (exact && target->directions != to_match->directions) { | ||
211 | return false; | ||
212 | } | ||
213 | |||
214 | // ... or ensure all target directions are matched | ||
215 | return (target->directions & to_match->directions) == target->directions; | ||
216 | } | ||
217 | |||
218 | bool gesture_equal(struct gesture *a, struct gesture *b) { | ||
219 | return a->type == b->type | ||
220 | && a->fingers == b->fingers | ||
221 | && a->directions == b->directions; | ||
222 | } | ||
223 | |||
224 | // Return count of set bits in directions bit field. | ||
225 | static uint8_t gesture_directions_count(uint32_t directions) { | ||
226 | uint8_t count = 0; | ||
227 | for (; directions; directions >>= 1) { | ||
228 | count += directions & 1; | ||
229 | } | ||
230 | return count; | ||
231 | } | ||
232 | |||
233 | // Compare direction bit count of two direction. | ||
234 | static int8_t gesture_directions_compare(uint32_t a, uint32_t b) { | ||
235 | return gesture_directions_count(a) - gesture_directions_count(b); | ||
236 | } | ||
237 | |||
238 | // Compare two direction based on their direction bit count | ||
239 | int8_t gesture_compare(struct gesture *a, struct gesture *b) { | ||
240 | return gesture_directions_compare(a->directions, b->directions); | ||
241 | } | ||
242 | |||
243 | void gesture_tracker_begin(struct gesture_tracker *tracker, | ||
244 | enum gesture_type type, uint8_t fingers) { | ||
245 | tracker->type = type; | ||
246 | tracker->fingers = fingers; | ||
247 | |||
248 | tracker->dx = 0.0; | ||
249 | tracker->dy = 0.0; | ||
250 | tracker->scale = 1.0; | ||
251 | tracker->rotation = 0.0; | ||
252 | |||
253 | sway_log(SWAY_DEBUG, "begin tracking %s:%u:? gesture", | ||
254 | gesture_type_string(type), fingers); | ||
255 | } | ||
256 | |||
257 | bool gesture_tracker_check(struct gesture_tracker *tracker, enum gesture_type type) { | ||
258 | return tracker->type == type; | ||
259 | } | ||
260 | |||
261 | void gesture_tracker_update(struct gesture_tracker *tracker, | ||
262 | double dx, double dy, double scale, double rotation) { | ||
263 | if (tracker->type == GESTURE_TYPE_HOLD) { | ||
264 | sway_assert(false, "hold does not update."); | ||
265 | return; | ||
266 | } | ||
267 | |||
268 | tracker->dx += dx; | ||
269 | tracker->dy += dy; | ||
270 | |||
271 | if (tracker->type == GESTURE_TYPE_PINCH) { | ||
272 | tracker->scale = scale; | ||
273 | tracker->rotation += rotation; | ||
274 | } | ||
275 | |||
276 | sway_log(SWAY_DEBUG, "update tracking %s:%u:? gesture: %f %f %f %f", | ||
277 | gesture_type_string(tracker->type), | ||
278 | tracker->fingers, | ||
279 | tracker->dx, tracker->dy, | ||
280 | tracker->scale, tracker->rotation); | ||
281 | } | ||
282 | |||
283 | void gesture_tracker_cancel(struct gesture_tracker *tracker) { | ||
284 | sway_log(SWAY_DEBUG, "cancel tracking %s:%u:? gesture", | ||
285 | gesture_type_string(tracker->type), tracker->fingers); | ||
286 | |||
287 | tracker->type = GESTURE_TYPE_NONE; | ||
288 | } | ||
289 | |||
290 | struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) { | ||
291 | struct gesture *result = calloc(1, sizeof(struct gesture)); | ||
292 | |||
293 | // Ignore gesture under some threshold | ||
294 | // TODO: Make configurable | ||
295 | const double min_rotation = 5; | ||
296 | const double min_scale_delta = 0.1; | ||
297 | |||
298 | // Determine direction | ||
299 | switch(tracker->type) { | ||
300 | // Gestures with scale and rotation | ||
301 | case GESTURE_TYPE_PINCH: | ||
302 | if (tracker->rotation > min_rotation) { | ||
303 | result->directions |= GESTURE_DIRECTION_CLOCKWISE; | ||
304 | } | ||
305 | if (tracker->rotation < -min_rotation) { | ||
306 | result->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE; | ||
307 | } | ||
308 | |||
309 | if (tracker->scale > (1.0 + min_scale_delta)) { | ||
310 | result->directions |= GESTURE_DIRECTION_OUTWARD; | ||
311 | } | ||
312 | if (tracker->scale < (1.0 - min_scale_delta)) { | ||
313 | result->directions |= GESTURE_DIRECTION_INWARD; | ||
314 | } | ||
315 | __attribute__ ((fallthrough)); | ||
316 | // Gestures with dx and dy | ||
317 | case GESTURE_TYPE_SWIPE: | ||
318 | if (fabs(tracker->dx) > fabs(tracker->dy)) { | ||
319 | if (tracker->dx > 0) { | ||
320 | result->directions |= GESTURE_DIRECTION_RIGHT; | ||
321 | } else { | ||
322 | result->directions |= GESTURE_DIRECTION_LEFT; | ||
323 | } | ||
324 | } else { | ||
325 | if (tracker->dy > 0) { | ||
326 | result->directions |= GESTURE_DIRECTION_DOWN; | ||
327 | } else { | ||
328 | result->directions |= GESTURE_DIRECTION_UP; | ||
329 | } | ||
330 | } | ||
331 | // Gesture without any direction | ||
332 | case GESTURE_TYPE_HOLD: | ||
333 | break; | ||
334 | // Not tracking any gesture | ||
335 | case GESTURE_TYPE_NONE: | ||
336 | sway_assert(false, "Not tracking any gesture."); | ||
337 | return result; | ||
338 | } | ||
339 | |||
340 | result->type = tracker->type; | ||
341 | result->fingers = tracker->fingers; | ||
342 | |||
343 | char *description = gesture_to_string(result); | ||
344 | sway_log(SWAY_DEBUG, "end tracking gesture: %s", description); | ||
345 | free(description); | ||
346 | |||
347 | tracker->type = GESTURE_TYPE_NONE; | ||
348 | |||
349 | return result; | ||
350 | } | ||
diff --git a/common/meson.build b/common/meson.build index c653dd72..3756075a 100644 --- a/common/meson.build +++ b/common/meson.build | |||
@@ -3,6 +3,7 @@ lib_sway_common = static_library( | |||
3 | files( | 3 | files( |
4 | 'background-image.c', | 4 | 'background-image.c', |
5 | 'cairo.c', | 5 | 'cairo.c', |
6 | 'gesture.c', | ||
6 | 'ipc-client.c', | 7 | 'ipc-client.c', |
7 | 'log.c', | 8 | 'log.c', |
8 | 'loop.c', | 9 | 'loop.c', |
diff --git a/include/gesture.h b/include/gesture.h new file mode 100644 index 00000000..9c6b0f91 --- /dev/null +++ b/include/gesture.h | |||
@@ -0,0 +1,104 @@ | |||
1 | #ifndef _SWAY_GESTURE_H | ||
2 | #define _SWAY_GESTURE_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | #include <stdint.h> | ||
6 | |||
7 | /** | ||
8 | * A gesture type used in binding. | ||
9 | */ | ||
10 | enum gesture_type { | ||
11 | GESTURE_TYPE_NONE = 0, | ||
12 | GESTURE_TYPE_HOLD, | ||
13 | GESTURE_TYPE_PINCH, | ||
14 | GESTURE_TYPE_SWIPE, | ||
15 | }; | ||
16 | |||
17 | // Turns single type enum value to constant string representation. | ||
18 | const char *gesture_type_string(enum gesture_type direction); | ||
19 | |||
20 | // Value to use to accept any finger count | ||
21 | extern const uint8_t GESTURE_FINGERS_ANY; | ||
22 | |||
23 | /** | ||
24 | * A gesture direction used in binding. | ||
25 | */ | ||
26 | enum gesture_direction { | ||
27 | GESTURE_DIRECTION_NONE = 0, | ||
28 | // Directions based on delta x and y | ||
29 | GESTURE_DIRECTION_UP = 1 << 0, | ||
30 | GESTURE_DIRECTION_DOWN = 1 << 1, | ||
31 | GESTURE_DIRECTION_LEFT = 1 << 2, | ||
32 | GESTURE_DIRECTION_RIGHT = 1 << 3, | ||
33 | // Directions based on scale | ||
34 | GESTURE_DIRECTION_INWARD = 1 << 4, | ||
35 | GESTURE_DIRECTION_OUTWARD = 1 << 5, | ||
36 | // Directions based on rotation | ||
37 | GESTURE_DIRECTION_CLOCKWISE = 1 << 6, | ||
38 | GESTURE_DIRECTION_COUNTERCLOCKWISE = 1 << 7, | ||
39 | }; | ||
40 | |||
41 | // Turns single direction enum value to constant string representation. | ||
42 | const char *gesture_direction_string(enum gesture_direction direction); | ||
43 | |||
44 | /** | ||
45 | * Struct representing a pointer gesture | ||
46 | */ | ||
47 | struct gesture { | ||
48 | enum gesture_type type; | ||
49 | uint8_t fingers; | ||
50 | uint32_t directions; | ||
51 | }; | ||
52 | |||
53 | /** | ||
54 | * Parses gesture from <gesture>[:<fingers>][:<directions>] string. | ||
55 | * | ||
56 | * Return NULL on success, otherwise error message string | ||
57 | */ | ||
58 | char *gesture_parse(const char *input, struct gesture *output); | ||
59 | |||
60 | // Turns gesture into string representation | ||
61 | char *gesture_to_string(struct gesture *gesture); | ||
62 | |||
63 | // Check if gesture is of certain type and finger count. | ||
64 | bool gesture_check(struct gesture *target, | ||
65 | enum gesture_type type, uint8_t fingers); | ||
66 | |||
67 | // Check if a gesture target/binding is match by other gesture/input | ||
68 | bool gesture_match(struct gesture *target, | ||
69 | struct gesture *to_match, bool exact); | ||
70 | |||
71 | // Returns true if gesture are exactly the same | ||
72 | bool gesture_equal(struct gesture *a, struct gesture *b); | ||
73 | |||
74 | // Compare distance between two matched target gestures. | ||
75 | int8_t gesture_compare(struct gesture *a, struct gesture *b); | ||
76 | |||
77 | // Small helper struct to track gestures over time | ||
78 | struct gesture_tracker { | ||
79 | enum gesture_type type; | ||
80 | uint8_t fingers; | ||
81 | double dx, dy; | ||
82 | double scale; | ||
83 | double rotation; | ||
84 | }; | ||
85 | |||
86 | // Begin gesture tracking | ||
87 | void gesture_tracker_begin(struct gesture_tracker *tracker, | ||
88 | enum gesture_type type, uint8_t fingers); | ||
89 | |||
90 | // Check if the provides type is currently being tracked | ||
91 | bool gesture_tracker_check(struct gesture_tracker *tracker, | ||
92 | enum gesture_type type); | ||
93 | |||
94 | // Update gesture track with new data point | ||
95 | void gesture_tracker_update(struct gesture_tracker *tracker, double dx, | ||
96 | double dy, double scale, double rotation); | ||
97 | |||
98 | // Reset tracker | ||
99 | void gesture_tracker_cancel(struct gesture_tracker *tracker); | ||
100 | |||
101 | // Reset tracker and return gesture tracked | ||
102 | struct gesture *gesture_tracker_end(struct gesture_tracker *tracker); | ||
103 | |||
104 | #endif | ||
diff --git a/include/stringop.h b/include/stringop.h index 8d7089e9..b29f59b2 100644 --- a/include/stringop.h +++ b/include/stringop.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define _SWAY_STRINGOP_H | 2 | #define _SWAY_STRINGOP_H |
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include <stddef.h> | ||
5 | #include "list.h" | 6 | #include "list.h" |
6 | 7 | ||
7 | void strip_whitespace(char *str); | 8 | void strip_whitespace(char *str); |
diff --git a/include/sway/commands.h b/include/sway/commands.h index 2746ef28..5f71a79d 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h | |||
@@ -106,6 +106,7 @@ sway_cmd cmd_exec_process; | |||
106 | sway_cmd cmd_assign; | 106 | sway_cmd cmd_assign; |
107 | sway_cmd cmd_bar; | 107 | sway_cmd cmd_bar; |
108 | sway_cmd cmd_bindcode; | 108 | sway_cmd cmd_bindcode; |
109 | sway_cmd cmd_bindgesture; | ||
109 | sway_cmd cmd_bindswitch; | 110 | sway_cmd cmd_bindswitch; |
110 | sway_cmd cmd_bindsym; | 111 | sway_cmd cmd_bindsym; |
111 | sway_cmd cmd_border; | 112 | sway_cmd cmd_border; |
@@ -191,6 +192,7 @@ sway_cmd cmd_titlebar_border_thickness; | |||
191 | sway_cmd cmd_titlebar_padding; | 192 | sway_cmd cmd_titlebar_padding; |
192 | sway_cmd cmd_unbindcode; | 193 | sway_cmd cmd_unbindcode; |
193 | sway_cmd cmd_unbindswitch; | 194 | sway_cmd cmd_unbindswitch; |
195 | sway_cmd cmd_unbindgesture; | ||
194 | sway_cmd cmd_unbindsym; | 196 | sway_cmd cmd_unbindsym; |
195 | sway_cmd cmd_unmark; | 197 | sway_cmd cmd_unmark; |
196 | sway_cmd cmd_urgent; | 198 | sway_cmd cmd_urgent; |
diff --git a/include/sway/config.h b/include/sway/config.h index 2e24c3ae..05678c33 100644 --- a/include/sway/config.h +++ b/include/sway/config.h | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <xkbcommon/xkbcommon.h> | 10 | #include <xkbcommon/xkbcommon.h> |
11 | #include <xf86drmMode.h> | 11 | #include <xf86drmMode.h> |
12 | #include "../include/config.h" | 12 | #include "../include/config.h" |
13 | #include "gesture.h" | ||
13 | #include "list.h" | 14 | #include "list.h" |
14 | #include "swaynag.h" | 15 | #include "swaynag.h" |
15 | #include "tree/container.h" | 16 | #include "tree/container.h" |
@@ -32,7 +33,8 @@ enum binding_input_type { | |||
32 | BINDING_KEYSYM, | 33 | BINDING_KEYSYM, |
33 | BINDING_MOUSECODE, | 34 | BINDING_MOUSECODE, |
34 | BINDING_MOUSESYM, | 35 | BINDING_MOUSESYM, |
35 | BINDING_SWITCH | 36 | BINDING_SWITCH, // dummy, only used to call seat_execute_command |
37 | BINDING_GESTURE // dummy, only used to call seat_execute_command | ||
36 | }; | 38 | }; |
37 | 39 | ||
38 | enum binding_flags { | 40 | enum binding_flags { |
@@ -45,6 +47,7 @@ enum binding_flags { | |||
45 | BINDING_RELOAD = 1 << 6, // switch only; (re)trigger binding on reload | 47 | BINDING_RELOAD = 1 << 6, // switch only; (re)trigger binding on reload |
46 | BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor | 48 | BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor |
47 | BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key | 49 | BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key |
50 | BINDING_EXACT = 1 << 9, // gesture only; only trigger on exact match | ||
48 | }; | 51 | }; |
49 | 52 | ||
50 | /** | 53 | /** |
@@ -79,6 +82,16 @@ struct sway_switch_binding { | |||
79 | }; | 82 | }; |
80 | 83 | ||
81 | /** | 84 | /** |
85 | * A gesture binding and an associated command. | ||
86 | */ | ||
87 | struct sway_gesture_binding { | ||
88 | char *input; | ||
89 | uint32_t flags; | ||
90 | struct gesture gesture; | ||
91 | char *command; | ||
92 | }; | ||
93 | |||
94 | /** | ||
82 | * Focus on window activation. | 95 | * Focus on window activation. |
83 | */ | 96 | */ |
84 | enum sway_fowa { | 97 | enum sway_fowa { |
@@ -97,6 +110,7 @@ struct sway_mode { | |||
97 | list_t *keycode_bindings; | 110 | list_t *keycode_bindings; |
98 | list_t *mouse_bindings; | 111 | list_t *mouse_bindings; |
99 | list_t *switch_bindings; | 112 | list_t *switch_bindings; |
113 | list_t *gesture_bindings; | ||
100 | bool pango; | 114 | bool pango; |
101 | }; | 115 | }; |
102 | 116 | ||
@@ -689,6 +703,8 @@ void free_sway_binding(struct sway_binding *sb); | |||
689 | 703 | ||
690 | void free_switch_binding(struct sway_switch_binding *binding); | 704 | void free_switch_binding(struct sway_switch_binding *binding); |
691 | 705 | ||
706 | void free_gesture_binding(struct sway_gesture_binding *binding); | ||
707 | |||
692 | void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); | 708 | void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); |
693 | 709 | ||
694 | void load_swaybar(struct bar_config *bar); | 710 | void load_swaybar(struct bar_config *bar); |
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 3a71a35f..8a2898dd 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h | |||
@@ -36,14 +36,14 @@ struct sway_cursor { | |||
36 | bool active_confine_requires_warp; | 36 | bool active_confine_requires_warp; |
37 | 37 | ||
38 | struct wlr_pointer_gestures_v1 *pointer_gestures; | 38 | struct wlr_pointer_gestures_v1 *pointer_gestures; |
39 | struct wl_listener hold_begin; | ||
40 | struct wl_listener hold_end; | ||
39 | struct wl_listener pinch_begin; | 41 | struct wl_listener pinch_begin; |
40 | struct wl_listener pinch_update; | 42 | struct wl_listener pinch_update; |
41 | struct wl_listener pinch_end; | 43 | struct wl_listener pinch_end; |
42 | struct wl_listener swipe_begin; | 44 | struct wl_listener swipe_begin; |
43 | struct wl_listener swipe_update; | 45 | struct wl_listener swipe_update; |
44 | struct wl_listener swipe_end; | 46 | struct wl_listener swipe_end; |
45 | struct wl_listener hold_begin; | ||
46 | struct wl_listener hold_end; | ||
47 | 47 | ||
48 | struct wl_listener motion; | 48 | struct wl_listener motion; |
49 | struct wl_listener motion_absolute; | 49 | struct wl_listener motion_absolute; |
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 47726159..c2041742 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h | |||
@@ -19,6 +19,22 @@ struct sway_seatop_impl { | |||
19 | void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec); | 19 | void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec); |
20 | void (*pointer_axis)(struct sway_seat *seat, | 20 | void (*pointer_axis)(struct sway_seat *seat, |
21 | struct wlr_pointer_axis_event *event); | 21 | struct wlr_pointer_axis_event *event); |
22 | void (*hold_begin)(struct sway_seat *seat, | ||
23 | struct wlr_pointer_hold_begin_event *event); | ||
24 | void (*hold_end)(struct sway_seat *seat, | ||
25 | struct wlr_pointer_hold_end_event *event); | ||
26 | void (*pinch_begin)(struct sway_seat *seat, | ||
27 | struct wlr_pointer_pinch_begin_event *event); | ||
28 | void (*pinch_update)(struct sway_seat *seat, | ||
29 | struct wlr_pointer_pinch_update_event *event); | ||
30 | void (*pinch_end)(struct sway_seat *seat, | ||
31 | struct wlr_pointer_pinch_end_event *event); | ||
32 | void (*swipe_begin)(struct sway_seat *seat, | ||
33 | struct wlr_pointer_swipe_begin_event *event); | ||
34 | void (*swipe_update)(struct sway_seat *seat, | ||
35 | struct wlr_pointer_swipe_update_event *event); | ||
36 | void (*swipe_end)(struct sway_seat *seat, | ||
37 | struct wlr_pointer_swipe_end_event *event); | ||
22 | void (*rebase)(struct sway_seat *seat, uint32_t time_msec); | 38 | void (*rebase)(struct sway_seat *seat, uint32_t time_msec); |
23 | void (*tablet_tool_motion)(struct sway_seat *seat, | 39 | void (*tablet_tool_motion)(struct sway_seat *seat, |
24 | struct sway_tablet_tool *tool, uint32_t time_msec); | 40 | struct sway_tablet_tool *tool, uint32_t time_msec); |
@@ -287,6 +303,25 @@ void seatop_tablet_tool_tip(struct sway_seat *seat, | |||
287 | void seatop_tablet_tool_motion(struct sway_seat *seat, | 303 | void seatop_tablet_tool_motion(struct sway_seat *seat, |
288 | struct sway_tablet_tool *tool, uint32_t time_msec); | 304 | struct sway_tablet_tool *tool, uint32_t time_msec); |
289 | 305 | ||
306 | void seatop_hold_begin(struct sway_seat *seat, | ||
307 | struct wlr_pointer_hold_begin_event *event); | ||
308 | void seatop_hold_end(struct sway_seat *seat, | ||
309 | struct wlr_pointer_hold_end_event *event); | ||
310 | |||
311 | void seatop_pinch_begin(struct sway_seat *seat, | ||
312 | struct wlr_pointer_pinch_begin_event *event); | ||
313 | void seatop_pinch_update(struct sway_seat *seat, | ||
314 | struct wlr_pointer_pinch_update_event *event); | ||
315 | void seatop_pinch_end(struct sway_seat *seat, | ||
316 | struct wlr_pointer_pinch_end_event *event); | ||
317 | |||
318 | void seatop_swipe_begin(struct sway_seat *seat, | ||
319 | struct wlr_pointer_swipe_begin_event *event); | ||
320 | void seatop_swipe_update(struct sway_seat *seat, | ||
321 | struct wlr_pointer_swipe_update_event *event); | ||
322 | void seatop_swipe_end(struct sway_seat *seat, | ||
323 | struct wlr_pointer_swipe_end_event *event); | ||
324 | |||
290 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); | 325 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); |
291 | 326 | ||
292 | /** | 327 | /** |
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 | |||
9 | void 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 | */ | ||
21 | static 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 | */ | ||
41 | static 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 | */ | ||
76 | static 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 | */ | ||
99 | static 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 | |||
160 | struct cmd_results *cmd_bindgesture(int argc, char **argv) { | ||
161 | return cmd_bind_or_unbind_gesture(argc, argv, false); | ||
162 | } | ||
163 | |||
164 | struct 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 |
12 | static const struct cmd_handler mode_handlers[] = { | 12 | static 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 | ||
931 | static 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 | |||
939 | static 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 | |||
931 | static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { | 947 | static 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 | ||
941 | static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { | 955 | static 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 | ||
952 | static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { | 963 | static 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 | ||
962 | static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { | 971 | static 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 | ||
972 | static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { | 979 | static 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 | ||
982 | static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { | 987 | static 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 | |||
992 | static 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 | |||
1002 | static 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 | ||
1012 | static void handle_image_surface_destroy(struct wl_listener *listener, | 995 | static 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 | ||
1617 | void 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 | |||
1624 | void 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 | |||
1631 | void 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 | |||
1638 | void 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 | |||
1645 | void 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 | |||
1652 | void 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 | |||
1659 | void 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 | |||
1666 | void 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 | |||
1617 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { | 1673 | void 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 | */ | ||
763 | static 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 | */ | ||
796 | static 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(¤t->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(¤t->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 | ||
850 | static 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 | ||
868 | static 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 | |||
884 | static 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 | |||
902 | static 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 | |||
929 | static 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 | |||
947 | static 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 | |||
965 | static 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 | |||
992 | static 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 | |||
1010 | static 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 | |||
1027 | static 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 |