Moved to bindworkspacegesture instead of bindgesture

This commit is contained in:
Erik Reider 2024-01-22 17:43:35 +01:00
parent 067613854b
commit 3480479f17
8 changed files with 212 additions and 62 deletions

View file

@ -50,8 +50,6 @@ char *gesture_parse(const char *input, struct gesture *output) {
output->type = GESTURE_TYPE_PINCH; output->type = GESTURE_TYPE_PINCH;
} else if (strcmp(split->items[0], "swipe") == 0) { } else if (strcmp(split->items[0], "swipe") == 0) {
output->type = GESTURE_TYPE_SWIPE; output->type = GESTURE_TYPE_SWIPE;
} else if (strcmp(split->items[0], "workspace_swipe") == 0) {
output->type = GESTURE_TYPE_WORKSPACE_SWIPE;
} else { } else {
return strformat("expected hold|pinch|swipe, got %s", return strformat("expected hold|pinch|swipe, got %s",
split->items[0]); split->items[0]);
@ -96,10 +94,58 @@ char *gesture_parse(const char *input, struct gesture *output) {
output->directions |= GESTURE_DIRECTION_CLOCKWISE; output->directions |= GESTURE_DIRECTION_CLOCKWISE;
} else if (strcmp(item, "counterclockwise") == 0) { } else if (strcmp(item, "counterclockwise") == 0) {
output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE; output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE;
} else if (strcmp(item, "horizontal") == 0) { } else {
output->directions |= GESTURE_DIRECTION_HORIZONTAL; return strformat("expected direction, got %s", item);
}
}
list_free_items_and_destroy(directions);
}
} // if optional args
list_free_items_and_destroy(split);
return NULL;
}
char *workspace_gesture_parse(const char *input, struct gesture *output) {
// Clear output in case of failure
output->type = GESTURE_TYPE_NONE;
output->fingers = GESTURE_FINGERS_ANY;
output->directions = GESTURE_DIRECTION_NONE;
// Split input type, fingers and directions
list_t *split = split_string(input, ":");
if (split->length < 1 || split->length > 3) {
return strformat(
"expected [:<fingers>][:direction], got %s",
input);
}
// Parse optional arguments
if (split->length > 1) {
char *next = split->items[1];
// Try to parse as finger count (1-9)
if (strlen(next) == 1 && '1' <= next[0] && next[0] <= '9') {
output->fingers = atoi(next);
// Move to next if available
next = split->length == 3 ? split->items[2] : NULL;
} else if (split->length == 3) {
// Fail here if argument can only be finger count
return strformat("expected 1-9, got %s", next);
}
// If there is an argument left, try to parse as direction
if (next) {
list_t *directions = split_string(next, "+");
for (int i = 0; i < directions->length; ++i) {
const char *item = directions->items[i];
if (strcmp(item, "horizontal") == 0) {
output->type = GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL;
} else if (strcmp(item, "vertical") == 0) { } else if (strcmp(item, "vertical") == 0) {
output->directions |= GESTURE_DIRECTION_VERTICAL; output->type = GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL;
} else { } else {
return strformat("expected direction, got %s", item); return strformat("expected direction, got %s", item);
} }
@ -123,22 +169,15 @@ const char *gesture_type_string(enum gesture_type type) {
return "pinch"; return "pinch";
case GESTURE_TYPE_SWIPE: case GESTURE_TYPE_SWIPE:
return "swipe"; return "swipe";
case GESTURE_TYPE_WORKSPACE_SWIPE: case GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL:
return "workspace_swipe"; return "workspace_swipe_horizontal";
case GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL:
return "workspace_swipe_vertical";
} }
return NULL; return NULL;
} }
int gesture_workspace_swipe_command_parse(char *cmd) {
if (strcmp(cmd, "normal") == 0) {
return -1;
} else if (strcmp(cmd, "inverted") == 0) {
return 1;
}
return 0;
}
const char *gesture_direction_string(enum gesture_direction direction) { const char *gesture_direction_string(enum gesture_direction direction) {
switch (direction) { switch (direction) {
case GESTURE_DIRECTION_NONE: case GESTURE_DIRECTION_NONE:
@ -159,10 +198,6 @@ const char *gesture_direction_string(enum gesture_direction direction) {
return "clockwise"; return "clockwise";
case GESTURE_DIRECTION_COUNTERCLOCKWISE: case GESTURE_DIRECTION_COUNTERCLOCKWISE:
return "counterclockwise"; return "counterclockwise";
case GESTURE_DIRECTION_HORIZONTAL:
return "horizontal";
case GESTURE_DIRECTION_VERTICAL:
return "vertical";
} }
return NULL; return NULL;
@ -335,13 +370,6 @@ struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) {
} }
__attribute__ ((fallthrough)); __attribute__ ((fallthrough));
// Gestures with dx and dy // Gestures with dx and dy
case GESTURE_TYPE_WORKSPACE_SWIPE:
if (fabs(tracker->dx) > fabs(tracker->dy)) {
result->directions |= GESTURE_DIRECTION_HORIZONTAL;
} else {
result->directions |= GESTURE_DIRECTION_VERTICAL;
}
__attribute__ ((fallthrough));
case GESTURE_TYPE_SWIPE: case GESTURE_TYPE_SWIPE:
if (fabs(tracker->dx) > fabs(tracker->dy)) { if (fabs(tracker->dx) > fabs(tracker->dy)) {
if (tracker->dx > 0) { if (tracker->dx > 0) {
@ -358,6 +386,8 @@ struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) {
} }
// Gesture without any direction // Gesture without any direction
case GESTURE_TYPE_HOLD: case GESTURE_TYPE_HOLD:
case GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL:
case GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL:
break; break;
// Not tracking any gesture // Not tracking any gesture
case GESTURE_TYPE_NONE: case GESTURE_TYPE_NONE:

View file

@ -12,14 +12,13 @@ enum gesture_type {
GESTURE_TYPE_HOLD, GESTURE_TYPE_HOLD,
GESTURE_TYPE_PINCH, GESTURE_TYPE_PINCH,
GESTURE_TYPE_SWIPE, GESTURE_TYPE_SWIPE,
GESTURE_TYPE_WORKSPACE_SWIPE, GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL,
GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL,
}; };
// Turns single type enum value to constant string representation. // Turns single type enum value to constant string representation.
const char *gesture_type_string(enum gesture_type direction); const char *gesture_type_string(enum gesture_type direction);
int gesture_workspace_swipe_command_parse(char *cmd);
// Value to use to accept any finger count // Value to use to accept any finger count
extern const uint8_t GESTURE_FINGERS_ANY; extern const uint8_t GESTURE_FINGERS_ANY;
@ -39,9 +38,6 @@ enum gesture_direction {
// Directions based on rotation // Directions based on rotation
GESTURE_DIRECTION_CLOCKWISE = 1 << 6, GESTURE_DIRECTION_CLOCKWISE = 1 << 6,
GESTURE_DIRECTION_COUNTERCLOCKWISE = 1 << 7, GESTURE_DIRECTION_COUNTERCLOCKWISE = 1 << 7,
// Directions for workspace swipe
GESTURE_DIRECTION_HORIZONTAL = 1 << 8,
GESTURE_DIRECTION_VERTICAL = 1 << 9,
}; };
// Turns single direction enum value to constant string representation. // Turns single direction enum value to constant string representation.
@ -63,6 +59,13 @@ struct gesture {
*/ */
char *gesture_parse(const char *input, struct gesture *output); char *gesture_parse(const char *input, struct gesture *output);
/**
* Parses workspace gesture from [:<fingers>][:<directions>] string.
*
* Return NULL on success, otherwise error message string
*/
char *workspace_gesture_parse(const char *input, struct gesture *output);
// Turns gesture into string representation // Turns gesture into string representation
char *gesture_to_string(struct gesture *gesture); char *gesture_to_string(struct gesture *gesture);

View file

@ -114,6 +114,7 @@ sway_cmd cmd_bindcode;
sway_cmd cmd_bindgesture; sway_cmd cmd_bindgesture;
sway_cmd cmd_bindswitch; sway_cmd cmd_bindswitch;
sway_cmd cmd_bindsym; sway_cmd cmd_bindsym;
sway_cmd cmd_bindworkspacegesture;
sway_cmd cmd_blur; sway_cmd cmd_blur;
sway_cmd cmd_blur_brightness; sway_cmd cmd_blur_brightness;
sway_cmd cmd_blur_contrast; sway_cmd cmd_blur_contrast;
@ -223,6 +224,7 @@ sway_cmd cmd_unbindcode;
sway_cmd cmd_unbindswitch; sway_cmd cmd_unbindswitch;
sway_cmd cmd_unbindgesture; sway_cmd cmd_unbindgesture;
sway_cmd cmd_unbindsym; sway_cmd cmd_unbindsym;
sway_cmd cmd_unbindworkspacegesture;
sway_cmd cmd_unmark; sway_cmd cmd_unmark;
sway_cmd cmd_urgent; sway_cmd cmd_urgent;
sway_cmd cmd_workspace; sway_cmd cmd_workspace;

View file

@ -49,6 +49,7 @@ enum binding_flags {
BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor
BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key
BINDING_EXACT = 1 << 9, // gesture only; only trigger on exact match BINDING_EXACT = 1 << 9, // gesture only; only trigger on exact match
BINDING_INVERTED = 1 << 10, // workspace gesture only; gesture is inverted
}; };
/** /**

View file

@ -49,6 +49,7 @@ static const struct cmd_handler handlers[] = {
{ "bindgesture", cmd_bindgesture }, { "bindgesture", cmd_bindgesture },
{ "bindswitch", cmd_bindswitch }, { "bindswitch", cmd_bindswitch },
{ "bindsym", cmd_bindsym }, { "bindsym", cmd_bindsym },
{ "bindworkspacegesture", cmd_bindworkspacegesture },
{ "blur", cmd_blur }, { "blur", cmd_blur },
{ "blur_brightness", cmd_blur_brightness }, { "blur_brightness", cmd_blur_brightness },
{ "blur_contrast", cmd_blur_contrast }, { "blur_contrast", cmd_blur_contrast },
@ -118,6 +119,7 @@ static const struct cmd_handler handlers[] = {
{ "unbindgesture", cmd_unbindgesture }, { "unbindgesture", cmd_unbindgesture },
{ "unbindswitch", cmd_unbindswitch }, { "unbindswitch", cmd_unbindswitch },
{ "unbindsym", cmd_unbindsym }, { "unbindsym", cmd_unbindsym },
{ "unbindworkspacegesture", cmd_unbindworkspacegesture },
{ "workspace", cmd_workspace }, { "workspace", cmd_workspace },
{ "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth }, { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth },
}; };

View file

@ -154,19 +154,6 @@ static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, boo
return gesture_binding_remove(binding, argv[0]); return gesture_binding_remove(binding, argv[0]);
} }
binding->command = join_args(argv + 1, argc - 1); binding->command = join_args(argv + 1, argc - 1);
// Make sure that the gesture command is valid
switch (binding->gesture.type) {
case GESTURE_TYPE_WORKSPACE_SWIPE:
if (gesture_workspace_swipe_command_parse(binding->command) == 0) {
free(binding);
return cmd_results_new(CMD_FAILURE,
"Invalid %s command (%s). Either normal or inverted",
bindtype, errmsg);
}
break;
default:
break;
}
return gesture_binding_add(binding, argv[0], warn); return gesture_binding_add(binding, argv[0], warn);
} }
@ -177,3 +164,76 @@ struct cmd_results *cmd_bindgesture(int argc, char **argv) {
struct cmd_results *cmd_unbindgesture(int argc, char **argv) { struct cmd_results *cmd_unbindgesture(int argc, char **argv) {
return cmd_bind_or_unbind_gesture(argc, argv, true); return cmd_bind_or_unbind_gesture(argc, argv, true);
} }
/**
* Parse and execute bindgesture or unbindgesture command.
*/
static struct cmd_results *cmd_bind_or_unbind_workspacegesture(int argc,
char **argv, bool unbind) {
int minargs = 1;
char *bindtype = "bindgesture";
if (unbind) {
bindtype = "unbindgesture";
}
struct cmd_results *error = NULL;
if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) {
return error;
}
struct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding));
if (!binding) {
return cmd_results_new(CMD_FAILURE, "Unable to allocate binding");
}
binding->input = strdup("*");
bool warn = true;
// Handle flags
binding->flags |= BINDING_EXACT;
while (argc > 0) {
if (strcmp("--inverted", argv[0]) == 0) {
binding->flags |= BINDING_INVERTED;
} else if (strcmp("--no-warn", argv[0]) == 0) {
warn = false;
} else if (strncmp("--input-device=", argv[0],
strlen("--input-device=")) == 0) {
free(binding->input);
binding->input = strdup(argv[0] + strlen("--input-device="));
} else {
break;
}
argv++;
argc--;
}
if (argc < minargs) {
free(binding);
return cmd_results_new(CMD_FAILURE,
"Invalid %s command (expected at least %d "
"non-option arguments, got %d)", bindtype, minargs, argc);
}
char* errmsg = NULL;
if ((errmsg = workspace_gesture_parse(argv[0], &binding->gesture))) {
free(binding);
struct cmd_results *final = cmd_results_new(CMD_FAILURE,
"Invalid %s command (%s)",
bindtype, errmsg);
free(errmsg);
return final;
}
if (unbind) {
return gesture_binding_remove(binding, argv[0]);
}
binding->command = NULL;
return gesture_binding_add(binding, argv[0], warn);
}
struct cmd_results *cmd_bindworkspacegesture(int argc, char **argv) {
return cmd_bind_or_unbind_workspacegesture(argc, argv, false);
}
struct cmd_results *cmd_unbindworkspacegesture(int argc, char **argv) {
return cmd_bind_or_unbind_workspacegesture(argc, argv, true);
}

