From 2b753a98b81d68f4aa8889b4190c558d72e1f7ec Mon Sep 17 00:00:00 2001 From: bi4k8 Date: Wed, 9 Nov 2022 05:18:14 +0000 Subject: [PATCH] Support smooth scroll and horizontal scroll note that this changes Scroll mousebinds from taking a "button" attribute to taking a "direction" attribute --- docs/labwc-config.5.scd | 11 +++++-- docs/rc.xml.all | 4 +-- include/config/mousebind.h | 12 ++++++++ include/labwc.h | 3 ++ src/config/mousebind.c | 36 ++++++++++++++++++++--- src/config/rcxml.c | 13 +++++++-- src/cursor.c | 60 ++++++++++++++++++++++++++++++++------ 7 files changed, 119 insertions(+), 20 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index f85ac8a6..7103be35 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -187,7 +187,7 @@ The rest of this man page describes configuration options. ** Set double click time in milliseconds. Default is 500. -** +** Multiple ** can exist within one **; and multiple ** can exist within one ** @@ -219,8 +219,12 @@ The rest of this man page describes configuration options. - Left - Middle - Right - - Up (scroll up) - - Down (scroll down) + + Supported scroll directions are: + - Up + - Down + - Left + - Right Supported mouse actions include: - Press: Pressing the specified button down in the context. @@ -229,6 +233,7 @@ The rest of this man page describes configuration options. - DoubleClick: Two presses within the doubleClickTime. - Drag: Pressing the button within the context, then moving the cursor - Scroll: Scrolling up or down in the context. + Scroll actions must have a *direction* specified instead of *button*. ** Load default mousebinds. This is an addition to the openbox diff --git a/docs/rc.xml.all b/docs/rc.xml.all index f7c1a24b..14e46d27 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -301,10 +301,10 @@ root-menu - + - + diff --git a/include/config/mousebind.h b/include/config/mousebind.h index ca8d35f8..f7dec7de 100644 --- a/include/config/mousebind.h +++ b/include/config/mousebind.h @@ -16,12 +16,23 @@ enum mouse_event { MOUSE_ACTION_SCROLL, }; +enum direction { + LAB_DIRECTION_INVALID = 0, + LAB_DIRECTION_LEFT, + LAB_DIRECTION_RIGHT, + LAB_DIRECTION_UP, + LAB_DIRECTION_DOWN, +}; + struct mousebind { enum ssd_part_type context; /* ex: BTN_LEFT, BTN_RIGHT from linux/input_event_codes.h */ uint32_t button; + /* scroll direction; considered instead of button for scroll events */ + enum direction direction; + /* ex: WLR_MODIFIER_SHIFT | WLR_MODIFIER_LOGO */ uint32_t modifiers; @@ -35,6 +46,7 @@ struct mousebind { enum mouse_event mousebind_event_from_str(const char *str); uint32_t mousebind_button_from_str(const char *str, uint32_t *modifiers); +enum direction mousebind_direction_from_str(const char *str, uint32_t *modifiers); struct mousebind *mousebind_create(const char *context); #endif /* __LABWC_MOUSEBIND_H */ diff --git a/include/labwc.h b/include/labwc.h index 0325bcf4..c16cc036 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -107,6 +107,9 @@ struct seat { enum lab_cursors server_cursor; struct wlr_cursor *cursor; struct wlr_xcursor_manager *xcursor_manager; + struct { + double x, y; + } smooth_scroll_offset; struct wlr_pointer_constraint_v1 *current_constraint; struct wlr_idle *wlr_idle; diff --git a/src/config/mousebind.c b/src/config/mousebind.c index 52640a97..4c37ad5f 100644 --- a/src/config/mousebind.c +++ b/src/config/mousebind.c @@ -34,16 +34,44 @@ mousebind_button_from_str(const char *str, uint32_t *modifiers) return BTN_RIGHT; } else if (!strcasecmp(str, "Middle")) { return BTN_MIDDLE; - } else if (!strcasecmp(str, "Up")) { - return BTN_GEAR_UP; - } else if (!strcasecmp(str, "Down")) { - return BTN_GEAR_DOWN; } invalid: wlr_log(WLR_ERROR, "unknown button (%s)", str); return UINT32_MAX; } +enum direction +mousebind_direction_from_str(const char *str, uint32_t *modifiers) +{ + assert(str); + + if (modifiers) { + *modifiers = 0; + while (strlen(str) >= 2 && str[1] == '-') { + char modname[2] = {str[0], 0}; + uint32_t parsed_modifier = parse_modifier(modname); + if (!parsed_modifier) { + goto invalid; + } + *modifiers |= parsed_modifier; + str += 2; + } + } + + if (!strcasecmp(str, "Left")) { + return LAB_DIRECTION_LEFT; + } else if (!strcasecmp(str, "Right")) { + return LAB_DIRECTION_RIGHT; + } else if (!strcasecmp(str, "Up")) { + return LAB_DIRECTION_UP; + } else if (!strcasecmp(str, "Down")) { + return LAB_DIRECTION_DOWN; + } +invalid: + wlr_log(WLR_ERROR, "unknown direction (%s)", str); + return LAB_DIRECTION_INVALID; +} + enum mouse_event mousebind_event_from_str(const char *str) { diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 01d35e4f..e5db0f6a 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -131,6 +131,9 @@ fill_mousebind(char *nodename, char *content) } else if (!strcmp(nodename, "button")) { current_mousebind->button = mousebind_button_from_str(content, ¤t_mousebind->modifiers); + } else if (!strcmp(nodename, "direction")) { + current_mousebind->direction = mousebind_direction_from_str(content, + ¤t_mousebind->modifiers); } else if (!strcmp(nodename, "action")) { /* */ current_mousebind->mouse_event = @@ -621,9 +624,14 @@ load_default_mouse_bindings(void) || strcmp(current->event, mouse_combos[i - 1].event)) { /* Create new mousebind */ m = mousebind_create(current->context); - m->button = mousebind_button_from_str(current->button, - &m->modifiers); m->mouse_event = mousebind_event_from_str(current->event); + if (m->mouse_event == MOUSE_ACTION_SCROLL) { + m->direction = mousebind_direction_from_str(current->button, + &m->modifiers); + } else { + m->button = mousebind_button_from_str(current->button, + &m->modifiers); + } count++; } @@ -649,6 +657,7 @@ merge_mouse_bindings(void) } if (existing->context == current->context && existing->button == current->button + && existing->direction == current->direction && existing->mouse_event == current->mouse_event) { wl_list_remove(&existing->link); action_list_free(&existing->actions); diff --git a/src/cursor.c b/src/cursor.c index fe4aae50..e5c90633 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -889,6 +889,38 @@ cursor_button(struct wl_listener *listener, void *data) } } +static int +compare_delta(const struct wlr_pointer_axis_event *event, double *accum) +{ + /* + * Smooth scroll deltas are in surface space, so treating each unit as a + * scroll event would result in too-fast scrolling. + * + * This fudge factor (inherited from various historic projects, incl. Weston) + * produces events at a more reasonable rate. + * + * For historic context, see: + * https://lists.freedesktop.org/archives/wayland-devel/2019-April/040377.html + */ + const double SCROLL_THRESHOLD = 10.0; + + if (event->delta == 0.0) { + /* Delta 0 marks the end of a scroll */ + *accum = 0.0; + } else { + /* Accumulate smooth scrolling until we hit threshold */ + *accum += event->delta; + } + if (event->delta_discrete < 0 || *accum < -SCROLL_THRESHOLD) { + *accum = fmod(*accum, SCROLL_THRESHOLD); + return -1; + } else if (event->delta_discrete > 0 || *accum > SCROLL_THRESHOLD) { + *accum = fmod(*accum, SCROLL_THRESHOLD); + return 1; + } + return 0; +} + bool handle_cursor_axis(struct server *server, struct cursor_context *ctx, struct wlr_pointer_axis_event *event) @@ -899,22 +931,32 @@ handle_cursor_axis(struct server *server, struct cursor_context *ctx, uint32_t modifiers = wlr_keyboard_get_modifiers( &server->seat.keyboard_group->keyboard); - uint32_t button = 0; - if (event->delta_discrete < 0) { - button = BTN_GEAR_UP; - } else if (event->delta_discrete > 0) { - button = BTN_GEAR_DOWN; + enum direction direction = LAB_DIRECTION_INVALID; + if (event->orientation == WLR_AXIS_ORIENTATION_HORIZONTAL) { + int rel = compare_delta(event, &server->seat.smooth_scroll_offset.x); + if (rel < 0) { + direction = LAB_DIRECTION_LEFT; + } else if (rel > 0) { + direction = LAB_DIRECTION_RIGHT; + } + } else if (event->orientation == WLR_AXIS_ORIENTATION_VERTICAL) { + int rel = compare_delta(event, &server->seat.smooth_scroll_offset.y); + if (rel < 0) { + direction = LAB_DIRECTION_UP; + } else if (rel > 0) { + direction = LAB_DIRECTION_DOWN; + } + } else { + wlr_log(WLR_DEBUG, "Failed to handle cursor axis event"); } - if (!button || event->source != WLR_AXIS_SOURCE_WHEEL - || event->orientation != WLR_AXIS_ORIENTATION_VERTICAL) { - wlr_log(WLR_DEBUG, "Failed to handle cursor axis event"); + if (direction == LAB_DIRECTION_INVALID) { return false; } wl_list_for_each(mousebind, &rc.mousebinds, link) { if (ssd_part_contains(mousebind->context, ctx->type) - && mousebind->button == button + && mousebind->direction == direction && modifiers == mousebind->modifiers && mousebind->mouse_event == MOUSE_ACTION_SCROLL) { handled = true; -- 2.52.0