mirror of
https://github.com/zsa/qmk_firmware.git
synced 2026-02-11 06:40:15 +00:00
Merge branch 'firmware25' into feat/trackpad
This commit is contained in:
@@ -1,6 +1,31 @@
|
||||
// Copyright 2025 ZSA Technology Labs, Inc <contact@zsa.io>
|
||||
// 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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -343,7 +355,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:
|
||||
@@ -428,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;
|
||||
}
|
||||
|
||||
@@ -442,7 +462,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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------------------------------------------------------------------------------ */
|
||||
|
||||
Reference in New Issue
Block a user