From 070fb5150f48500242329697235adb8e2648d17d Mon Sep 17 00:00:00 2001 From: SimoneMariaRomeo <180769497+SimoneMariaRomeo@users.noreply.github.com> Date: Fri, 15 May 2026 20:59:14 +0700 Subject: [PATCH] Add classic acceleration exponent --- README.md | 5 +++++ README_NIXOS.md | 1 + crates/core/src/context.rs | 1 + crates/core/src/params.rs | 11 +++++++++++ crates/core/src/persist.rs | 2 ++ crates/core/src/sens_fns.rs | 1 + driver/accel/linear.h | 16 +++++++++++----- driver/accel_k.h | 1 + driver/params.h | 7 +++++++ driver/tests/accel.test.c | 26 +++++++++++++++++++++++--- module.nix | 9 +++++++++ 11 files changed, 72 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 493e3eb..9d0a5ba 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,11 @@ The more general function, which is relevant with a set input offset, is: $$(dx_f, dy_f) = (dx_0, dy_0) * (1 + a * (V - offset_in)^2 / V)$$ +The `classic-exponent` / `CLASSIC_EXPONENT` parameter controls that power. +Its default value is `2`, which preserves the existing curve. Values greater +than `2` create a more classic-style curve that stays lower near the offset and +ramps more aggressively at higher input speeds. + ## Other Curves - [x] **Natural** diff --git a/README_NIXOS.md b/README_NIXOS.md index 330c226..052a204 100644 --- a/README_NIXOS.md +++ b/README_NIXOS.md @@ -42,6 +42,7 @@ Create your `maccel.nix` module: acceleration = 0.3; offset = 2.0; outputCap = 2.0; + classicExponent = 2.0; # Natural mode decayRate = 0.1; diff --git a/crates/core/src/context.rs b/crates/core/src/context.rs index d3282af..7d0c04b 100644 --- a/crates/core/src/context.rs +++ b/crates/core/src/context.rs @@ -96,6 +96,7 @@ impl TuiContext { offset_linear: get!(OffsetLinear), offset_natural: get!(OffsetNatural), output_cap: get!(OutputCap), + classic_exponent: get!(ClassicExponent), decay_rate: get!(DecayRate), limit: get!(Limit), gamma: get!(Gamma), diff --git a/crates/core/src/params.rs b/crates/core/src/params.rs index 10215d3..aca79aa 100644 --- a/crates/core/src/params.rs +++ b/crates/core/src/params.rs @@ -180,6 +180,7 @@ declare_params!( Accel, OffsetLinear, OutputCap, + ClassicExponent, }, Natural { DecayRate, @@ -226,6 +227,7 @@ impl Param { Param::OffsetLinear => "OFFSET", Param::OffsetNatural => "OFFSET", Param::OutputCap => "OUTPUT_CAP", + Param::ClassicExponent => "CLASSIC_EXPONENT", Param::DecayRate => "DECAY_RATE", Param::Limit => "LIMIT", Param::Gamma => "GAMMA", @@ -244,6 +246,7 @@ impl Param { Param::OffsetLinear => "Offset", Param::OffsetNatural => "Offset", Param::OutputCap => "Output-Cap", + Param::ClassicExponent => "Classic Exponent", Param::YxRatio => "Y/x Ratio", Param::DecayRate => "Decay-Rate", Param::Limit => "Limit", @@ -268,6 +271,9 @@ impl Param { Param::Accel => "Acceleration strength. Higher values = faster cursor at high speeds.", Param::OffsetLinear => "Speed threshold (counts/ms) before acceleration begins.", Param::OutputCap => "Maximum sensitivity multiplier cap. Prevents excessive speed.", + Param::ClassicExponent => { + "Power exponent for the linear/classic curve. 2 preserves existing behavior." + } Param::DecayRate => "How quickly acceleration decays. Higher = faster decay.", Param::OffsetNatural => "Speed threshold (counts/ms) for natural curve activation.", Param::Limit => "Maximum gain limit for natural acceleration.", @@ -317,6 +323,11 @@ pub(crate) fn validate_param_value(param_tag: Param, value: f64) -> anyhow::Resu } Param::AngleRotation => {} Param::Accel => {} + Param::ClassicExponent => { + if value <= 0.0 { + anyhow::bail!("classic exponent must be positive"); + } + } Param::OutputCap => {} Param::OffsetLinear | Param::OffsetNatural => { if value < 0.0 { diff --git a/crates/core/src/persist.rs b/crates/core/src/persist.rs index 2173492..0614f17 100644 --- a/crates/core/src/persist.rs +++ b/crates/core/src/persist.rs @@ -93,11 +93,13 @@ impl SysFsStore { accel, offset_linear, output_cap, + classic_exponent, } = args; self.set(Param::Accel, accel)?; self.set(Param::OffsetLinear, offset_linear)?; self.set(Param::OutputCap, output_cap)?; + self.set(Param::ClassicExponent, classic_exponent)?; Ok(()) } diff --git a/crates/core/src/sens_fns.rs b/crates/core/src/sens_fns.rs index 76b0b82..814de4a 100644 --- a/crates/core/src/sens_fns.rs +++ b/crates/core/src/sens_fns.rs @@ -13,6 +13,7 @@ impl AllParamArgs { accel: self.accel, offset_linear: self.offset_linear, output_cap: self.output_cap, + classic_exponent: self.classic_exponent, }), AccelMode::Natural => AccelParamsByMode::Natural(NaturalCurveParams { decay_rate: self.decay_rate, diff --git a/driver/accel/linear.h b/driver/accel/linear.h index f69bc94..5341f46 100644 --- a/driver/accel/linear.h +++ b/driver/accel/linear.h @@ -9,14 +9,16 @@ struct linear_curve_args { fpt accel; fpt offset; fpt output_cap; + fpt classic_exponent; }; static inline fpt linear_base_fn(fpt x, fpt accel, - fpt input_offset) { + fpt input_offset, fpt classic_exponent) { fpt _x = x - input_offset; - fpt _x_square = fpt_mul( - _x, _x); // because linear in rawaccel is classic with exponent = 2 - return fpt_mul(accel, fpt_div(_x_square, x)); + fpt _x_power = + classic_exponent == FIXEDPT_TWO ? fpt_mul(_x, _x) + : fpt_pow(_x, classic_exponent); + return fpt_mul(accel, fpt_div(_x_power, x)); } /** @@ -27,12 +29,16 @@ static inline fpt __linear_sens_fun(fpt input_speed, dbg("linear: accel %s", fptoa(args.accel)); dbg("linear: offset %s", fptoa(args.offset)); dbg("linear: output_cap %s", fptoa(args.output_cap)); + dbg("linear: classic_exponent %s", fptoa(args.classic_exponent)); if (input_speed <= args.offset) { return FIXEDPT_ONE; } - fpt sens = linear_base_fn(input_speed, args.accel, args.offset); + fpt classic_exponent = + args.classic_exponent > 0 ? args.classic_exponent : FIXEDPT_TWO; + fpt sens = + linear_base_fn(input_speed, args.accel, args.offset, classic_exponent); dbg("linear: base_fn sens %s", fptoa(args.accel)); fpt sign = FIXEDPT_ONE; diff --git a/driver/accel_k.h b/driver/accel_k.h index 029b6c9..a7e4275 100644 --- a/driver/accel_k.h +++ b/driver/accel_k.h @@ -38,6 +38,7 @@ static struct accel_args collect_args(void) { accel.args.linear.accel = atofp(PARAM_ACCEL); accel.args.linear.offset = atofp(PARAM_OFFSET); accel.args.linear.output_cap = atofp(PARAM_OUTPUT_CAP); + accel.args.linear.classic_exponent = atofp(PARAM_CLASSIC_EXPONENT); break; } case no_accel: diff --git a/driver/params.h b/driver/params.h index 277a27d..0303363 100644 --- a/driver/params.h +++ b/driver/params.h @@ -40,6 +40,13 @@ PARAM(ANGLE_ROTATION, 0, PARAM(ACCEL, 0, "Control the sensitivity calculation."); PARAM(OFFSET, 0, "Input speed threshold (counts/ms) before acceleration begins."); PARAM(OUTPUT_CAP, 0, "Control the maximum sensitivity."); +#if FIXEDPT_BITS == 64 +PARAM(CLASSIC_EXPONENT, 8589934592, // 2 << 32 + "Exponent for the Linear curve's classic-style power calculation."); +#else +PARAM(CLASSIC_EXPONENT, 131072, // 2 << 16 + "Exponent for the Linear curve's classic-style power calculation."); +#endif // For Natural Mode diff --git a/driver/tests/accel.test.c b/driver/tests/accel.test.c index 861f656..90c725b 100644 --- a/driver/tests/accel.test.c +++ b/driver/tests/accel.test.c @@ -33,11 +33,14 @@ static int test_acceleration(const char *filename, struct accel_args args) { static int test_linear_acceleration(const char *filename, fpt param_sens_mult, fpt param_yx_ratio, fpt param_accel, - fpt param_offset, fpt param_output_cap) { + fpt param_offset, fpt param_output_cap, + fpt param_classic_exponent) { struct linear_curve_args _args = (struct linear_curve_args){.accel = param_accel, .offset = param_offset, - .output_cap = param_output_cap}; + .output_cap = param_output_cap, + .classic_exponent = + param_classic_exponent}; struct accel_args args = { .sens_mult = param_sens_mult, @@ -127,7 +130,7 @@ static int test_rotation_no_accel(const char *filename, fpt param_sens_mult, "SENS_MULT-" #sens_mult "-ACCEL-" #accel "-OFFSET" #offset \ "-OUTPUT_CAP-" #cap ".snapshot", \ fpt_rconst(sens_mult), fpt_rconst(yx_ratio), fpt_rconst(accel), \ - fpt_rconst(offset), fpt_rconst(cap)) == 0); + fpt_rconst(offset), fpt_rconst(cap), fpt_rconst(2)) == 0); #define test_natural(sens_mult, yx_ratio, decay_rate, offset, limit) \ assert(test_natural_acceleration( \ @@ -159,6 +162,22 @@ static int test_rotation_no_accel(const char *filename, fpt param_sens_mult, ".snapshot", \ fpt_rconst(sens_mult), fpt_rconst(angle_deg)) == 0); +static void test_classic_exponent_changes_linear_curve(void) { + struct linear_curve_args exponent_2 = + (struct linear_curve_args){.accel = fpt_rconst(0.01), + .offset = 0, + .output_cap = 0, + .classic_exponent = fpt_rconst(2)}; + struct linear_curve_args exponent_3 = + (struct linear_curve_args){.accel = fpt_rconst(0.01), + .offset = 0, + .output_cap = 0, + .classic_exponent = fpt_rconst(3)}; + + assert(__linear_sens_fun(fpt_rconst(20), exponent_3) > + __linear_sens_fun(fpt_rconst(20), exponent_2)); +} + int main(void) { test_linear(1, 1, 0, 0, 0); test_linear(1, 1, 0.3, 2, 2); @@ -166,6 +185,7 @@ int main(void) { test_linear(0.1875, 1, 0.05625, 10.6666666, 2); test_linear(0.0917, 1, 0.002048, 78.125, 2.0239); test_linear(0.07, 1.15, 0.055, 21, 3); + test_classic_exponent_changes_linear_curve(); test_natural(1, 1, 0, 0, 0); test_natural(1, 1, 0.1, 0, 0); diff --git a/module.nix b/module.nix index 1efbd29..d74af3c 100644 --- a/module.nix +++ b/module.nix @@ -41,6 +41,7 @@ with lib; let ACCEL = cfg.parameters.acceleration; OFFSET = cfg.parameters.offset; OUTPUT_CAP = cfg.parameters.outputCap; + CLASSIC_EXPONENT = cfg.parameters.classicExponent; # Natural mode parameters DECAY_RATE = cfg.parameters.decayRate; @@ -189,6 +190,14 @@ in { description = "Maximum sensitivity multiplier cap."; }; + classicExponent = mkOption { + type = + types.nullOr (types.addCheck types.float (x: x > 0.0) + // {description = "positive float";}); + default = null; + description = "Power exponent for the linear/classic acceleration curve. 2 preserves existing behavior."; + }; + # Natural mode parameters decayRate = mkOption { type =