smooth scroll speed estimation to reduce red-dot spikes
Tests / test_core_function (push) Failing after 8s

This commit is contained in:
2026-03-25 09:38:07 +00:00
parent 5f1254d11a
commit d359a2048f
+39 -18
View File
@@ -40,11 +40,29 @@ struct Args {
no_grab: bool,
}
#[derive(Debug, Default)]
struct AxisRuntime {
last_event_at: Option<Instant>,
remainder: f64,
smoothed_speed: f64,
}
#[derive(Debug, Default)]
struct ScrollState {
last_scroll_event_at: Option<Instant>,
remainder_v: f64,
remainder_h: f64,
vertical: AxisRuntime,
horizontal: AxisRuntime,
}
const SPEED_EMA_ALPHA: f64 = 0.25;
const MIN_DT_SECONDS: f64 = 0.004;
const SPEED_IDLE_RESET_SECONDS: f64 = 0.2;
fn axis_runtime_mut(state: &mut ScrollState, axis: RelativeAxisCode) -> &mut AxisRuntime {
if axis == RelativeAxisCode::REL_HWHEEL || axis == RelativeAxisCode::REL_HWHEEL_HI_RES {
&mut state.horizontal
} else {
&mut state.vertical
}
}
fn main() -> anyhow::Result<()> {
@@ -269,13 +287,14 @@ fn transform_event(
{
return None;
}
let axis_state = axis_runtime_mut(state, axis);
let now = Instant::now();
let dt = state
.last_scroll_event_at
let dt = axis_state
.last_event_at
.map(|t| now.saturating_duration_since(t).as_secs_f64())
.unwrap_or(0.016)
.max(0.001);
state.last_scroll_event_at = Some(now);
.max(MIN_DT_SECONDS);
axis_state.last_event_at = Some(now);
let axis_unit = if axis == RelativeAxisCode::REL_WHEEL_HI_RES
|| axis == RelativeAxisCode::REL_HWHEEL_HI_RES
@@ -288,7 +307,17 @@ fn transform_event(
// Normalize hi-res wheel units (120 per detent) to "detent-equivalent"
// speed so tuning behaves similarly across REL_WHEEL and REL_WHEEL_HI_RES.
let normalized_value = (value as f64) / axis_unit;
let speed = normalized_value.abs() / dt;
let raw_speed = normalized_value.abs() / dt;
// Smooth out bursty evdev timing so the live indicator and accel input
// don't jump wildly on single tightly-packed events.
let speed = if dt > SPEED_IDLE_RESET_SECONDS {
raw_speed
} else {
SPEED_EMA_ALPHA * raw_speed + (1.0 - SPEED_EMA_ALPHA) * axis_state.smoothed_speed
};
axis_state.smoothed_speed = speed;
let (gain_x, gain_y) = sensitivity(speed, *mode, params);
let gain = if axis == RelativeAxisCode::REL_HWHEEL
|| axis == RelativeAxisCode::REL_HWHEEL_HI_RES
@@ -298,17 +327,9 @@ fn transform_event(
gain_y
};
let remainder = if axis == RelativeAxisCode::REL_HWHEEL
|| axis == RelativeAxisCode::REL_HWHEEL_HI_RES
{
&mut state.remainder_h
} else {
&mut state.remainder_v
};
let scaled = (value as f64) * gain + *remainder;
let scaled = (value as f64) * gain + axis_state.remainder;
let quantized = scaled.trunc() as i32;
*remainder = scaled - (quantized as f64);
axis_state.remainder = scaled - (quantized as f64);
let _ = write_speed_stat(speed);