Feat/smoother scrolling (#420)
Some checks failed
Build firmware / build-firmware (default) (push) Has been cancelled
Build firmware / build-firmware (oryx) (push) Has been cancelled
Unit Tests / test (push) Has been cancelled

* feat(navigator): a smoother scrolling experience at slower speed

* doc(navigator): Fix scrolling docs, tweak defaults

* chore(navigator): remove unused include

* fix(trackball) inverted default horizontal scrolling
This commit is contained in:
Florian Didron
2025-09-12 16:15:34 +07:00
committed by GitHub
parent 1f95d83f68
commit e87a3e6949
2 changed files with 103 additions and 6 deletions

View File

@@ -1,6 +1,31 @@
// Copyright 2025 ZSA Technology Labs, Inc <contact@zsa.io> // Copyright 2025 ZSA Technology Labs, Inc <contact@zsa.io>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/*
* Navigator Trackball Smooth Scrolling
*
* Enhanced scrolling algorithm that eliminates deadzones and provides natural,
* responsive scrolling for both slow and fast movements.
*
* Key Features:
* - No initial deadzone - scrolling starts immediately with any movement
* - Smooth acceleration - speed increases naturally with faster movement
* - Fractional accumulation - sub-pixel movements accumulate until triggering scroll
* - Reduced jitter - consistent consumption prevents oscillation
*
* Configuration Parameters (add to keymap config.h):
* - NAVIGATOR_SCROLL_DIVIDER: Lower = more sensitive (default: 10)
* - NAVIGATOR_SCROLL_THRESHOLD: Minimum to scroll (default: 0f)
* - NAVIGATOR_SCROLL_ACCELERATION: Speed multiplier (default: 1.5f)
* - NAVIGATOR_SCROLL_MAX_SPEED: Maximum speed limit (default: 8.0f)
*
* Algorithm:
* 1. Accumulate input as floating-point values
* 2. When accumulated >= 1.0, trigger scrolling with acceleration
* 3. Subtract exactly 1.0 from accumulation regardless of output
* 4. Gentle decay (2% per frame) only after 20 frames of inactivity
*/
#include "quantum.h" #include "quantum.h"
#include "navigator.h" #include "navigator.h"
@@ -25,17 +50,76 @@ report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) {
mouse_report.y /= NAVIGATOR_AIM_DIVIDER; mouse_report.y /= NAVIGATOR_AIM_DIVIDER;
} }
if (set_scrolling) { if (set_scrolling) {
// Accumulate scroll movement
scroll_accumulated_h += (float)mouse_report.x / NAVIGATOR_SCROLL_DIVIDER; scroll_accumulated_h += (float)mouse_report.x / NAVIGATOR_SCROLL_DIVIDER;
scroll_accumulated_v += (float)mouse_report.y / NAVIGATOR_SCROLL_DIVIDER; scroll_accumulated_v += (float)mouse_report.y / NAVIGATOR_SCROLL_DIVIDER;
mouse_report.h = (int8_t)scroll_accumulated_h;
#ifdef NAVIGATOR_SCROLL_INVERT // This allows fractional accumulation to build up before triggering scroll
mouse_report.v = (int8_t)-scroll_accumulated_v; float abs_h = (scroll_accumulated_h < 0) ? -scroll_accumulated_h : scroll_accumulated_h;
float abs_v = (scroll_accumulated_v < 0) ? -scroll_accumulated_v : scroll_accumulated_v;
float scroll_h = 0.0f;
float scroll_v = 0.0f;
if (abs_h >= 1.0f) {
// Simple acceleration for faster movements
float speed_h = 1.0f + ((abs_h - 1.0f) * NAVIGATOR_SCROLL_ACCELERATION);
if (speed_h > NAVIGATOR_SCROLL_MAX_SPEED) {
speed_h = NAVIGATOR_SCROLL_MAX_SPEED;
}
scroll_h = (scroll_accumulated_h > 0) ? speed_h : -speed_h;
}
if (abs_v >= 1.0f) {
float speed_v = 1.0f + ((abs_v - 1.0f) * NAVIGATOR_SCROLL_ACCELERATION);
if (speed_v > NAVIGATOR_SCROLL_MAX_SPEED) {
speed_v = NAVIGATOR_SCROLL_MAX_SPEED;
}
scroll_v = (scroll_accumulated_v > 0) ? speed_v : -speed_v;
}
#ifdef NAVIGATOR_SCROLL_INVERT_X
mouse_report.h = (int8_t)scroll_h;
#else #else
mouse_report.v = (int8_t)scroll_accumulated_v; mouse_report.h = (int8_t)-scroll_h;
#endif #endif
scroll_accumulated_h -= (int8_t)scroll_accumulated_h; #ifdef NAVIGATOR_SCROLL_INVERT_Y
scroll_accumulated_v -= (int8_t)scroll_accumulated_v; mouse_report.v = (int8_t)-scroll_v;
#else
mouse_report.v = (int8_t)scroll_v;
#endif
// Subtract proportional to the base scroll (before acceleration) to prevent jitter
if (abs_h >= 1.0f) {
scroll_accumulated_h -= (scroll_accumulated_h > 0) ? 1.0f : -1.0f;
}
if (abs_v >= 1.0f) {
scroll_accumulated_v -= (scroll_accumulated_v > 0) ? 1.0f : -1.0f;
}
// Much gentler decay and only after longer idle periods
static uint8_t idle_counter_h = 0, idle_counter_v = 0;
if (mouse_report.x == 0 && mouse_report.h == 0) {
idle_counter_h++;
if (idle_counter_h > 20) { // Only decay after 20 frames of no input
scroll_accumulated_h *= 0.98f; // Very gentle decay
}
} else {
idle_counter_h = 0;
}
if (mouse_report.y == 0 && mouse_report.v == 0) {
idle_counter_v++;
if (idle_counter_v > 20) {
scroll_accumulated_v *= 0.98f;
}
} else {
idle_counter_v = 0;
}
mouse_report.x = 0; mouse_report.x = 0;
mouse_report.y = 0; mouse_report.y = 0;

View File

@@ -6,6 +6,19 @@
#define NAVIGATOR_SCROLL_DIVIDER 10 #define NAVIGATOR_SCROLL_DIVIDER 10
#endif #endif
#ifndef NAVIGATOR_SCROLL_THRESHOLD
#define NAVIGATOR_SCROLL_THRESHOLD 0f
#endif
#ifndef NAVIGATOR_SCROLL_ACCELERATION
#define NAVIGATOR_SCROLL_ACCELERATION 1.5f
#endif
#ifndef NAVIGATOR_SCROLL_MAX_SPEED
#define NAVIGATOR_SCROLL_MAX_SPEED 8.0f
#endif
#ifdef POINTING_DEVICE_DRIVER_navigator_trackball #ifdef POINTING_DEVICE_DRIVER_navigator_trackball
#define NAVIGATOR_TURBO_MULTIPLIER 3 #define NAVIGATOR_TURBO_MULTIPLIER 3
#define NAVIGATOR_AIM_DIVIDER 3 #define NAVIGATOR_AIM_DIVIDER 3