View file

@ -1011,9 +1011,12 @@ static void handle_swipe_begin(struct sway_seat *seat,
if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) { if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) {
struct seatop_default_event *seatop = seat->seatop_data; struct seatop_default_event *seatop = seat->seatop_data;
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers); gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers);
} else if (gesture_binding_check(bindings, GESTURE_TYPE_WORKSPACE_SWIPE, event->fingers, device)) { } else if (gesture_binding_check(bindings, GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL, event->fingers, device)) {
struct seatop_default_event *seatop = seat->seatop_data; struct seatop_default_event *seatop = seat->seatop_data;
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE, event->fingers); gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL, event->fingers);
} else if (gesture_binding_check(bindings, GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL, event->fingers, device)) {
struct seatop_default_event *seatop = seat->seatop_data;
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL, event->fingers);
} else { } else {
// ... otherwise forward to client // ... otherwise forward to client
struct sway_cursor *cursor = seat->cursor; struct sway_cursor *cursor = seat->cursor;
@ -1031,10 +1034,12 @@ static void handle_swipe_update(struct sway_seat *seat,
if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
gesture_tracker_update(&seatop->gestures, gesture_tracker_update(&seatop->gestures,
event->dx, event->dy, NAN, NAN); event->dx, event->dy, NAN, NAN);
} else if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE)) { } else if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL) ||
gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL)) {
gesture_tracker_update(&seatop->gestures, gesture_tracker_update(&seatop->gestures,
event->dx, event->dy, NAN, NAN); event->dx, event->dy, NAN, NAN);
// Find the gesture and update the swipe percentage
struct gesture_tracker *tracker = &seatop->gestures; struct gesture_tracker *tracker = &seatop->gestures;
struct sway_input_device *device = struct sway_input_device *device =
event->pointer ? event->pointer->base.data : NULL; event->pointer ? event->pointer->base.data : NULL;
@ -1046,22 +1051,24 @@ static void handle_swipe_update(struct sway_seat *seat,
struct gesture gesture = { struct gesture gesture = {
.fingers = tracker->fingers, .fingers = tracker->fingers,
.type = tracker->type, .type = tracker->type,
.directions = fabs(tracker->dx) > fabs(tracker->dy) .directions = GESTURE_DIRECTION_NONE,
? GESTURE_DIRECTION_HORIZONTAL : GESTURE_DIRECTION_VERTICAL,
}; };
struct sway_gesture_binding *binding = gesture_binding_match( struct sway_gesture_binding *binding = gesture_binding_match(
config->current_mode->gesture_bindings, &gesture, input); config->current_mode->gesture_bindings, &gesture, input);
if (binding) { if (binding) {
int invert = gesture_workspace_swipe_command_parse(binding->command); int invert = binding->flags & BINDING_INVERTED ? 1 : -1;
if ((binding->gesture.directions & GESTURE_DIRECTION_VERTICAL) == switch (binding->gesture.type) {
GESTURE_DIRECTION_VERTICAL) { case GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL:
update_workspace_scroll_percent(seat, tracker->dy, invert,
SWIPE_GESTURE_DIRECTION_VERTICAL);
} else if ((binding->gesture.directions & GESTURE_DIRECTION_HORIZONTAL) ==
GESTURE_DIRECTION_HORIZONTAL) {
update_workspace_scroll_percent(seat, tracker->dx, invert, update_workspace_scroll_percent(seat, tracker->dx, invert,
SWIPE_GESTURE_DIRECTION_HORIZONTAL); SWIPE_GESTURE_DIRECTION_HORIZONTAL);
break;
case GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL:
update_workspace_scroll_percent(seat, tracker->dy, invert,
SWIPE_GESTURE_DIRECTION_VERTICAL);
break;
default:
break;
} }
} }
} else { } else {
@ -1078,13 +1085,16 @@ static void handle_swipe_end(struct sway_seat *seat,
// Ensure gesture is being tracked and was not cancelled // Ensure gesture is being tracked and was not cancelled
struct seatop_default_event *seatop = seat->seatop_data; struct seatop_default_event *seatop = seat->seatop_data;
if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE) && if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE) &&
!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE)) { !gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL) &&
!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL)) {
struct sway_cursor *cursor = seat->cursor; struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_swipe_end(cursor->pointer_gestures, wlr_pointer_gestures_v1_send_swipe_end(cursor->pointer_gestures,
cursor->seat->wlr_seat, event->time_msec, event->cancelled); cursor->seat->wlr_seat, event->time_msec, event->cancelled);
return; return;
} }
if (event->cancelled && seatop->gestures.type != GESTURE_TYPE_WORKSPACE_SWIPE) { if (event->cancelled &&
seatop->gestures.type != GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL
&& seatop->gestures.type != GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL) {
gesture_tracker_cancel(&seatop->gestures); gesture_tracker_cancel(&seatop->gestures);
return; return;
} }
@ -1097,7 +1107,8 @@ static void handle_swipe_end(struct sway_seat *seat,
if (binding) { if (binding) {
switch (binding->gesture.type) { switch (binding->gesture.type) {
case GESTURE_TYPE_WORKSPACE_SWIPE:; case GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL:
case GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL:
snap_workspace_scroll_percent(seat); snap_workspace_scroll_percent(seat);
break; break;
default: default:

View file

@ -547,6 +547,47 @@ runtime.
``` ```
*bindworkspacegesture* [--input-device=<device>] [--no-warn] \
[--inverted] [<fingers>][:direction]
Binds the swipe to switch workspaces in a 1:1 fashion, but only when
detected. Optionally can be limited to bind to a certain number of
_fingers_ or to certain _directions_.
[[ *type*
:[ *fingers*
:< *direction*
| swipe
: 3 - 5
: horizontal, vertical
The _fingers_ can be limited to any sensible number or left empty to accept
any finger counts.
Valid directions are _horizontal_ and _vertical_.
If a _input-device_ is given, the binding will only be executed for
that input device and will be executed instead of any binding that is
generic to all devices. By default, if you overwrite a binding,
swaynag will give you a warning. To silence this, use the _--no-warn_ flag.
If _inverted_ is given, the swipe direction will be reversed.
The priority for matching bindings is as follows: input device, then
exact matches.
Gestures executed while the pointer is above a bar are not handled by sway.
See the respective documentation, e.g. *bindgesture* in *sway-bar*(5).
Example:
```
# Allow switching between workspaces with left and right swipes
bindworkspacegesture 4:horizontal
# Allow switching between workspaces with up and down swipes
bindworkspacegesture 3:vertical
# Allow switching between workspaces with up and down swipes but inverted
bindworkspacegesture --inverted 3:vertical
```
*client.background* <color> *client.background* <color>
This command is ignored and is only present for i3 compatibility. This command is ignored and is only present for i3 compatibility.