import upstream maccel baseline
Tests / test_core_function (push) Failing after 12s

This commit is contained in:
2026-03-24 12:10:31 +00:00
parent 6e948d7b39
commit 5f1254d11a
108 changed files with 18930 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/target
+36
View File
@@ -0,0 +1,36 @@
use std::{
env::{self, consts::ARCH},
path::PathBuf,
};
fn main() {
let out = PathBuf::from(
env::var("OUT_DIR").expect("Expected OUT_DIR to be defined in the environment"),
);
let fixedpt_bits = match ARCH {
"x86" => "32",
#[cfg(feature = "long_bit_32")]
"x86_64" => "32",
#[cfg(not(feature = "long_bit_32"))]
"x86_64" => "64",
a => panic!("unsupported/untested architecture: {a}"),
};
let mut compiler = cc::Build::new();
compiler
.file("src/libmaccel.c")
.define("FIXEDPT_BITS", fixedpt_bits);
if cfg!(feature = "dbg") {
compiler.define("DEBUG", "1");
compiler.debug(true);
}
compiler.compile("maccel");
println!("cargo:rust-link-search=static={}", out.display());
const DRIVER_DIR: &str = "../../driver";
println!("cargo:rerun-if-changed={DRIVER_DIR}");
println!("cargo:rerun-if-changed=src/libmaccel.c");
}
+11
View File
@@ -0,0 +1,11 @@
mod context;
pub mod inputspeed;
mod libmaccel;
mod params;
pub mod persist;
mod sens_fns;
pub use context::*;
pub use libmaccel::fixedptc;
pub use params::*;
pub use sens_fns::*;
+14
View File
@@ -0,0 +1,14 @@
#include "../../../driver/accel_rs.h"
#include "../../../driver/fixedptc.h"
char *fpt_to_str(fpt num);
fpt str_to_fpt(char *string);
double fpt_to_float(fpt value);
fpt fpt_from_float(double value);
extern char *fpt_to_str(fpt num) { return fptoa(num); }
extern fpt str_to_fpt(char *string) { return atofp(string); }
extern double fpt_to_float(fpt value) { return fpt_todouble(value); }
extern fpt fpt_from_float(double value) { return fpt_rconst(value); }
+91
View File
@@ -0,0 +1,91 @@
pub mod fixedptc {
use std::{
ffi::{CStr, CString},
str::FromStr,
};
use anyhow::Context;
use super::c_libmaccel::{self, str_to_fpt};
#[derive(Debug, Default, Clone, Copy, PartialEq)]
#[repr(transparent)]
pub struct Fpt(pub i64);
impl From<Fpt> for f64 {
fn from(value: Fpt) -> Self {
unsafe { c_libmaccel::fpt_to_float(value) }
}
}
impl From<f64> for Fpt {
fn from(value: f64) -> Self {
unsafe { c_libmaccel::fpt_from_float(value) }
}
}
#[cfg(test)]
#[test]
fn fpt_and_float_conversion_to_and_fro() {
macro_rules! assert_for {
($value:literal) => {{
let fp = Fpt::from($value);
assert_eq!(f64::from(fp), $value);
}};
}
assert_for!(1.5);
assert_for!(4.5);
assert_for!(1.0);
assert_for!(0.0);
assert_for!(0.125);
assert_for!(0.5);
}
impl<'a> TryFrom<&'a Fpt> for &'a str {
type Error = anyhow::Error;
fn try_from(value: &'a Fpt) -> Result<Self, Self::Error> {
unsafe {
let s = CStr::from_ptr(c_libmaccel::fpt_to_str(*value));
let s = core::str::from_utf8(s.to_bytes())?;
Ok(s)
}
}
}
impl FromStr for Fpt {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let cstr = CString::new(s).context("Failed to convert to a C string")?;
let f = unsafe { str_to_fpt(cstr.as_ptr()) };
Ok(f)
}
}
}
#[repr(C)]
pub struct Vector {
pub x: i64,
pub y: i64,
}
mod c_libmaccel {
use super::{Vector, fixedptc};
use crate::params::AccelParams;
use std::ffi::c_char;
unsafe extern "C" {
pub fn sensitivity_rs(speed_in: fixedptc::Fpt, args: AccelParams) -> Vector;
}
unsafe extern "C" {
pub fn fpt_to_str(num: fixedptc::Fpt) -> *const c_char;
pub fn str_to_fpt(string: *const c_char) -> fixedptc::Fpt;
pub fn fpt_from_float(value: f64) -> fixedptc::Fpt;
pub fn fpt_to_float(value: fixedptc::Fpt) -> f64;
}
}
pub use c_libmaccel::sensitivity_rs;
+358
View File
@@ -0,0 +1,358 @@
use crate::libmaccel::fixedptc::Fpt;
use paste::paste;
/// Declare an enum for every parameter.
macro_rules! declare_common_params {
($($param:tt,)+) => {
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Param {
$($param),+
}
paste!(
#[derive(Debug)]
pub struct AllParamArgs {
$( pub [< $param:snake:lower >]: Fpt ),+
}
);
pub const ALL_PARAMS: &[Param] = &[ $(Param::$param),+ ];
};
}
// Helper macro to create FFI-safe curve parameter structs
macro_rules! make_curve_params_struct {
// Case: has parameters
($mode:tt, $($param:tt),+) => {
paste! {
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct [< $mode CurveParams >] {
$( pub [< $param:snake:lower >]: Fpt ),+
}
}
};
// Case: no parameters, add a ZST field for FFI safety
($mode:tt, ) => {
paste! {
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct [< $mode CurveParams >] {
pub _ffi_guard: [u8; 0],
}
}
};
}
macro_rules! declare_params {
( Common { $($common_param:tt),+$(,)? } , $( $mode:tt { $($param:tt),*$(,)? }, )+) => {
declare_common_params! {
$( $common_param, )+
$( $( $param, )* )+
}
/// Array of all the common parameters for convenience.
pub const ALL_COMMON_PARAMS: &[Param] = &[ $( Param::$common_param),+ ];
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[derive(Debug, Default, PartialEq, Clone, Copy)]
#[repr(u8)]
pub enum AccelMode {
#[default]
$( $mode, )+
}
pub const ALL_MODES: &[AccelMode] = &[ $( AccelMode::$mode, )+ ];
paste! {
/// Define the complete shape (and memory layout) of the argument
/// of the sensitivity function as it is expected to be in `C`
#[repr(C)]
pub struct AccelParams {
$( pub [< $common_param:snake:lower >] : Fpt, )+
pub by_mode: AccelParamsByMode,
}
/// Represents the tagged union of curve-specific parameters.
#[repr(C, u8)]
pub enum AccelParamsByMode {
$(
$mode([< $mode CurveParams >] ),
)+
}
/// Represents the common parameters and their float values.
/// Use it to bulk set the common parameters.
#[cfg_attr(feature = "clap", derive(clap::Args))]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CommonParamArgs {
$( pub [< $common_param:snake:lower >]: f64 ),+
}
}
paste! {
$(
// Use the helper macro to define the struct
make_curve_params_struct!($mode, $($param),*);
#[doc = "Array of all parameters for the `" $mode "` mode for convenience." ]
pub const [< ALL_ $mode:upper _PARAMS >]: &[Param] = &[ $( Param::$param),* ];
#[doc = "Represents the parameters for `" $mode "` curve and their float values"]
/// Use it to bulk set the curve's parameters.
#[cfg_attr(feature = "clap", derive(clap::Args))]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct [< $mode ParamArgs >] {
$( pub [< $param:snake:lower >]: f64 ),*
}
)+
/// Subcommands for the CLI
#[cfg(feature = "clap")]
pub mod subcommads {
#[derive(clap::Subcommand)]
pub enum SetParamByModesSubcommands {
/// Set all the common parameters
Common(super::CommonParamArgs),
$(
#[doc = "Set all the parameters for the " $mode " curve" ]
$mode(super::[< $mode ParamArgs >]),
)+
}
#[derive(clap::Subcommand)]
pub enum CliSubcommandSetParams {
/// Set the value for a single parameter
Param { name: crate::params::Param, value: f64 },
/// Set the acceleration mode (curve)
Mode { mode: crate::params::AccelMode },
/// Set the values for all parameters for a curve in order
All {
#[clap(subcommand)]
command: SetParamByModesSubcommands
},
}
#[derive(clap::Subcommand)]
pub enum GetParamsByModesSubcommands {
/// Get all the common parameters
Common,
$(
#[doc = "Get all the parameters for the " $mode " curve" ]
$mode
),+
}
#[derive(clap::Subcommand)]
pub enum CliSubcommandGetParams {
/// Get the value for a single parameter
Param { name: crate::params::Param },
/// Get the current acceleration mode (curve)
Mode,
/// Get the values for all parameters for a curve in order
All {
/// Print the values in one line, separated by a space
#[arg(long)]
oneline: bool,
#[arg(short, long)]
/// Print only the values
quiet: bool,
#[clap(subcommand)]
command: GetParamsByModesSubcommands
},
}
}
}
};
}
declare_params!(
Common {
SensMult,
YxRatio,
InputDpi,
AngleRotation
},
Linear {
Accel,
OffsetLinear,
OutputCap,
},
Natural {
DecayRate,
OffsetNatural,
Limit,
},
Synchronous {
Gamma,
Smooth,
Motivity,
SyncSpeed,
},
NoAccel {},
);
impl AccelMode {
pub fn as_title(&self) -> &'static str {
match self {
AccelMode::Linear => "Linear Acceleration",
AccelMode::Natural => "Natural (w/ Gain)",
AccelMode::Synchronous => "Synchronous",
AccelMode::NoAccel => "No Acceleration",
}
}
}
impl AccelMode {
pub const PARAM_NAME: &'static str = "MODE";
pub fn ordinal(&self) -> i64 {
(*self as i8).into()
}
}
impl Param {
/// The canonical internal name of the parameter.
pub fn name(&self) -> &'static str {
match self {
Param::SensMult => "SENS_MULT",
Param::YxRatio => "YX_RATIO",
Param::InputDpi => "INPUT_DPI",
Param::Accel => "ACCEL",
Param::OffsetLinear => "OFFSET",
Param::OffsetNatural => "OFFSET",
Param::OutputCap => "OUTPUT_CAP",
Param::DecayRate => "DECAY_RATE",
Param::Limit => "LIMIT",
Param::Gamma => "GAMMA",
Param::Smooth => "SMOOTH",
Param::Motivity => "MOTIVITY",
Param::SyncSpeed => "SYNC_SPEED",
Param::AngleRotation => "ANGLE_ROTATION",
}
}
pub fn display_name(&self) -> &'static str {
match self {
Param::SensMult => "Sens-Multiplier",
Param::Accel => "Accel",
Param::InputDpi => "Input DPI",
Param::OffsetLinear => "Offset",
Param::OffsetNatural => "Offset",
Param::OutputCap => "Output-Cap",
Param::YxRatio => "Y/x Ratio",
Param::DecayRate => "Decay-Rate",
Param::Limit => "Limit",
Param::Gamma => "Gamma",
Param::Smooth => "Smooth",
Param::Motivity => "Motivity",
Param::SyncSpeed => "Sync Speed",
Param::AngleRotation => "Angle Rotation",
}
}
pub fn description(&self) -> &'static str {
match self {
Param::SensMult => "Base sensitivity multiplier. Adjusts overall mouse speed.",
Param::YxRatio => {
"Y to X axis sensitivity ratio. Values > 1 increase vertical sensitivity."
}
Param::InputDpi => {
"Mouse DPI. Used to normalize to 1000 DPI equivalent for consistent acceleration."
}
Param::AngleRotation => "Rotation angle in degrees for sensitivity direction.",
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::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.",
Param::Gamma => "Exponent controlling curve shape. Higher = more aggressive ramp-up.",
Param::Smooth => "Smoothing factor (0-1). Higher = more gradual transitions.",
Param::Motivity => "Degree of acceleration effect. Must be > 1.",
Param::SyncSpeed => "Synchronization speed. Controls how fast sync responds.",
}
}
}
pub(crate) fn format_param_value(value: f64) -> String {
let mut number = format!("{value:.5}");
for idx in (1..number.len()).rev() {
let this_char = &number[idx..idx + 1];
if this_char != "0" {
if this_char == "." {
number.remove(idx);
}
break;
}
number.remove(idx);
}
number
}
#[cfg(test)]
#[test]
fn format_param_value_works() {
assert_eq!(format_param_value(1.5), "1.5");
assert_eq!(format_param_value(1.50), "1.5");
assert_eq!(format_param_value(100.0), "100");
assert_eq!(format_param_value(0.0600), "0.06");
assert_eq!(format_param_value(0.055000), "0.055");
}
pub(crate) fn validate_param_value(param_tag: Param, value: f64) -> anyhow::Result<()> {
match param_tag {
Param::SensMult => {}
Param::YxRatio => {}
Param::InputDpi => {
if value <= 0.0 {
anyhow::bail!("Input DPI must be positive");
}
}
Param::AngleRotation => {}
Param::Accel => {}
Param::OutputCap => {}
Param::OffsetLinear | Param::OffsetNatural => {
if value < 0.0 {
anyhow::bail!("offset cannot be less than 0");
}
}
Param::DecayRate => {
if value <= 0.0 {
anyhow::bail!("decay rate must be positive");
}
}
Param::Limit => {
if value < 1.0 {
anyhow::bail!("limit cannot be less than 1");
}
}
Param::Gamma => {
if value <= 0.0 {
anyhow::bail!("Gamma must be positive");
}
}
Param::Smooth => {
if !(0.0..=1.0).contains(&value) {
anyhow::bail!("Smooth must be between 0 and 1");
}
}
Param::Motivity => {
if value <= 1.0 {
anyhow::bail!("Motivity must be greater than 1");
}
}
Param::SyncSpeed => {
if value <= 0.0 {
anyhow::bail!("'Synchronous speed' must be positive");
}
}
}
Ok(())
}
+53
View File
@@ -0,0 +1,53 @@
use crate::{
AccelParams, AccelParamsByMode, LinearCurveParams, NaturalCurveParams, SynchronousCurveParams,
libmaccel::{self, fixedptc::Fpt},
params::AllParamArgs,
};
use crate::AccelMode;
impl AllParamArgs {
fn convert_to_accel_args(&self, mode: AccelMode) -> AccelParams {
let params_by_mode = match mode {
AccelMode::Linear => AccelParamsByMode::Linear(LinearCurveParams {
accel: self.accel,
offset_linear: self.offset_linear,
output_cap: self.output_cap,
}),
AccelMode::Natural => AccelParamsByMode::Natural(NaturalCurveParams {
decay_rate: self.decay_rate,
offset_natural: self.offset_natural,
limit: self.limit,
}),
AccelMode::Synchronous => AccelParamsByMode::Synchronous(SynchronousCurveParams {
gamma: self.gamma,
smooth: self.smooth,
motivity: self.motivity,
sync_speed: self.sync_speed,
}),
AccelMode::NoAccel => {
AccelParamsByMode::NoAccel(crate::params::NoAccelCurveParams { _ffi_guard: [] })
}
};
AccelParams {
sens_mult: self.sens_mult,
yx_ratio: self.yx_ratio,
input_dpi: self.input_dpi,
angle_rotation: self.angle_rotation,
by_mode: params_by_mode,
}
}
}
pub type SensXY = (f64, f64);
/// Ratio of Output speed to Input speed
pub fn sensitivity(s_in: f64, mode: AccelMode, params: &AllParamArgs) -> SensXY {
let sens =
unsafe { libmaccel::sensitivity_rs(s_in.into(), params.convert_to_accel_args(mode)) };
let ratio_x: f64 = Fpt(sens.x).into();
let ratio_y: f64 = Fpt(sens.y).into();
(ratio_x, ratio_y)
}