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

swaybar: Fix scrolling to switch workspace with precise trackpads #5067

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions include/swaybar/input.h
Expand Up @@ -2,13 +2,17 @@
#define _SWAYBAR_INPUT_H

#include <wayland-client.h>
#include <stdbool.h>
#include "list.h"

#define SWAY_SCROLL_UP KEY_MAX + 1
#define SWAY_SCROLL_DOWN KEY_MAX + 2
#define SWAY_SCROLL_LEFT KEY_MAX + 3
#define SWAY_SCROLL_RIGHT KEY_MAX + 4

#define SWAY_CONTINUOUS_SCROLL_TIMEOUT 1000
#define SWAY_CONTINUOUS_SCROLL_THRESHOLD 10000

struct swaybar;
struct swaybar_output;

Expand Down Expand Up @@ -50,13 +54,20 @@ struct swaybar_hotspot {
void *data;
};

struct swaybar_scroll_axis {
wl_fixed_t value;
uint32_t discrete_steps;
uint32_t update_time;
};

struct swaybar_seat {
struct swaybar *bar;
uint32_t wl_name;
struct wl_seat *wl_seat;
struct swaybar_pointer pointer;
struct swaybar_touch touch;
struct wl_list link; // swaybar_seat:link
struct swaybar_scroll_axis axis[2];
};

extern const struct wl_seat_listener seat_listener;
Expand Down
2 changes: 1 addition & 1 deletion swaybar/bar.c
Expand Up @@ -340,7 +340,7 @@ static void handle_global(void *data, struct wl_registry *registry,
}
seat->bar = bar;
seat->wl_name = name;
seat->wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, 3);
seat->wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, 5);
wl_seat_add_listener(seat->wl_seat, &seat_listener, seat);
wl_list_insert(&bar->seats, &seat->link);
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
Expand Down
90 changes: 79 additions & 11 deletions swaybar/input.c
Expand Up @@ -211,18 +211,17 @@ static void workspace_next(struct swaybar *bar, struct swaybar_output *output,

if (new) {
ipc_send_workspace_command(bar, new->name);
}
}

static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis, wl_fixed_t value) {
struct swaybar_seat *seat = data;
struct swaybar_pointer *pointer = &seat->pointer;
struct swaybar_output *output = pointer->current;
if (!sway_assert(output, "axis with no active output")) {
return;
// Since we're asking Sway to switch to 'new', it should become visible.
// Marking it visible right now allows calling workspace_next in a loop.
new->visible = true;
active->visible = false;
}
}

static void process_discrete_scroll(struct swaybar_seat *seat,
struct swaybar_output *output, struct swaybar_pointer *pointer,
uint32_t axis, wl_fixed_t value) {
// If there is a button press binding, execute it, skip default behavior,
// and check button release bindings
uint32_t button = wl_axis_to_button(axis, value);
Expand Down Expand Up @@ -252,8 +251,72 @@ static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED);
}

static void process_continuous_scroll(struct swaybar_seat *seat,
struct swaybar_output *output, struct swaybar_pointer *pointer,
uint32_t axis) {
while (abs(seat->axis[axis].value) > SWAY_CONTINUOUS_SCROLL_THRESHOLD) {
if (seat->axis[axis].value > 0) {
process_discrete_scroll(seat, output, pointer, axis,
SWAY_CONTINUOUS_SCROLL_THRESHOLD);
seat->axis[axis].value -= SWAY_CONTINUOUS_SCROLL_THRESHOLD;
} else {
process_discrete_scroll(seat, output, pointer, axis,
-SWAY_CONTINUOUS_SCROLL_THRESHOLD);
seat->axis[axis].value += SWAY_CONTINUOUS_SCROLL_THRESHOLD;
}
}
}

static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis, wl_fixed_t value) {
struct swaybar_seat *seat = data;
struct swaybar_pointer *pointer = &seat->pointer;
struct swaybar_output *output = pointer->current;
if (!sway_assert(output, "axis with no active output")) {
return;
}

if (!sway_assert(axis < 2, "axis out of range")) {
return;
}

// If there's a while since the last scroll event,
// set 'value' to zero as if to reset the "virtual scroll wheel"
if (seat->axis[axis].discrete_steps == 0 &&
time - seat->axis[axis].update_time > SWAY_CONTINUOUS_SCROLL_TIMEOUT) {
seat->axis[axis].value = 0;
}

seat->axis[axis].value += value;
seat->axis[axis].update_time = time;
}

static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) {
// Who cares
struct swaybar_seat *seat = data;
struct swaybar_pointer *pointer = &seat->pointer;
struct swaybar_output *output = pointer->current;

if (output == NULL) {
return;
}

for (uint32_t axis = 0; axis < 2; ++axis) {
if (seat->axis[axis].discrete_steps) {
for (uint32_t step = 0; step < seat->axis[axis].discrete_steps; ++step) {
// Honestly, it would probabyl make sense to pass in
// 'seat->axis[axis].value / seat->axis[axi].discrete_steps' here,
// but it's only used to check whether it's positive or negative
// so I don't think it's worth the risk of rounding errors.
process_discrete_scroll(seat, output, pointer, axis,
seat->axis[axis].value);
}

seat->axis[axis].value = 0;
seat->axis[axis].discrete_steps = 0;
} else {
process_continuous_scroll(seat, output, pointer, axis);
}
}
}

static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer,
Expand All @@ -268,7 +331,12 @@ static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,

static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
uint32_t axis, int32_t discrete) {
// Who cares
struct swaybar_seat *seat = data;
emersion marked this conversation as resolved.
Show resolved Hide resolved
if (!sway_assert(axis < 2, "axis out of range")) {
return;
}

seat->axis[axis].discrete_steps += abs(discrete);
}

static struct wl_pointer_listener pointer_listener = {
Expand Down