From 82bfceccf681801c041251541a1f4b53c13fe187 Mon Sep 17 00:00:00 2001 From: Florian Didron Date: Tue, 9 Sep 2025 09:49:55 +0700 Subject: [PATCH 1/4] fix(automouse): make sure KC_NO do not fire layer 0 mappings on automouse layer (#419) --- quantum/pointing_device/pointing_device_auto_mouse.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/quantum/pointing_device/pointing_device_auto_mouse.c b/quantum/pointing_device/pointing_device_auto_mouse.c index 0f227d8435..41df31f976 100644 --- a/quantum/pointing_device/pointing_device_auto_mouse.c +++ b/quantum/pointing_device/pointing_device_auto_mouse.c @@ -343,7 +343,8 @@ bool process_auto_mouse(uint16_t keycode, keyrecord_t* record) { if (!(AUTO_MOUSE_ENABLED)) return true; switch (keycode) { - // Skip Mod keys and layer lock to avoid layer reset + // Skip Mod keys, KC_NO, and layer lock to avoid layer reset + case KC_NO: case KC_LEFT_CTRL ... KC_RIGHT_GUI: case QK_MODS ... QK_MODS_MAX: case QK_LLCK: From 1f95d83f68571b5dd398313cf502235d48fbb0ec Mon Sep 17 00:00:00 2001 From: Florian Didron Date: Tue, 9 Sep 2025 09:50:13 +0700 Subject: [PATCH 2/4] fix(automouse): prevent mouse keys to trigger auto mouse on other layers (#418) --- quantum/pointing_device/pointing_device_auto_mouse.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/quantum/pointing_device/pointing_device_auto_mouse.c b/quantum/pointing_device/pointing_device_auto_mouse.c index 41df31f976..2b8cfdbf68 100644 --- a/quantum/pointing_device/pointing_device_auto_mouse.c +++ b/quantum/pointing_device/pointing_device_auto_mouse.c @@ -443,7 +443,14 @@ bool process_auto_mouse(uint16_t keycode, keyrecord_t* record) { */ static bool is_mouse_record(uint16_t keycode, keyrecord_t* record) { // allow for keyboard to hook in and override if need be - if (is_mouse_record_kb(keycode, record) || IS_MOUSEKEY(keycode)) return true; + if (is_mouse_record_kb(keycode, record)) return true; + + // if it's a mouse key, only treat it as a mouse record if we're currently on the auto mouse target layer + // this prevents mouse keys from activating the auto mouse layer when pressed on other layers + if (IS_MOUSEKEY(keycode)) { + return layer_state_is((AUTO_MOUSE_TARGET_LAYER)); + } + return false; } From e87a3e6949719612b6444faddaed2463ed03f250 Mon Sep 17 00:00:00 2001 From: Florian Didron Date: Fri, 12 Sep 2025 16:15:34 +0700 Subject: [PATCH 3/4] Feat/smoother scrolling (#420) * 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 --- drivers/sensors/navigator.c | 96 ++++++++++++++++++++++++++++++++++--- drivers/sensors/navigator.h | 13 +++++ 2 files changed, 103 insertions(+), 6 deletions(-) diff --git a/drivers/sensors/navigator.c b/drivers/sensors/navigator.c index 90ca16709a..596d7ae4ad 100644 --- a/drivers/sensors/navigator.c +++ b/drivers/sensors/navigator.c @@ -1,6 +1,31 @@ // Copyright 2025 ZSA Technology Labs, Inc // 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 "navigator.h" @@ -25,17 +50,76 @@ report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { mouse_report.y /= NAVIGATOR_AIM_DIVIDER; } if (set_scrolling) { + // Accumulate scroll movement scroll_accumulated_h += (float)mouse_report.x / 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 - mouse_report.v = (int8_t)-scroll_accumulated_v; + + // This allows fractional accumulation to build up before triggering scroll + 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 - mouse_report.v = (int8_t)scroll_accumulated_v; + mouse_report.h = (int8_t)-scroll_h; #endif - scroll_accumulated_h -= (int8_t)scroll_accumulated_h; - scroll_accumulated_v -= (int8_t)scroll_accumulated_v; +#ifdef NAVIGATOR_SCROLL_INVERT_Y + 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.y = 0; diff --git a/drivers/sensors/navigator.h b/drivers/sensors/navigator.h index 73e809715b..dc0fbe0214 100644 --- a/drivers/sensors/navigator.h +++ b/drivers/sensors/navigator.h @@ -6,6 +6,19 @@ #define NAVIGATOR_SCROLL_DIVIDER 10 #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 #define NAVIGATOR_TURBO_MULTIPLIER 3 #define NAVIGATOR_AIM_DIVIDER 3 From 634e375f117674e3f3df7ef70f19618b793a4d52 Mon Sep 17 00:00:00 2001 From: Florian Didron Date: Mon, 29 Sep 2025 12:57:53 +0700 Subject: [PATCH 4/4] Feat/oneshot automouse (#421) * feat(automouse): one shot automouse * fix(automouse): one shot automouse status get reset when mouse is active * fix(automouse): remove stray defines --- .../pointing_device_auto_mouse.c | 23 +++++++++++++++++-- .../pointing_device_auto_mouse.h | 3 +++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/quantum/pointing_device/pointing_device_auto_mouse.c b/quantum/pointing_device/pointing_device_auto_mouse.c index 2b8cfdbf68..3cfb0ed464 100644 --- a/quantum/pointing_device/pointing_device_auto_mouse.c +++ b/quantum/pointing_device/pointing_device_auto_mouse.c @@ -32,6 +32,9 @@ static auto_mouse_context_t auto_mouse_context = { .config.layer = (uint8_t)(AUTO_MOUSE_DEFAULT_LAYER), .config.timeout = (uint16_t)(AUTO_MOUSE_TIME), .config.debounce = (uint8_t)(AUTO_MOUSE_DEBOUNCE), +#ifdef AUTO_MOUSE_ONESHOT + .one_shot = false, +#endif }; /* local functions */ @@ -49,7 +52,11 @@ static inline bool layer_hold_check(void) { /* check all layer activation criteria */ bool is_auto_mouse_active(void) { +#ifdef AUTO_MOUSE_ONESHOT + return auto_mouse_context.status.is_activated || auto_mouse_context.status.mouse_key_tracker || layer_hold_check() || auto_mouse_context.one_shot; +#else return auto_mouse_context.status.is_activated || auto_mouse_context.status.mouse_key_tracker || layer_hold_check(); +#endif } /** @@ -267,6 +274,11 @@ void pointing_device_task_auto_mouse(report_mouse_t mouse_report) { if (!layer_state_is((AUTO_MOUSE_TARGET_LAYER))) { layer_on((AUTO_MOUSE_TARGET_LAYER)); } +#ifdef AUTO_MOUSE_ONESHOT + if (!auto_mouse_context.one_shot) { + auto_mouse_context.one_shot = true; + } +#endif } else if (layer_state_is((AUTO_MOUSE_TARGET_LAYER)) && timer_elapsed(auto_mouse_context.timer.active) > auto_mouse_context.config.timeout) { #ifdef LAYER_LOCK_ENABLE if(is_layer_locked(AUTO_MOUSE_DEFAULT_LAYER)) return; @@ -429,6 +441,13 @@ bool process_auto_mouse(uint16_t keycode, keyrecord_t* record) { auto_mouse_context.status.mouse_key_tracker = 0; dprintf("key tracker error (<0) \n"); } + +#ifdef AUTO_MOUSE_ONESHOT + if (is_auto_mouse_active()) { + auto_mouse_context.one_shot = false; + } +#endif + return true; } @@ -444,13 +463,13 @@ bool process_auto_mouse(uint16_t keycode, keyrecord_t* record) { static bool is_mouse_record(uint16_t keycode, keyrecord_t* record) { // allow for keyboard to hook in and override if need be if (is_mouse_record_kb(keycode, record)) return true; - + // if it's a mouse key, only treat it as a mouse record if we're currently on the auto mouse target layer // this prevents mouse keys from activating the auto mouse layer when pressed on other layers if (IS_MOUSEKEY(keycode)) { return layer_state_is((AUTO_MOUSE_TARGET_LAYER)); } - + return false; } diff --git a/quantum/pointing_device/pointing_device_auto_mouse.h b/quantum/pointing_device/pointing_device_auto_mouse.h index 7689234eb4..cab4310904 100644 --- a/quantum/pointing_device/pointing_device_auto_mouse.h +++ b/quantum/pointing_device/pointing_device_auto_mouse.h @@ -73,6 +73,9 @@ typedef struct { int8_t mouse_key_tracker; } status; total_mouse_movement_t total_mouse_movement; +#ifdef AUTO_MOUSE_ONESHOT + bool one_shot; +#endif } auto_mouse_context_t; /* ----------Set up and control------------------------------------------------------------------------------ */