nvibrant: add module

This commit is contained in:
mikaeladev 2026-06-17 15:10:43 +01:00 committed by Austin Horstman
parent 66eb0fc116
commit af11a877f2
7 changed files with 396 additions and 0 deletions

View file

@ -0,0 +1,12 @@
{ pkgs, ... }:
{
time = "2026-06-17T14:09:44+00:00";
condition = pkgs.stdenv.hostPlatform.isLinux;
message = ''
A new module is available: `services.nvibrant`.
[nvibrant] is used for configuring NVIDIA's "Digital Vibrance" on Wayland.
[nvibrant]: https://github.com/tremeschin/nvibrant
'';
}

View file

@ -0,0 +1,197 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
assertMsg
concatLines
escapeShellArgs
floor
getExe
head
hm
imap
isList
literalExpression
maintainers
match
mkEnableOption
mkIf
mkOption
mkOptionType
mkPackageOption
optionals
platforms
removeSuffix
toInt
types
;
inherit (pkgs)
writeShellScript
;
listOrListOfListOf =
t:
types.addCheck (with types; listOf (either t (listOf t))) (
values:
values == [ ]
|| (
let
isMultiGpu = isList (head values);
in
lib.all (value: isList value == isMultiGpu) values
)
);
percentStr = mkOptionType {
inherit (types.str) merge;
name = "percentStr";
description = "percentage";
descriptionClass = "noun";
check = x: types.str.check x && match "[0-9]([0-9]+)?%" x != null;
};
percentStrToInt = x: toInt (removeSuffix "%" x);
percentStrBetween =
lowest: highest:
assert assertMsg (lowest <= highest) "percentStrBetween: lowest must be smaller than highest";
types.addCheck percentStr (
v:
let
i = percentStrToInt v;
in
i >= lowest && i <= highest
)
// {
name = "percentStrBetween";
description = "percentage between ${toString lowest}% and ${toString highest}% (both inclusive)";
};
cfg = config.services.nvibrant;
in
{
meta.maintainers = with maintainers; [ mikaeladev ];
options.services.nvibrant = {
enable = mkEnableOption "nvibrant";
package = mkPackageOption pkgs "nvibrant" { };
dithering = mkOption {
type = with types; listOrListOfListOf (nullOr (either bool (enum [ "auto" ])));
default = [ ];
example = literalExpression ''
[
true # HDMI
null # DP1
false # DP2
]
'';
description = ''
Whether to enable or disable dithering for your monitor(s).
Values should match the order of physical ports on your GPU. If a null
value is passed, nvibrant will default to false (even if a device is
connected at that port).
If you have multiple GPUs, you can pass a list of lists in order of
device.
'';
};
vibrancy = mkOption {
type = with types; listOrListOfListOf (nullOr (percentStrBetween 0 200));
default = [ ];
example = literalExpression ''
[
"0%" # HDMI
null # DP1
"200%" # DP2
]
'';
description = ''
The vibrancy level for your monitor(s).
Values should match the order of physical ports on your GPU. If a null
value is passed, nvibrant will default to 100% (even if a device is
connected at that port).
If you have multiple GPUs, you can pass a list of lists in order of
device.
'';
};
};
config = mkIf cfg.enable {
assertions = [
(hm.assertions.assertPlatform "services.nvibrant" pkgs platforms.linux)
{
assertion = !(cfg.dithering == [ ] && cfg.vibrancy == [ ]);
message = "Either `services.nvibrant.dithering` or `services.nvibrant.vibrancy` must be set";
}
];
systemd.user.services.nvibrant = {
Unit = {
Description = "Applies nvibrant";
After = [ "graphical-session.target" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
Type = "oneshot";
ExecStart =
let
clampMax = x: y: if x > y then y else x;
percentIntToValue = x: clampMax (floor (10.24 * x - 1024)) 1023;
mkDitheringArgs =
value:
escapeShellArgs (
map (
v:
if v == "auto" then
0
else if v == true then
1
else
2
) value
);
mkVibrancyArgs =
value:
escapeShellArgs (map (v: percentIntToValue (if v == null then 100 else percentStrToInt v)) value);
mkLines =
fn: list:
if isList (head list) then
imap (i: v: "NVIDIA_GPU=${toString (i - 1)} ${fn v}") list
else
[ (fn list) ];
binPath = getExe cfg.package;
ditheringLines = optionals (cfg.dithering != [ ]) (
mkLines (value: "ATTRIBUTE=dithering ${binPath} ${mkDitheringArgs value}") cfg.dithering
);
vibrancyLines = optionals (cfg.vibrancy != [ ]) (
mkLines (value: "${binPath} ${mkVibrancyArgs value}") cfg.vibrancy
);
scriptLines = concatLines (ditheringLines ++ vibrancyLines);
in
writeShellScript "apply-nvibrant" scriptLines;
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
};
}

View file

@ -0,0 +1,8 @@
{ lib, pkgs, ... }:
lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
nvibrant-dithering = ./dithering.nix;
nvibrant-merged = ./merged.nix;
nvibrant-multigpu = ./multigpu.nix;
nvibrant-vibrancy = ./vibrancy.nix;
}

View file

@ -0,0 +1,40 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) getExe head;
inherit (pkgs) writeShellScript;
cfg = config.services.nvibrant;
service = config.systemd.user.services.nvibrant;
scriptFile = head service.Service.ExecStart;
expectedFile = writeShellScript "apply-nvibrant" ''
ATTRIBUTE=dithering ${getExe cfg.package} 1 2 2 0
'';
in
{
services.nvibrant = {
enable = true;
dithering = [
true
null
false
"auto"
];
};
nmt.script = ''
local script_file=${scriptFile}
local expected_file=${expectedFile}
assertFileExists "$script_file"
assertFileContent "$script_file" "$expected_file"
'';
}

View file

@ -0,0 +1,46 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) getExe head;
inherit (pkgs) writeShellScript;
cfg = config.services.nvibrant;
service = config.systemd.user.services.nvibrant;
scriptFile = head service.Service.ExecStart;
expectedFile = writeShellScript "apply-nvibrant" ''
ATTRIBUTE=dithering ${getExe cfg.package} 1 2 2 0
${getExe cfg.package} -1024 0 1023
'';
in
{
services.nvibrant = {
enable = true;
dithering = [
true
null
false
"auto"
];
vibrancy = [
"0%"
null
"200%"
];
};
nmt.script = ''
local script_file=${scriptFile}
local expected_file=${expectedFile}
assertFileExists "$script_file"
assertFileContent "$script_file" "$expected_file"
'';
}

