From 067613854bdda8a0d35b441dfb0f410dd03d4a19 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:39:44 +0100 Subject: [PATCH] Added vertical gesture support --- common/gesture.c | 16 ++++++++- include/gesture.h | 3 ++ include/sway/output.h | 14 ++++++-- sway/commands/gesture.c | 2 +- sway/desktop/output.c | 30 +++++++++------- sway/desktop/render.c | 71 ++++++++++++++++++++++++++----------- sway/input/seatop_default.c | 32 ++++++++--------- sway/tree/workspace.c | 3 +- 8 files changed, 116 insertions(+), 55 deletions(-) diff --git a/common/gesture.c b/common/gesture.c index dbb8a5a8..969521f9 100644 --- a/common/gesture.c +++ b/common/gesture.c @@ -96,6 +96,10 @@ char *gesture_parse(const char *input, struct gesture *output) { output->directions |= GESTURE_DIRECTION_CLOCKWISE; } else if (strcmp(item, "counterclockwise") == 0) { 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 { 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) { if (strcmp(cmd, "normal") == 0) { return -1; - } else if (strcmp(cmd, "invert") == 0) { + } else if (strcmp(cmd, "inverted") == 0) { return 1; } return 0; @@ -155,6 +159,10 @@ const char *gesture_direction_string(enum gesture_direction direction) { return "clockwise"; case GESTURE_DIRECTION_COUNTERCLOCKWISE: return "counterclockwise"; + case GESTURE_DIRECTION_HORIZONTAL: + return "horizontal"; + case GESTURE_DIRECTION_VERTICAL: + return "vertical"; } return NULL; @@ -328,6 +336,12 @@ struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) { __attribute__ ((fallthrough)); // 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: if (fabs(tracker->dx) > fabs(tracker->dy)) { if (tracker->dx > 0) { diff --git a/include/gesture.h b/include/gesture.h index 459053ba..3b3fd168 100644 --- a/include/gesture.h +++ b/include/gesture.h @@ -39,6 +39,9 @@ enum gesture_direction { // Directions based on rotation GESTURE_DIRECTION_CLOCKWISE = 1 << 6, 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. diff --git a/include/sway/output.h b/include/sway/output.h index 3a678278..2a28b2e2 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -13,6 +13,12 @@ struct sway_server; struct sway_container; +enum swipe_gesture_direction { + SWIPE_GESTURE_DIRECTION_NONE, + SWIPE_GESTURE_DIRECTION_HORIZONTAL, + SWIPE_GESTURE_DIRECTION_VERTICAL, +}; + struct render_data { pixman_region32_t *damage; struct wlr_box *clip_box; @@ -58,7 +64,10 @@ struct sway_output { struct wl_listener frame; struct wl_listener needs_frame; - float workspace_scroll_percent; + struct { + float percent; + enum swipe_gesture_direction direction; + } workspace_scroll; struct { 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 *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); diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c index a85b72d6..a75d54bb 100644 --- a/sway/commands/gesture.c +++ b/sway/commands/gesture.c @@ -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) { free(binding); return cmd_results_new(CMD_FAILURE, - "Invalid %s command (%s). Either normal or invert", + "Invalid %s command (%s). Either normal or inverted", bindtype, errmsg); } break; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 48b11bf6..5c04fdb4 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -543,7 +543,7 @@ static int output_repaint_timer_handler(void *data) { if (fullscreen_con && fullscreen_con->view && !debug.noscanout // Only output to monitor without compositing when saturation is changed && fullscreen_con->saturation == 1.0f && - output->workspace_scroll_percent == 0.0f) { + output->workspace_scroll.percent == 0.0f) { // Try to scan-out the fullscreen view static bool last_scanned_out = false; 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); 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, @@ -1105,7 +1106,8 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, 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_output *output = focused_ws->output; @@ -1114,16 +1116,16 @@ void update_workspace_scroll_percent(struct sway_seat *seat, int dx, int invert) return; } - dx *= invert; + gesture_percent *= 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; + if (gesture_percent < 0) { + percent = (float) gesture_percent / SPEED_FACTOR; + } else if (gesture_percent > 0) { + percent = (float) gesture_percent / SPEED_FACTOR; } else { 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 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); transaction_commit_dirty(); @@ -1160,14 +1163,14 @@ void snap_workspace_scroll_percent(struct sway_seat *seat) { // TODO: Make the threshold configurable?? const float THRESHOLD = 0.35; - if (fabs(output->workspace_scroll_percent) <= THRESHOLD) { + if (fabs(output->workspace_scroll.percent) <= THRESHOLD) { goto reset_state; } int dir = 0; - if (output->workspace_scroll_percent < 0) { + if (output->workspace_scroll.percent < 0) { dir = -1; - } else if (output->workspace_scroll_percent > 0) { + } else if (output->workspace_scroll.percent > 0) { dir = 1; } else { // Skip setting workspace if the percentage is zero @@ -1184,7 +1187,8 @@ void snap_workspace_scroll_percent(struct sway_seat *seat) { reset_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); transaction_commit_dirty(); diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 1d938cf7..84912a0f 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -83,14 +83,29 @@ static struct wlr_box get_monitor_box(struct wlr_output *output) { // Adjust the box position when switching the workspace static void adjust_box_to_workspace_offset(struct wlr_box *box, 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; - box->x -= ws_width * scroll_percent; + float scroll_percent = ws->output->workspace_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 (scroll_percent > 0) { - box->x += ws_width; + *box_coord += ws_dimen; } 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. static void adjust_damage_to_workspace_bounds(pixman_region32_t *damage, 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 x = round(-ws_width * scroll_percent); + int ws_dimen; + 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 (scroll_percent > 0) { - x += ws_width; + *coord += ws_dimen; } 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, monitor_box.x, monitor_box.y, 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 *workspaces[2] = { ws, NULL }; - if (output->workspace_scroll_percent < 0) { + if (output->workspace_scroll.percent < 0) { workspaces[0] = other_ws; workspaces[1] = ws; - } else if (output->workspace_scroll_percent > 0) { + } else if (output->workspace_scroll.percent > 0) { workspaces[1] = other_ws; } @@ -1924,7 +1955,7 @@ static void render_floating(struct sway_output *soutput, struct sway_output *output = root->outputs->items[i]; // 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; } @@ -1932,7 +1963,7 @@ static void render_floating(struct sway_output *soutput, for (int j = 0; j < output->current.workspaces->length; ++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 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 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); } else { 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; 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); } @@ -2105,7 +2136,7 @@ void output_render(struct sway_output *output, struct timespec *when, 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 render_fullscreen_con(damage, output, fullscreen_con, workspace, true); } else { @@ -2198,10 +2229,10 @@ void output_render(struct sway_output *output, struct timespec *when, // Render the fullscreen containers on top if (has_fullscreen) { struct sway_workspace *workspaces[2] = { workspace, NULL }; - if (output->workspace_scroll_percent < 0) { + if (output->workspace_scroll.percent < 0) { workspaces[0] = other_ws; workspaces[1] = workspace; - } else if (output->workspace_scroll_percent > 0) { + } else if (output->workspace_scroll.percent > 0) { workspaces[1] = other_ws; } for (int i = 0; i < 2; i++) { diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 8dda4993..f88f1c55 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -1036,7 +1036,6 @@ static void handle_swipe_update(struct sway_seat *seat, 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 @@ -1047,25 +1046,24 @@ static void handle_swipe_update(struct sway_seat *seat, struct gesture gesture = { .fingers = tracker->fingers, .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) { - 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 *binding = gesture_binding_match( + config->current_mode->gesture_bindings, &gesture, input); + if (binding) { + int invert = gesture_workspace_swipe_command_parse(binding->command); + if ((binding->gesture.directions & GESTURE_DIRECTION_VERTICAL) == + GESTURE_DIRECTION_VERTICAL) { + 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, + 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 { // ... otherwise forward to client struct sway_cursor *cursor = seat->cursor; diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 99c390b4..4723fdfd 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -620,7 +620,8 @@ bool workspace_switch(struct sway_workspace *workspace) { sway_log(SWAY_DEBUG, "Switching to workspace %p:%s", 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); if (next == NULL) { next = &workspace->node;