Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**
Expand Down
1 change: 1 addition & 0 deletions README_NIXOS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ impl<PS: ParamStore> TuiContext<PS> {
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),
Expand Down
11 changes: 11 additions & 0 deletions crates/core/src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ declare_params!(
Accel,
OffsetLinear,
OutputCap,
ClassicExponent,
},
Natural {
DecayRate,
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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.",
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions crates/core/src/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/sens_fns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
16 changes: 11 additions & 5 deletions driver/accel/linear.h
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

/**
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions driver/accel_k.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
7 changes: 7 additions & 0 deletions driver/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
26 changes: 23 additions & 3 deletions driver/tests/accel.test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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( \
Expand Down Expand Up @@ -159,13 +162,30 @@ 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);
test_linear(0.1325, 1, 0.3, 21.333333, 2);
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);
Expand Down
9 changes: 9 additions & 0 deletions module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 =
Expand Down