View file

@ -0,0 +1,54 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) getExe head;
inherit (pkgs) writeShellScript;
cfg = config.services.nvibrant;
service = config.systemd.user.services.nvibrant;
scriptFile = head service.Service.ExecStart;
expectedFile = writeShellScript "apply-nvibrant" ''
NVIDIA_GPU=0 ATTRIBUTE=dithering ${getExe cfg.package} 1 2 2 0
NVIDIA_GPU=1 ATTRIBUTE=dithering ${getExe cfg.package} 2
NVIDIA_GPU=0 ${getExe cfg.package} -1024 0 1023
NVIDIA_GPU=1 ${getExe cfg.package} 0
'';
in
{
services.nvibrant = {
enable = true;
dithering = [
[
true
null
false
"auto"
]
[ null ]
];
vibrancy = [
[
"0%"
null
"200%"
]
[ null ]
];
};
nmt.script = ''
local script_file=${scriptFile}
local expected_file=${expectedFile}
assertFileExists "$script_file"
assertFileContent "$script_file" "$expected_file"
'';
}

View file

@ -0,0 +1,39 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) getExe head;
inherit (pkgs) writeShellScript;
cfg = config.services.nvibrant;
service = config.systemd.user.services.nvibrant;
scriptFile = head service.Service.ExecStart;
expectedFile = writeShellScript "apply-nvibrant" ''
${getExe cfg.package} -1024 0 1023
'';
in
{
services.nvibrant = {
enable = true;
vibrancy = [
"0%"
null
"200%"
];
};
nmt.script = ''
local script_file=${scriptFile}
local expected_file=${expectedFile}
assertFileExists "$script_file"
assertFileContent "$script_file" "$expected_file"
'';
}