diff --git a/include/sway/output.h b/include/sway/output.h index 2a28b2e2..3c5edfe9 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -19,6 +19,13 @@ enum swipe_gesture_direction { SWIPE_GESTURE_DIRECTION_VERTICAL, }; +struct workspace_scroll { + double percent; + double avg_velocity; + int num_updates; + enum swipe_gesture_direction direction; +}; + struct render_data { pixman_region32_t *damage; struct wlr_box *clip_box; @@ -64,10 +71,7 @@ struct sway_output { struct wl_listener frame; struct wl_listener needs_frame; - struct { - float percent; - enum swipe_gesture_direction direction; - } workspace_scroll; + struct workspace_scroll workspace_scroll; struct { struct wl_signal disable; @@ -211,10 +215,13 @@ 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 gesture_percent, - int invert, enum swipe_gesture_direction direction); +void workspace_scroll_begin(struct sway_seat *seat, + enum swipe_gesture_direction direction); -void snap_workspace_scroll_percent(struct sway_seat *seat); +void workspace_scroll_update(struct sway_seat *seat, double delta_sum, + enum swipe_gesture_direction direction); + +void workspace_scroll_end(struct sway_seat *seat); struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 7b64487d..8499696c 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -36,6 +36,15 @@ #define PREV_WS_LIMIT -1.0f #define NEXT_WS_LIMIT 1.0f +static struct workspace_scroll workspace_scroll_get_default() { + return (struct workspace_scroll) { + .percent = 0, + .avg_velocity = 0, + .num_updates = 0, + .direction = SWIPE_GESTURE_DIRECTION_NONE, + }; +} + struct sway_output *output_by_name_or_id(const char *name_or_id) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; @@ -994,8 +1003,7 @@ 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.direction = SWIPE_GESTURE_DIRECTION_NONE; + output->workspace_scroll = workspace_scroll_get_default(); } void handle_output_layout_change(struct wl_listener *listener, @@ -1109,39 +1117,49 @@ 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 gesture_percent, - int invert, enum swipe_gesture_direction direction) { +void workspace_scroll_begin(struct sway_seat *seat, + enum swipe_gesture_direction direction) { struct sway_workspace *focused_ws = seat_get_focused_workspace(seat); struct sway_output *output = focused_ws->output; + // Reset the state + output->workspace_scroll = workspace_scroll_get_default(); + output->workspace_scroll.direction = direction; + + output_damage_whole(output); + transaction_commit_dirty(); +} + +void workspace_scroll_update(struct sway_seat *seat, double delta_sum, + enum swipe_gesture_direction direction) { + struct sway_workspace *focused_ws = seat_get_focused_workspace(seat); + struct sway_output *output = focused_ws->output; + struct workspace_scroll *ws_scroll = &output->workspace_scroll; + + if (direction != ws_scroll->direction) { + return; + } + int visible_index = list_find(output->workspaces, focused_ws); if (visible_index == -1) { return; } - gesture_percent *= invert; + // Get the updated average velocity + ws_scroll->avg_velocity = fabs(delta_sum) / (++ws_scroll->num_updates); + // TODO: Make configurable + const int SPEED_FACTOR = 750; + double percent = delta_sum / SPEED_FACTOR; - // 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 (gesture_percent < 0) { - percent = (float) gesture_percent / SPEED_FACTOR; - } else if (gesture_percent > 0) { - percent = (float) gesture_percent / SPEED_FACTOR; - } else { - return; - } - - float min = PREV_WS_LIMIT, max = NEXT_WS_LIMIT; + double min = PREV_WS_LIMIT, max = NEXT_WS_LIMIT; if (!config->workspace_gesture_wrap_around) { // Visualized to the user that this is the last / first workspace by // allowing a small swipe, a "Spring effect" - float spring_limit = (float) config->workspace_gesture_spring_size / + double spring_limit = (double) config->workspace_gesture_spring_size / output->width * output->wlr_output->scale; // Make sure that the limit is always smaller than the threshold to // avoid accidental workspace switches - float small_threshold = MAX(config->workspace_gesture_threshold - 0.1, 0); + double small_threshold = MAX(config->workspace_gesture_threshold - 0.1, 0); spring_limit = MIN(small_threshold, spring_limit); // Limit the percent depending on if the workspace is the first/last or in // the middle somewhere. @@ -1152,32 +1170,42 @@ void update_workspace_scroll_percent(struct sway_seat *seat, int gesture_percent min = -spring_limit; } } - output->workspace_scroll.percent = MIN(max, MAX(min, percent)); - output->workspace_scroll.direction = direction; + ws_scroll->percent = CLAMP(percent, min, max); + ws_scroll->direction = direction; output_damage_whole(output); transaction_commit_dirty(); } -void snap_workspace_scroll_percent(struct sway_seat *seat) { +void workspace_scroll_end(struct sway_seat *seat) { struct sway_workspace *focused_ws = seat_get_focused_workspace(seat); struct sway_output *output = focused_ws->output; + struct workspace_scroll *ws_scroll = &output->workspace_scroll; - if (fabs(output->workspace_scroll.percent) <= config->workspace_gesture_threshold) { - goto reset_state; - } + int visible_index = list_find(output->workspaces, focused_ws); - int dir = 0; - if (output->workspace_scroll.percent < 0) { + bool not_edge_ws = config->workspace_gesture_wrap_around; + int dir; + if (ws_scroll->percent < 0) { dir = PREV_WS_LIMIT; - } else if (output->workspace_scroll.percent > 0) { + not_edge_ws |= visible_index > 0; + } else if (ws_scroll->percent > 0) { dir = NEXT_WS_LIMIT; + not_edge_ws |= visible_index + 1 < output->workspaces->length; } else { // Skip setting workspace if the percentage is zero goto reset_state; } - int visible_index = list_find(output->workspaces, focused_ws); + // TODO: Make configurable + const int VELOCITY_NEEDED = 8; + // Only switch workspaces when the percent exceeds the threshold or if + // the avg_speed exceeds the limit (for fast but short swipes). + bool threshold_met = fabs(ws_scroll->percent) >= config->workspace_gesture_threshold; + bool enough_velocity = ws_scroll->avg_velocity >= VELOCITY_NEEDED && not_edge_ws; + if (!threshold_met && !enough_velocity) { + goto reset_state; + } size_t ws_index = wrap(visible_index + dir, output->workspaces->length); struct sway_workspace *new_ws = output->workspaces->items[ws_index]; @@ -1187,8 +1215,7 @@ void snap_workspace_scroll_percent(struct sway_seat *seat) { reset_state: // Reset the state - output->workspace_scroll.percent = 0; - output->workspace_scroll.direction = SWIPE_GESTURE_DIRECTION_NONE; + output->workspace_scroll = workspace_scroll_get_default(); output_damage_whole(output); transaction_commit_dirty(); diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 0313b80b..6d861318 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -1014,9 +1014,11 @@ static void handle_swipe_begin(struct sway_seat *seat, } else if (gesture_binding_check(bindings, GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL, event->fingers, device)) { struct seatop_default_event *seatop = seat->seatop_data; gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL, event->fingers); + workspace_scroll_begin(seat, SWIPE_GESTURE_DIRECTION_HORIZONTAL); } 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); + workspace_scroll_begin(seat, SWIPE_GESTURE_DIRECTION_VERTICAL); } else { // ... otherwise forward to client struct sway_cursor *cursor = seat->cursor; @@ -1036,9 +1038,6 @@ static void handle_swipe_update(struct sway_seat *seat, event->dx, event->dy, NAN, NAN); } 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, - event->dx, event->dy, NAN, NAN); - // Find the gesture and update the swipe percentage struct gesture_tracker *tracker = &seatop->gestures; struct sway_input_device *device = @@ -1058,13 +1057,15 @@ static void handle_swipe_update(struct sway_seat *seat, if (binding) { int invert = binding->flags & BINDING_INVERTED ? 1 : -1; + tracker->dx += event->dx * invert; + tracker->dy += event->dy * invert; switch (binding->gesture.type) { case GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL: - update_workspace_scroll_percent(seat, tracker->dx, invert, + workspace_scroll_update(seat, tracker->dx, SWIPE_GESTURE_DIRECTION_HORIZONTAL); break; case GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL: - update_workspace_scroll_percent(seat, tracker->dy, invert, + workspace_scroll_update(seat, tracker->dy, SWIPE_GESTURE_DIRECTION_VERTICAL); break; default: @@ -1109,7 +1110,7 @@ static void handle_swipe_end(struct sway_seat *seat, switch (binding->gesture.type) { case GESTURE_TYPE_WORKSPACE_SWIPE_HORIZONTAL: case GESTURE_TYPE_WORKSPACE_SWIPE_VERTICAL: - snap_workspace_scroll_percent(seat); + workspace_scroll_end(seat); break; default: gesture_binding_execute(seat, binding);