Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workspace movement with touchpad gestures #269

Draft
wants to merge 38 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
4e9e7c8
Initial implementation of workspace movement (without gestures)
ErikReider Jan 21, 2024
0b86078
Fixed transition not compensating for workspace gaps
ErikReider Jan 21, 2024
6552778
Fixed scroll not resetting to 0 when manually switching ws
ErikReider Jan 21, 2024
0c6d69b
Added gesture support
ErikReider Jan 21, 2024
44f54b9
Removed workspace_offset command
ErikReider Jan 21, 2024
8440285
Only damage the focused output
ErikReider Jan 22, 2024
8afad34
Render fullscreen containers over all layers
ErikReider Jan 22, 2024
9e55168
Try switching workspace on gesture cancel instead of reset
ErikReider Jan 22, 2024
8da7c50
Fixed popups not compensating for transition
ErikReider Jan 22, 2024
3ad50f9
Clear transformed fullscreen damage
ErikReider Jan 22, 2024
29c481d
Workspace code cleanup
ErikReider Jan 22, 2024
fdf764d
Added "Spring effect" when trying to swipe past limits
ErikReider Jan 22, 2024
0676138
Added vertical gesture support
ErikReider Jan 22, 2024
3480479
Moved to bindworkspacegesture instead of bindgesture
ErikReider Jan 22, 2024
519bfc4
Added workspace_gesture_spring_size config option
ErikReider Jan 22, 2024
0b521ab
Added workspace_gesture_wrap_around config option
ErikReider Jan 22, 2024
1113e56
Added workspace_gesture_threshold config option
ErikReider Jan 22, 2024
288b04d
Fixed gesture detecting every finger combination instead of the speci…
ErikReider Jan 22, 2024
42fcd6c
Fixed fullscreen sometimes segfaulting
ErikReider Jan 22, 2024
54daaa9
Updated swipe percentage calculation to also use avg_velocity
ErikReider Jan 23, 2024
0a619a0
Use Ease-Out when swiping into the non-wrapped edges
ErikReider Jan 23, 2024
83e0872
Fixed regular views rendering behind fullscreen ones + code cleanup
ErikReider Jan 24, 2024
bd3745c
Draw rect and shadow on "Spring effect" for fullscreen views
ErikReider Jan 24, 2024
7214db9
Updated default threshold to 50%
ErikReider Jan 24, 2024
a12a8ea
Merged workspace_output_next and workspace_output_next_wrap
ErikReider Jan 24, 2024
0da8221
Unset focus while swiping
ErikReider Jan 24, 2024
1fedb1a
Reset swipe on other cursor rebase
ErikReider Jan 24, 2024
b491ac4
Code cleanuo: Simplified adjust_* functions
ErikReider Jan 25, 2024
4f9ac2d
Fixed tracker value being updated even if percent isn't
ErikReider Jan 25, 2024
d7ca31c
Removed for-loop in render_workspace
ErikReider Jan 26, 2024
5bb8877
Made fullscreen rendering a bit more clear
ErikReider Jan 26, 2024
84c1b9c
Removed unneeded condition check in render_floating func
ErikReider Jan 26, 2024
8193bb6
Fixed switch-statement nit
ErikReider Jan 26, 2024
dec948d
Moved easing functions into their own file
ErikReider Jan 26, 2024
60ecc42
Added comment about workspace_gesture_parse reasoning
ErikReider Jan 26, 2024
5ace6bb
Fixed compilation issues on Nix
ErikReider Jan 26, 2024
358d649
Merge branch 'master' into workspace-movement
ErikReider May 6, 2024
59c2b7a
Merge branch 'master' into workspace-movement
ErikReider Aug 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions common/gesture.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,60 @@ char *gesture_parse(const char *input, struct gesture *output) {
return NULL;
}

// Similar to gesture_parse but with fewer checks to match the different
// workspace_gesture syntax
char *workspace_gesture_parse(const char *input, struct gesture *output) {
// Clear output in case of failure
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a copy+paste of the gesture_parse function

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just use gesture_parse? I see we add some gesture types but it doesn't look like they have any implications in the gesture_tracker_end function

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find a good enough syntax for the new command that fit into the bind_gestures parser so I needed to create a new one with fewer checks :/

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 > 2) {
return format_str(
"expected [:<fingers>][:direction], got %s",
input);
}

// Parse optional arguments
if (split->length > 1) {
char *next = split->items[0];

// 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 == 2 ? split->items[1] : NULL;
} else if (split->length == 2) {
// Fail here if argument can only be finger count
return format_str("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) {
output->type = GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL;
} else {
return format_str("expected direction, got %s", item);
}
}
list_free_items_and_destroy(directions);
}
} // if optional args

