Added gesture support

This commit is contained in:
Erik Reider 2024-01-21 20:01:11 +01:00
parent 6552778dd6
commit 0c6d69b7fe
6 changed files with 185 additions and 2 deletions

View file

@ -50,6 +50,8 @@ char *gesture_parse(const char *input, struct gesture *output) {
output->type = GESTURE_TYPE_PINCH;
} else if (strcmp(split->items[0], "swipe") == 0) {
output->type = GESTURE_TYPE_SWIPE;
} else if (strcmp(split->items[0], "workspace_swipe") == 0) {
output->type = GESTURE_TYPE_WORKSPACE_SWIPE;
} else {
return strformat("expected hold|pinch|swipe, got %s",
split->items[0]);
@ -117,11 +119,22 @@ const char *gesture_type_string(enum gesture_type type) {
return "pinch";
case GESTURE_TYPE_SWIPE:
return "swipe";
case GESTURE_TYPE_WORKSPACE_SWIPE:
return "workspace_swipe";
}
return NULL;
}
int gesture_workspace_swipe_command_parse(char *cmd) {
if (strcmp(cmd, "normal") == 0) {
return -1;
} else if (strcmp(cmd, "invert") == 0) {
return 1;
}
return 0;
}
const char *gesture_direction_string(enum gesture_direction direction) {
switch (direction) {
case GESTURE_DIRECTION_NONE:
@ -314,6 +327,7 @@ struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) {
}
__attribute__ ((fallthrough));
// Gestures with dx and dy
case GESTURE_TYPE_WORKSPACE_SWIPE:
case GESTURE_TYPE_SWIPE:
if (fabs(tracker->dx) > fabs(tracker->dy)) {
if (tracker->dx > 0) {

View file

@ -12,11 +12,14 @@ enum gesture_type {
GESTURE_TYPE_HOLD,
GESTURE_TYPE_PINCH,
GESTURE_TYPE_SWIPE,
GESTURE_TYPE_WORKSPACE_SWIPE,
};
// Turns single type enum value to constant string representation.
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
extern const uint8_t GESTURE_FINGERS_ANY;

View file

@ -202,6 +202,12 @@ void handle_output_manager_test(struct wl_listener *listener, void *data);
void handle_output_power_manager_set_mode(struct wl_listener *listener,
void *data);
void update_workspace_scroll_percent(int dx, int invert);
void snap_workspace_scroll_percent(int dx, int invert);
void reset_workspace_scroll_percent();
struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output);
#endif

View file

@ -154,6 +154,19 @@ static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, boo
return gesture_binding_remove(binding, argv[0]);
}
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 invert",
bindtype, errmsg);
}
break;
default:
break;
}
return gesture_binding_add(binding, argv[0], warn);
}

View file

