This commit is contained in:
+275
@@ -0,0 +1,275 @@
|
||||
# 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"
|
||||
'';
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user