list_free_items_and_destroy(split);

return NULL;
}

const char *gesture_type_string(enum gesture_type type) {
switch (type) {
case GESTURE_TYPE_NONE:
Expand All @@ -100,6 +154,10 @@ const char *gesture_type_string(enum gesture_type type) {
return "pinch";
case GESTURE_TYPE_SWIPE:
return "swipe";
case GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL:
return "workspace_swipe_horizontal";
case GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL:
return "workspace_swipe_vertical";
}

return NULL;
Expand Down Expand Up @@ -313,6 +371,8 @@ struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) {
}
// Gesture without any direction
case GESTURE_TYPE_HOLD:
case GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL:
case GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL:
break;
// Not tracking any gesture
case GESTURE_TYPE_NONE:
Expand Down
9 changes: 9 additions & 0 deletions include/gesture.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ enum gesture_type {
GESTURE_TYPE_HOLD,
GESTURE_TYPE_PINCH,
GESTURE_TYPE_SWIPE,
GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL,
GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL,
};

// Turns single type enum value to constant string representation.
Expand Down Expand Up @@ -57,6 +59,13 @@ struct gesture {
*/
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
char *gesture_to_string(struct gesture *gesture);

Expand Down
5 changes: 5 additions & 0 deletions include/sway/commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ sway_cmd cmd_bindcode;
sway_cmd cmd_bindgesture;
sway_cmd cmd_bindswitch;
sway_cmd cmd_bindsym;
sway_cmd cmd_bindworkspacegesture;
sway_cmd cmd_blur;
sway_cmd cmd_blur_brightness;
sway_cmd cmd_blur_contrast;
Expand Down Expand Up @@ -223,11 +224,15 @@ sway_cmd cmd_unbindcode;
sway_cmd cmd_unbindswitch;
sway_cmd cmd_unbindgesture;
sway_cmd cmd_unbindsym;
sway_cmd cmd_unbindworkspacegesture;
sway_cmd cmd_unmark;
sway_cmd cmd_urgent;
sway_cmd cmd_workspace;
sway_cmd cmd_workspace_layout;
sway_cmd cmd_ws_auto_back_and_forth;
sway_cmd cmd_ws_gesture_spring_size;
sway_cmd cmd_ws_gesture_threshold;
sway_cmd cmd_ws_gesture_wrap_around;
sway_cmd cmd_xwayland;

sway_cmd bar_cmd_bindcode;
Expand Down
5 changes: 5 additions & 0 deletions include/sway/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ enum binding_flags {
BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor
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_INVERTED = 1 << 10, // workspace gesture only; gesture is inverted
};

/**
Expand Down Expand Up @@ -501,6 +502,10 @@ struct sway_config {
bool titlebar_separator;
bool scratchpad_minimize;

int workspace_gesture_spring_size;
bool workspace_gesture_wrap_around;
float workspace_gesture_threshold;

list_t *layer_criteria;

char *swaynag_command;
Expand Down
10 changes: 10 additions & 0 deletions include/sway/fx_util/animation_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef ANIMATION_UTILS_H
#define ANIMATION_UTILS_H

double lerp (double a, double b, double t);

double ease_out_cubic (double t);

// TODO: Add more easing functions in the future like ease_in and ease_in_out, etc...

#endif
30 changes: 30 additions & 0 deletions include/sway/output.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ struct decoration_data get_undecorated_decoration_data();
struct sway_server;
struct sway_container;

enum swipe_gesture_direction {
SWIPE_GESTURE_DIRECTION_NONE,
SWIPE_GESTURE_DIRECTION_HORIZONTAL,
SWIPE_GESTURE_DIRECTION_VERTICAL,
};

struct workspace_scroll {
double percent;
double avg_velocity;
int num_updates;
enum swipe_gesture_direction direction;
};

struct decoration_data {
float alpha;
float saturation;
Expand All @@ -32,6 +45,7 @@ struct render_data {
pixman_region32_t *damage;
struct wlr_box *clip_box;
struct decoration_data deco_data;
bool on_focused_workspace;
struct sway_view *view;
};

Expand Down Expand Up @@ -71,6 +85,8 @@ struct sway_output {
struct wl_listener needs_frame;
struct wl_listener request_state;

struct workspace_scroll workspace_scroll;

struct {
struct wl_signal disable;
} events;
Expand Down Expand Up @@ -227,6 +243,20 @@ void handle_output_manager_test(struct wl_listener *listener, void *data);
void handle_output_power_manager_set_mode(struct wl_listener *listener,
void *data);

struct workspace_scroll workspace_scroll_get_default();

bool workspace_scroll_equal(struct workspace_scroll *a, struct workspace_scroll *b);

void workspace_scroll_begin(struct sway_seat *seat,
enum swipe_gesture_direction direction);

void workspace_scroll_update(struct sway_seat *seat, struct gesture_tracker *tracker,
struct wlr_pointer_swipe_update_event *event, int invert);

void workspace_scroll_end(struct sway_seat *seat);

void workspace_scroll_reset(struct sway_seat *seat, struct sway_workspace *ws);

struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output);

#endif
6 changes: 4 additions & 2 deletions include/sway/tree/workspace.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,13 @@ struct sway_workspace *workspace_by_number(const char* name);

struct sway_workspace *workspace_by_name(const char*);

struct sway_workspace *workspace_output_next(struct sway_workspace *current);
struct sway_workspace *workspace_output_next(struct sway_workspace *current,
bool should_wrap);

struct sway_workspace *workspace_next(struct sway_workspace *current);

struct sway_workspace *workspace_output_prev(struct sway_workspace *current);
struct sway_workspace *workspace_output_prev(struct sway_workspace *current,
bool should_wrap);

struct sway_workspace *workspace_prev(struct sway_workspace *current);

Expand Down
5 changes: 5 additions & 0 deletions sway/commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ static const struct cmd_handler handlers[] = {
{ "bindgesture", cmd_bindgesture },
{ "bindswitch", cmd_bindswitch },
{ "bindsym", cmd_bindsym },
{ "bindworkspacegesture", cmd_bindworkspacegesture },
{ "blur", cmd_blur },
{ "blur_brightness", cmd_blur_brightness },
{ "blur_contrast", cmd_blur_contrast },
Expand Down Expand Up @@ -118,8 +119,12 @@ static const struct cmd_handler handlers[] = {
{ "unbindgesture", cmd_unbindgesture },
{ "unbindswitch", cmd_unbindswitch },
{ "unbindsym", cmd_unbindsym },
{ "unbindworkspacegesture", cmd_unbindworkspacegesture },
{ "workspace", cmd_workspace },
{ "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth },
{ "workspace_gesture_spring_size", cmd_ws_gesture_spring_size },
{ "workspace_gesture_threshold", cmd_ws_gesture_threshold },
{ "workspace_gesture_wrap_around", cmd_ws_gesture_wrap_around },
};

/* Config-time only commands. Keep alphabetized */
Expand Down
73 changes: 73 additions & 0 deletions sway/commands/gesture.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,76 @@ struct cmd_results *cmd_bindgesture(int argc, char **argv) {
struct cmd_results *cmd_unbindgesture(int argc, char **argv) {
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,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a copy+paste of cmd_bind_or_unbind_gesture

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as my other comment, why do we need a different file for this?

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);
}
2 changes: 2 additions & 0 deletions sway/commands/workspace.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/input/seat.h"
#include "sway/output.h"
#include "sway/tree/workspace.h"
#include "list.h"
#include "log.h"
#include "stringop.h"
#include "util.h"

static struct workspace_config *workspace_config_find_or_create(char *ws_name) {
struct workspace_config *wsc = workspace_find_config(ws_name);
Expand Down
49 changes: 49 additions & 0 deletions sway/commands/workspace_gesture.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#define _POSIX_C_SOURCE 200809L
#include "sway/commands.h"
#include "sway/config.h"
#include "util.h"

struct cmd_results *cmd_ws_gesture_spring_size(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "workspace_gesture_spring_size", EXPECTED_EQUAL_TO, 1))) {
return error;
}

char *inv;
int value = strtol(argv[0], &inv, 10);
if (*inv != '\0' || value < 0 || value > 250) {
return cmd_results_new(CMD_FAILURE, "Invalid size specified");
}

config->workspace_gesture_spring_size = value;

return cmd_results_new(CMD_SUCCESS, NULL);
}

struct cmd_results *cmd_ws_gesture_wrap_around(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "workspace_gesture_wrap_around", EXPECTED_EQUAL_TO, 1))) {
return error;
}

config->workspace_gesture_wrap_around = parse_boolean(argv[0], true);

return cmd_results_new(CMD_SUCCESS, NULL);
}

struct cmd_results *cmd_ws_gesture_threshold(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "workspace_gesture_threshold", EXPECTED_EQUAL_TO, 1))) {
return error;
}

char *err;
float val = strtof(argv[0], &err);
if (*err || val < 0.1f || val > 0.9f) {
return cmd_results_new(CMD_INVALID, "workspace_gesture_threshold float invalid. "
"Should be between 0.1 and 0.9");
}
config->workspace_gesture_threshold = val;

return cmd_results_new(CMD_SUCCESS, NULL);
}
Loading