@ -31,6 +31,7 @@
#include "sway/tree/root.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
#include "util.h"
struct sway_output *output_by_name_or_id(const char *name_or_id) {
for (int i = 0; i < root->outputs->length; ++i) {
@ -1103,3 +1104,95 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
oc = store_output_config(oc);
apply_output_config(oc, output);
}
static void workspace_scroll_mark_dirty() {
// Damage all outputs
struct sway_output *soutput;
wl_list_for_each(soutput, &root->all_outputs, link) {
output_damage_whole(soutput);
}
transaction_commit_dirty();
}
void update_workspace_scroll_percent(int dx, int invert) {
struct sway_seat *seat = input_manager_get_default_seat();
struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
struct sway_output *output = focused_ws->output;
int visible_index = list_find(output->workspaces, focused_ws);
if (visible_index == -1) {
return;
}
dx *= invert;
// TODO: Make the speed factor configurable?? Works well on my trackpad...
// Maybe also take in account the width of the actual trackpad??
const int SPEED_FACTOR = 500;
float percent = 0;
if (dx < 0) {
percent = (float) dx / SPEED_FACTOR;
} else if (dx > 0) {
percent = (float) dx / SPEED_FACTOR;
} else {
return;
}
// Limit the percent depending on if the workspace is the first/last or in
// the middle somewhere.
int min = -1, max = 1;
if (visible_index + 1 >= output->workspaces->length) {
max = 0;
}
if (visible_index == 0) {
min = 0;
}
output->workspace_scroll_percent = MIN(max, MAX(min, percent));
workspace_scroll_mark_dirty();
}
void snap_workspace_scroll_percent(int dx, int invert) {
struct sway_seat *seat = input_manager_get_default_seat();
struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
struct sway_output *output = focused_ws->output;
// TODO: Make the threshold configurable??
const float THRESHOLD = 0.35;
if (ABS(output->workspace_scroll_percent) <= THRESHOLD) {
goto reset;
}
dx *= invert;
int dir = 0;
if (dx < 0) {
dir = -1;
} else if (dx > 0) {
dir = 1;
} else {
goto reset;
}
int visible_index = list_find(output->workspaces, focused_ws);
size_t ws_index = wrap(visible_index + dir, output->workspaces->length);
struct sway_workspace *new_ws = output->workspaces->items[ws_index];
sway_log(SWAY_DEBUG, "Switched to workspace: %s\n", new_ws->name);
workspace_switch(new_ws);
seat_consider_warp_to_focus(seat);
reset:
// Reset the state
reset_workspace_scroll_percent();
}
void reset_workspace_scroll_percent() {
struct sway_seat *seat = input_manager_get_default_seat();
struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
struct sway_output *output = focused_ws->output;
output->workspace_scroll_percent = 0;
workspace_scroll_mark_dirty();
}

View file

@ -1011,6 +1011,9 @@ static void handle_swipe_begin(struct sway_seat *seat,
if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) {
struct seatop_default_event *seatop = seat->seatop_data;
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers);
} else if (gesture_binding_check(bindings, GESTURE_TYPE_WORKSPACE_SWIPE, event->fingers, device)) {
struct seatop_default_event *seatop = seat->seatop_data;
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE, event->fingers);
} else {
// ... otherwise forward to client
struct sway_cursor *cursor = seat->cursor;
@ -1028,6 +1031,41 @@ static void handle_swipe_update(struct sway_seat *seat,
if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
gesture_tracker_update(&seatop->gestures,
event->dx, event->dy, NAN, NAN);
} else if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE)) {
gesture_tracker_update(&seatop->gestures,
event->dx, event->dy, NAN, NAN);
struct gesture_tracker *tracker = &seatop->gestures;
struct sway_input_device *device =
event->pointer ? event->pointer->base.data : NULL;
// Determine name of input that received gesture
char *input = device
? input_device_get_identifier(device->wlr_device)
: strdup("*");
struct gesture gesture = {
.fingers = tracker->fingers,
.type = tracker->type,
};
if (fabs(tracker->dx) > fabs(tracker->dy)) {
if (tracker->dx > 0) {
gesture.directions |= GESTURE_DIRECTION_RIGHT;
} else {
gesture.directions |= GESTURE_DIRECTION_LEFT;
}
} else {
if (tracker->dy > 0) {
gesture.directions |= GESTURE_DIRECTION_DOWN;
} else {
gesture.directions |= GESTURE_DIRECTION_UP;
}
}
struct sway_gesture_binding *current = gesture_binding_match(config->current_mode->gesture_bindings, &gesture, input);
int invert = gesture_workspace_swipe_command_parse(current->command);
update_workspace_scroll_percent(seatop->gestures.dx, invert);
} else {
// ... otherwise forward to client
struct sway_cursor *cursor = seat->cursor;
@ -1041,13 +1079,21 @@ static void handle_swipe_end(struct sway_seat *seat,
struct wlr_pointer_swipe_end_event *event) {
// Ensure gesture is being tracked and was not cancelled
struct seatop_default_event *seatop = seat->seatop_data;
if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE) &&
!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE)) {
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_swipe_end(cursor->pointer_gestures,
cursor->seat->wlr_seat, event->time_msec, event->cancelled);
return;
}
if (event->cancelled) {
switch (seatop->gestures.type) {
case GESTURE_TYPE_WORKSPACE_SWIPE:
reset_workspace_scroll_percent();
break;
default:
break;
}
gesture_tracker_cancel(&seatop->gestures);
return;
}
@ -1059,7 +1105,15 @@ static void handle_swipe_end(struct sway_seat *seat,
&seatop->gestures, device);
if (binding) {
gesture_binding_execute(seat, binding);
switch (binding->gesture.type) {
case GESTURE_TYPE_WORKSPACE_SWIPE:;
int invert = gesture_workspace_swipe_command_parse(binding->command);
snap_workspace_scroll_percent(seatop->gestures.dx, invert);
break;
default:
gesture_binding_execute(seat, binding);
break;
}
}
}