Added vertical gesture support

This commit is contained in:
Erik Reider 2024-01-22 16:39:44 +01:00
parent fdf764d3f6
commit 067613854b
8 changed files with 116 additions and 55 deletions

View file

@ -96,6 +96,10 @@ 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) {
output->directions |= GESTURE_DIRECTION_HORIZONTAL;
} else if (strcmp(item, "vertical") == 0) {
output->directions |= GESTURE_DIRECTION_VERTICAL;
} else { } else {
return strformat("expected direction, got %s", item); return strformat("expected direction, got %s", item);
} }
@ -129,7 +133,7 @@ const char *gesture_type_string(enum gesture_type type) {
int gesture_workspace_swipe_command_parse(char *cmd) { int gesture_workspace_swipe_command_parse(char *cmd) {
if (strcmp(cmd, "normal") == 0) { if (strcmp(cmd, "normal") == 0) {
return -1; return -1;
} else if (strcmp(cmd, "invert") == 0) { } else if (strcmp(cmd, "inverted") == 0) {
return 1; return 1;
} }
return 0; return 0;
@ -155,6 +159,10 @@ 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;
@ -328,6 +336,12 @@ 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: 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) {

View file

@ -39,6 +39,9 @@ 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.

View file

@ -13,6 +13,12 @@
struct sway_server; struct sway_server;
struct sway_container; struct sway_container;
enum swipe_gesture_direction {
SWIPE_GESTURE_DIRECTION_NONE,
SWIPE_GESTURE_DIRECTION_HORIZONTAL,
SWIPE_GESTURE_DIRECTION_VERTICAL,
};
struct render_data { struct render_data {
pixman_region32_t *damage; pixman_region32_t *damage;
struct wlr_box *clip_box; struct wlr_box *clip_box;
@ -58,7 +64,10 @@ struct sway_output {
struct wl_listener frame; struct wl_listener frame;
struct wl_listener needs_frame; struct wl_listener needs_frame;
float workspace_scroll_percent; struct {
float percent;
enum swipe_gesture_direction direction;
} workspace_scroll;
struct { struct {
struct wl_signal disable; struct wl_signal disable;
@ -202,7 +211,8 @@ void handle_output_manager_test(struct wl_listener *listener, void *data);
void handle_output_power_manager_set_mode(struct wl_listener *listener, void handle_output_power_manager_set_mode(struct wl_listener *listener,
void *data); void *data);
void update_workspace_scroll_percent(struct sway_seat *seat, int dx, int invert); void update_workspace_scroll_percent(struct sway_seat *seat, int gesture_percent,
int invert, enum swipe_gesture_direction direction);
void snap_workspace_scroll_percent(struct sway_seat *seat); void snap_workspace_scroll_percent(struct sway_seat *seat);

View file

@ -160,7 +160,7 @@ static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, boo
if (gesture_workspace_swipe_command_parse(binding->command) == 0) { if (gesture_workspace_swipe_command_parse(binding->command) == 0) {
free(binding); free(binding);
return cmd_results_new(CMD_FAILURE, return cmd_results_new(CMD_FAILURE,
"Invalid %s command (%s). Either normal or invert", "Invalid %s command (%s). Either normal or inverted",
bindtype, errmsg); bindtype, errmsg);
} }
break; break;

View file

@ -543,7 +543,7 @@ static int output_repaint_timer_handler(void *data) {
if (fullscreen_con && fullscreen_con->view && !debug.noscanout if (fullscreen_con && fullscreen_con->view && !debug.noscanout
// Only output to monitor without compositing when saturation is changed // Only output to monitor without compositing when saturation is changed
&& fullscreen_con->saturation == 1.0f && && fullscreen_con->saturation == 1.0f &&
output->workspace_scroll_percent == 0.0f) { output->workspace_scroll.percent == 0.0f) {
// Try to scan-out the fullscreen view // Try to scan-out the fullscreen view
static bool last_scanned_out = false; static bool last_scanned_out = false;
bool scanned_out = bool scanned_out =
@ -991,7 +991,8 @@ void handle_new_output(struct wl_listener *listener, void *data) {
wlr_damage_ring_set_bounds(&output->damage_ring, width, height); wlr_damage_ring_set_bounds(&output->damage_ring, width, height);
update_output_manager_config(server); update_output_manager_config(server);
output->workspace_scroll_percent = 0.0f; output->workspace_scroll.percent = 0.0f;
output->workspace_scroll.direction = SWIPE_GESTURE_DIRECTION_NONE;
} }
void handle_output_layout_change(struct wl_listener *listener, void handle_output_layout_change(struct wl_listener *listener,
@ -1105,7 +1106,8 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
apply_output_config(oc, output); apply_output_config(oc, output);
} }
void update_workspace_scroll_percent(struct sway_seat *seat, int dx, int invert) { void update_workspace_scroll_percent(struct sway_seat *seat, int gesture_percent,
int invert, enum swipe_gesture_direction direction) {
struct sway_workspace *focused_ws = seat_get_focused_workspace(seat); struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
struct sway_output *output = focused_ws->output; struct sway_output *output = focused_ws->output;
@ -1114,16 +1116,16 @@ void update_workspace_scroll_percent(struct sway_seat *seat, int dx, int invert)
return; return;
} }
dx *= invert; gesture_percent *= invert;
// TODO: Make the speed factor configurable?? Works well on my trackpad... // TODO: Make the speed factor configurable?? Works well on my trackpad...
// Maybe also take in account the width of the actual trackpad?? // Maybe also take in account the width of the actual trackpad??
const int SPEED_FACTOR = 500; const int SPEED_FACTOR = 500;
float percent = 0; float percent = 0;
if (dx < 0) { if (gesture_percent < 0) {
percent = (float) dx / SPEED_FACTOR; percent = (float) gesture_percent / SPEED_FACTOR;
} else if (dx > 0) { } else if (gesture_percent > 0) {
percent = (float) dx / SPEED_FACTOR; percent = (float) gesture_percent / SPEED_FACTOR;
} else { } else {
return; return;
} }
@ -1148,7 +1150,8 @@ void update_workspace_scroll_percent(struct sway_seat *seat, int dx, int invert)
// NOTE: Can be adjusted in the future to wrap around workspaces // NOTE: Can be adjusted in the future to wrap around workspaces
min = -spring_limit; min = -spring_limit;
} }
output->workspace_scroll_percent = MIN(max, MAX(min, percent)); output->workspace_scroll.percent = MIN(max, MAX(min, percent));
output->workspace_scroll.direction = direction;
output_damage_whole(output); output_damage_whole(output);
transaction_commit_dirty(); transaction_commit_dirty();
@ -1160,14 +1163,14 @@ void snap_workspace_scroll_percent(struct sway_seat *seat) {
// TODO: Make the threshold configurable?? // TODO: Make the threshold configurable??
const float THRESHOLD = 0.35; const float THRESHOLD = 0.35;
if (fabs(output->workspace_scroll_percent) <= THRESHOLD) { if (fabs(output->workspace_scroll.percent) <= THRESHOLD) {
goto reset_state; goto reset_state;
} }
int dir = 0; int dir = 0;
if (output->workspace_scroll_percent < 0) { if (output->workspace_scroll.percent < 0) {
dir = -1; dir = -1;
} else if (output->workspace_scroll_percent > 0) { } else if (output->workspace_scroll.percent > 0) {
dir = 1; dir = 1;
} else { } else {
// Skip setting workspace if the percentage is zero // Skip setting workspace if the percentage is zero
@ -1184,7 +1187,8 @@ void snap_workspace_scroll_percent(struct sway_seat *seat) {
reset_state: reset_state:
// Reset the state // Reset the state
output->workspace_scroll_percent = 0; output->workspace_scroll.percent = 0;
output->workspace_scroll.direction = SWIPE_GESTURE_DIRECTION_NONE;
output_damage_whole(output); output_damage_whole(output);
transaction_commit_dirty(); transaction_commit_dirty();

View file

@ -83,14 +83,29 @@ static struct wlr_box get_monitor_box(struct wlr_output *output) {
// Adjust the box position when switching the workspace // Adjust the box position when switching the workspace
static void adjust_box_to_workspace_offset(struct wlr_box *box, static void adjust_box_to_workspace_offset(struct wlr_box *box,
struct decoration_data *deco_data, struct sway_workspace *ws) { struct decoration_data *deco_data, struct sway_workspace *ws) {
int ws_width = ws->current.width + ws->current_gaps.left + ws->current_gaps.right; float scroll_percent = ws->output->workspace_scroll.percent;
float scroll_percent = ws->output->workspace_scroll_percent;
box->x -= ws_width * scroll_percent; int ws_dimen;
int *box_coord;
switch (ws->output->workspace_scroll.direction) {
case SWIPE_GESTURE_DIRECTION_NONE:
return;
case SWIPE_GESTURE_DIRECTION_HORIZONTAL:
ws_dimen = ws->current.width + ws->current_gaps.left + ws->current_gaps.right;
box_coord = &box->x;
break;
case SWIPE_GESTURE_DIRECTION_VERTICAL:
ws_dimen = ws->current.height + ws->current_gaps.top + ws->current_gaps.bottom;
box_coord = &box->y;
break;
}
*box_coord -= ws_dimen * scroll_percent;
if (!deco_data->on_focused_workspace) { if (!deco_data->on_focused_workspace) {
if (scroll_percent > 0) { if (scroll_percent > 0) {
box->x += ws_width; *box_coord += ws_dimen;
} else if (scroll_percent < 0) { } else if (scroll_percent < 0) {
box->x -= ws_width; *box_coord -= ws_dimen;
} }
} }
} }
@ -99,15 +114,31 @@ static void adjust_box_to_workspace_offset(struct wlr_box *box,
// Fixes containers being rendered across workspaces while switching. // Fixes containers being rendered across workspaces while switching.
static void adjust_damage_to_workspace_bounds(pixman_region32_t *damage, static void adjust_damage_to_workspace_bounds(pixman_region32_t *damage,
struct decoration_data *deco_data, struct sway_workspace *ws) { struct decoration_data *deco_data, struct sway_workspace *ws) {
float scroll_percent = ws->output->workspace_scroll_percent; float scale = ws->output->wlr_output->scale;
float scroll_percent = ws->output->workspace_scroll.percent;
int x = 0, y = 0;
int ws_width = ws->current.width + ws->current_gaps.left + ws->current_gaps.right; int ws_dimen;
int x = round(-ws_width * scroll_percent); int *coord;
switch (ws->output->workspace_scroll.direction) {
case SWIPE_GESTURE_DIRECTION_NONE:
return;
case SWIPE_GESTURE_DIRECTION_HORIZONTAL:
ws_dimen = ws->current.width + ws->current_gaps.left + ws->current_gaps.right;
coord = &x;
break;
case SWIPE_GESTURE_DIRECTION_VERTICAL:
ws_dimen = ws->current.height + ws->current_gaps.top + ws->current_gaps.bottom;
coord = &y;
break;
}
*coord = round(-ws_dimen * scroll_percent);
if (!deco_data->on_focused_workspace) { if (!deco_data->on_focused_workspace) {
if (scroll_percent > 0) { if (scroll_percent > 0) {
x += ws_width; *coord += ws_dimen;
} else if (scroll_percent < 0) { } else if (scroll_percent < 0) {
x -= ws_width; *coord -= ws_dimen;
} }
} }
@ -115,7 +146,7 @@ static void adjust_damage_to_workspace_bounds(pixman_region32_t *damage,
pixman_region32_intersect_rect(damage, damage, pixman_region32_intersect_rect(damage, damage,
monitor_box.x, monitor_box.y, monitor_box.x, monitor_box.y,
monitor_box.width, monitor_box.height); monitor_box.width, monitor_box.height);
pixman_region32_translate(damage, x * ws->output->wlr_output->scale, 0); pixman_region32_translate(damage, x * scale, y * scale);
} }
/** /**
@ -1837,10 +1868,10 @@ static void render_workspace(struct sway_output *output,
struct sway_workspace *other_ws) { struct sway_workspace *other_ws) {
struct sway_workspace *workspaces[2] = { ws, NULL }; struct sway_workspace *workspaces[2] = { ws, NULL };
if (output->workspace_scroll_percent < 0) { if (output->workspace_scroll.percent < 0) {
workspaces[0] = other_ws; workspaces[0] = other_ws;
workspaces[1] = ws; workspaces[1] = ws;
} else if (output->workspace_scroll_percent > 0) { } else if (output->workspace_scroll.percent > 0) {
workspaces[1] = other_ws; workspaces[1] = other_ws;
} }
@ -1924,7 +1955,7 @@ static void render_floating(struct sway_output *soutput,
struct sway_output *output = root->outputs->items[i]; struct sway_output *output = root->outputs->items[i];
// Don't render floating windows across outputs when switching workspaces // Don't render floating windows across outputs when switching workspaces
if (output->workspace_scroll_percent != 0 && output != soutput) { if (output->workspace_scroll.percent != 0 && output != soutput) {
continue; continue;
} }
@ -1932,7 +1963,7 @@ static void render_floating(struct sway_output *soutput,
for (int j = 0; j < output->current.workspaces->length; ++j) { for (int j = 0; j < output->current.workspaces->length; ++j) {
struct sway_workspace *ws = output->current.workspaces->items[j]; struct sway_workspace *ws = output->current.workspaces->items[j];
float scroll_percent = soutput->workspace_scroll_percent; float scroll_percent = soutput->workspace_scroll.percent;
// Only render visible workspace when not scrolling // Only render visible workspace when not scrolling
if (!workspace_is_visible(ws) && scroll_percent == 0) { if (!workspace_is_visible(ws) && scroll_percent == 0) {
@ -2026,7 +2057,7 @@ void output_render(struct sway_output *output, struct timespec *when,
// Get the sibling workspaces // Get the sibling workspaces
struct sway_workspace *other_ws = NULL; struct sway_workspace *other_ws = NULL;
if (output->workspace_scroll_percent < 0) { if (output->workspace_scroll.percent < 0) {
other_ws = workspace_output_prev_wrap(workspace, false); other_ws = workspace_output_prev_wrap(workspace, false);
} else { } else {
other_ws = workspace_output_next_wrap(workspace, false); other_ws = workspace_output_next_wrap(workspace, false);
@ -2053,7 +2084,7 @@ void output_render(struct sway_output *output, struct timespec *when,
int output_width, output_height; int output_width, output_height;
wlr_output_transformed_resolution(wlr_output, &output_width, &output_height); wlr_output_transformed_resolution(wlr_output, &output_width, &output_height);
if (debug.damage == DAMAGE_RERENDER || output->workspace_scroll_percent != 0) { if (debug.damage == DAMAGE_RERENDER || output->workspace_scroll.percent != 0) {
pixman_region32_union_rect(damage, damage, 0, 0, output_width, output_height); pixman_region32_union_rect(damage, damage, 0, 0, output_width, output_height);
} }
@ -2105,7 +2136,7 @@ void output_render(struct sway_output *output, struct timespec *when,
goto render_overlay; goto render_overlay;
} }
if (fullscreen_con && output->workspace_scroll_percent == 0) { if (fullscreen_con && output->workspace_scroll.percent == 0) {
// Only draw fullscreen con if not transitioning between workspaces // Only draw fullscreen con if not transitioning between workspaces
render_fullscreen_con(damage, output, fullscreen_con, workspace, true); render_fullscreen_con(damage, output, fullscreen_con, workspace, true);
} else { } else {
@ -2198,10 +2229,10 @@ void output_render(struct sway_output *output, struct timespec *when,
// Render the fullscreen containers on top // Render the fullscreen containers on top
if (has_fullscreen) { if (has_fullscreen) {
struct sway_workspace *workspaces[2] = { workspace, NULL }; struct sway_workspace *workspaces[2] = { workspace, NULL };
if (output->workspace_scroll_percent < 0) { if (output->workspace_scroll.percent < 0) {
workspaces[0] = other_ws; workspaces[0] = other_ws;
workspaces[1] = workspace; workspaces[1] = workspace;
} else if (output->workspace_scroll_percent > 0) { } else if (output->workspace_scroll.percent > 0) {
workspaces[1] = other_ws; workspaces[1] = other_ws;
} }
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {

View file

@ -1036,7 +1036,6 @@ static void handle_swipe_update(struct sway_seat *seat,
event->dx, event->dy, NAN, NAN); event->dx, event->dy, NAN, NAN);
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;
// Determine name of input that received gesture // Determine name of input that received gesture
@ -1047,25 +1046,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)
? GESTURE_DIRECTION_HORIZONTAL : GESTURE_DIRECTION_VERTICAL,
}; };
if (fabs(tracker->dx) > fabs(tracker->dy)) {
if (tracker->dx > 0) { struct sway_gesture_binding *binding = gesture_binding_match(
gesture.directions |= GESTURE_DIRECTION_RIGHT; config->current_mode->gesture_bindings, &gesture, input);
} else { if (binding) {
gesture.directions |= GESTURE_DIRECTION_LEFT; int invert = gesture_workspace_swipe_command_parse(binding->command);
} if ((binding->gesture.directions & GESTURE_DIRECTION_VERTICAL) ==
} else { GESTURE_DIRECTION_VERTICAL) {
if (tracker->dy > 0) { update_workspace_scroll_percent(seat, tracker->dy, invert,
gesture.directions |= GESTURE_DIRECTION_DOWN; SWIPE_GESTURE_DIRECTION_VERTICAL);
} else { } else if ((binding->gesture.directions & GESTURE_DIRECTION_HORIZONTAL) ==
gesture.directions |= GESTURE_DIRECTION_UP; GESTURE_DIRECTION_HORIZONTAL) {
update_workspace_scroll_percent(seat, tracker->dx, invert,
SWIPE_GESTURE_DIRECTION_HORIZONTAL);
} }
} }
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(seat, seatop->gestures.dx, invert);
} else { } else {
// ... otherwise forward to client // ... otherwise forward to client
struct sway_cursor *cursor = seat->cursor; struct sway_cursor *cursor = seat->cursor;

View file

@ -620,7 +620,8 @@ bool workspace_switch(struct sway_workspace *workspace) {
sway_log(SWAY_DEBUG, "Switching to workspace %p:%s", sway_log(SWAY_DEBUG, "Switching to workspace %p:%s",
workspace, workspace->name); workspace, workspace->name);
workspace->output->workspace_scroll_percent = 0.0f; workspace->output->workspace_scroll.percent = 0.0f;
workspace->output->workspace_scroll.direction = SWIPE_GESTURE_DIRECTION_NONE;
struct sway_node *next = seat_get_focus_inactive(seat, &workspace->node); struct sway_node *next = seat_get_focus_inactive(seat, &workspace->node);
if (next == NULL) { if (next == NULL) {
next = &workspace->node; next = &workspace->node;