Files
gotta-scroll-fast/module.nix
T
thomas 5f1254d11a
Tests / test_core_function (push) Failing after 12s
import upstream maccel baseline
2026-03-25 09:32:22 +00:00

276 lines
8.6 KiB
Nix

# NixOS module for maccel mouse acceleration driver
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.hardware.maccel;
# Extract version from PKGBUILD
pkgbuildContent = builtins.readFile ./PKGBUILD;
kernelModuleVersion = builtins.head (builtins.match ".*pkgver=([^[:space:]]+).*" pkgbuildContent);
# Extract version from cli/Cargo.toml
cliCargoToml = builtins.fromTOML (builtins.readFile ./cli/Cargo.toml);
cliVersion = cliCargoToml.package.version;
# Convert float to fixed-point integer (64-bit, 32 fractional bits)
fixedPointScale = 4294967296; # 2^32
toFixedPoint = value: builtins.floor (value * fixedPointScale + 0.5);
# Mode enum mapping (from driver/accel/mode.h)
modeMap = {
linear = 0;
natural = 1;
synchronous = 2;
no_accel = 3;
};
# Parameter mapping (from driver/params.h)
parameterMap = {
# Common parameters
SENS_MULT = cfg.parameters.sensMultiplier;
YX_RATIO = cfg.parameters.yxRatio;
INPUT_DPI = cfg.parameters.inputDpi;
ANGLE_ROTATION = cfg.parameters.angleRotation;
MODE = cfg.parameters.mode;
# Linear mode parameters
ACCEL = cfg.parameters.acceleration;
OFFSET = cfg.parameters.offset;
OUTPUT_CAP = cfg.parameters.outputCap;
# Natural mode parameters
DECAY_RATE = cfg.parameters.decayRate;
LIMIT = cfg.parameters.limit;
# Synchronous mode parameters
GAMMA = cfg.parameters.gamma;
SMOOTH = cfg.parameters.smooth;
MOTIVITY = cfg.parameters.motivity;
SYNC_SPEED = cfg.parameters.syncSpeed;
};
# Generate modprobe parameter string
kernelModuleParams = let
validParams = filterAttrs (_: v: v != null) parameterMap;
formatParam = name: value:
if name == "MODE"
then "${name}=${toString (modeMap.${value})}"
else "${name}=${toString (toFixedPoint value)}";
in
concatStringsSep " " (mapAttrsToList formatParam validParams);
# Build kernel module
maccel-kernel-module = config.boot.kernelPackages.callPackage ({
lib,
stdenv,
kernel,
}:
stdenv.mkDerivation rec {
pname = "maccel-dkms";
version = kernelModuleVersion;
src = ./.;
nativeBuildInputs = kernel.moduleBuildDependencies;
makeFlags =
[
"KVER=${kernel.modDirVersion}"
"KDIR=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build"
"DRIVER_CFLAGS=-DFIXEDPT_BITS=64"
]
++ optionals cfg.debug ["DRIVER_CFLAGS+=-g -DDEBUG"];
preBuild = "cd driver";
installPhase = ''
mkdir -p $out/lib/modules/${kernel.modDirVersion}/kernel/drivers/usb
cp maccel.ko $out/lib/modules/${kernel.modDirVersion}/kernel/drivers/usb/
'';
meta = with lib; {
description = "Mouse acceleration driver and kernel module for Linux.";
homepage = "https://www.maccel.org/";
license = licenses.gpl2Plus;
platforms = platforms.linux;
};
}) {};
# Optional CLI tools
maccel-cli = pkgs.rustPlatform.buildRustPackage rec {
pname = "maccel-cli";
version = cliVersion;
src = ./.;
cargoLock.lockFile = "${src}/Cargo.lock";
cargoBuildFlags = ["--bin" "maccel"];
meta = with lib; {
description = "CLI and TUI tools for configuring maccel.";
homepage = "https://www.maccel.org/";
license = licenses.gpl2Plus;
platforms = platforms.linux;
};
};
in {
options.hardware.maccel = {
enable = mkEnableOption "Enable maccel mouse acceleration driver (kernel module). Parameters must be configured via `hardware.maccel.parameters`.";
debug = mkOption {
type = types.bool;
default = false;
description = "Enable debug build of the kernel module.";
};
enableCli = mkOption {
type = types.bool;
default = false;
description = "Install CLI and TUI tools for real-time parameter tuning. You may add your user to the `maccel` group using `users.groups.maccel.members = [\"your_username\"];` to run maccel CLI/TUI without sudo. Note: Changes made via CLI/TUI are temporary and do not persist across reboots. Use this to discover optimal parameter values, then apply them permanently via `hardware.maccel.parameters`.";
};
parameters = {
# Common parameters
sensMultiplier = mkOption {
type = types.nullOr types.float;
default = null;
description = "Sensitivity multiplier applied after acceleration calculation.";
};
yxRatio = mkOption {
type = types.nullOr types.float;
default = null;
description = "Y/X ratio - factor by which Y-axis sensitivity is multiplied.";
};
inputDpi = mkOption {
type =
types.nullOr (types.addCheck types.float (x: x > 0.0)
// {description = "positive float";});
default = null;
description = "DPI of the mouse, used to normalize effective DPI. Must be positive.";
};
angleRotation = mkOption {
type = types.nullOr types.float;
default = null;
description = "Apply rotation in degrees to mouse movement input.";
};
mode = mkOption {
type = types.nullOr (types.enum ["linear" "natural" "synchronous" "no_accel"]);
default = null;
description = "Acceleration mode.";
};
# Linear mode parameters
acceleration = mkOption {
type = types.nullOr types.float;
default = null;
description = "Linear acceleration factor.";
};
offset = mkOption {
type =
types.nullOr (types.addCheck types.float (x: x >= 0.0)
// {description = "non-negative float";});
default = null;
description = "Input speed past which to allow acceleration. Cannot be negative.";
};
outputCap = mkOption {
type = types.nullOr types.float;
default = null;
description = "Maximum sensitivity multiplier cap.";
};
# Natural mode parameters
decayRate = mkOption {
type =
types.nullOr (types.addCheck types.float (x: x > 0.0)
// {description = "positive float";});
default = null;
description = "Decay rate of the Natural acceleration curve. Must be positive.";
};
limit = mkOption {
type =
types.nullOr (types.addCheck types.float (x: x >= 1.0)
// {description = "float >= 1.0";});
default = null;
description = "Limit of the Natural acceleration curve. Cannot be less than 1.";
};
# Synchronous mode parameters
gamma = mkOption {
type =
types.nullOr (types.addCheck types.float (x: x > 0.0)
// {description = "positive float";});
default = null;
description = "Controls how fast you get from low to fast around the midpoint. Must be positive.";
};
smooth = mkOption {
type =
types.nullOr (types.addCheck types.float (x: x >= 0.0 && x <= 1.0)
// {description = "float between 0.0 and 1.0";});
default = null;
description = "Controls the suddenness of the sensitivity increase. Must be between 0 and 1.";
};
motivity = mkOption {
type =
types.nullOr (types.addCheck types.float (x: x > 1.0)
// {description = "float > 1.0";});
default = null;
description = "Sets max sensitivity while setting min to 1/motivity. Must be greater than 1.";
};
syncSpeed = mkOption {
type =
types.nullOr (types.addCheck types.float (x: x > 0.0)
// {description = "positive float";});
default = null;
description = "Sets the middle sensitivity between min and max sensitivity. Must be positive.";
};
};
};
config = mkIf cfg.enable {
# Add kernel module
boot.extraModulePackages = [maccel-kernel-module];
# Load module with parameters
boot.kernelModules = ["maccel"];
boot.extraModprobeConfig = mkIf (kernelModuleParams != "") ''
options maccel ${kernelModuleParams}
'';
# Create maccel group
users.groups.maccel = {};
# Install CLI tools if requested
environment.systemPackages = mkIf cfg.enableCli [maccel-cli];
# Create reset scripts directory
systemd.tmpfiles.rules = mkIf cfg.enableCli [
"d /var/opt/maccel/resets 0775 root maccel"
];
# Add udev rules
services.udev.extraRules = mkIf cfg.enableCli ''
# Set sysfs parameter permissions
ACTION=="add", SUBSYSTEM=="module", DEVPATH=="/module/maccel", \
RUN+="${pkgs.coreutils}/bin/chgrp -R maccel /sys/module/maccel/parameters"
# Set /dev/maccel character device permissions
ACTION=="add", KERNEL=="maccel", \
GROUP="maccel", MODE="0640"
'';
};
}