diff --git a/include/swaybar/input.h b/include/swaybar/input.h index 65e492183..e8735d883 100644 --- a/include/swaybar/input.h +++ b/include/swaybar/input.h @@ -2,6 +2,7 @@ #define _SWAYBAR_INPUT_H #include +#include #include "list.h" #define SWAY_SCROLL_UP KEY_MAX + 1 @@ -9,6 +10,9 @@ #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; @@ -50,6 +54,12 @@ 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; @@ -57,6 +67,7 @@ struct swaybar_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; diff --git a/swaybar/bar.c b/swaybar/bar.c index efd0da4b9..f4dd4405d 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -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) { diff --git a/swaybar/input.c b/swaybar/input.c index f768cd406..4fe6dd934 100644 --- a/swaybar/input.c +++ b/swaybar/input.c @@ -211,18 +211,17 @@ static void workspace_next(struct swaybar *bar, struct swaybar_output *output, if (new) { ipc_send_workspace_command(bar, new->name); + + // 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 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; - } - +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); @@ -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, @@ -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; + if (!sway_assert(axis < 2, "axis out of range")) { + return; + } + + seat->axis[axis].discrete_steps += abs(discrete); } static struct wl_pointer_listener pointer_listener = {