This commit is contained in:
@@ -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(())
|
||||
}
|
||||
Reference in New Issue
Block a user