nvibrant: add module
This commit is contained in:
parent
66eb0fc116
commit
af11a877f2
7 changed files with 396 additions and 0 deletions
12
modules/misc/news/2026/06/2026-06-17_15-09-44.nix
Normal file
12
modules/misc/news/2026/06/2026-06-17_15-09-44.nix
Normal 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
|
||||
'';
|
||||
}
|
||||
197
modules/services/nvibrant.nix
Normal file
197
modules/services/nvibrant.nix
Normal 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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
8
tests/modules/services/nvibrant/default.nix
Normal file
8
tests/modules/services/nvibrant/default.nix
Normal 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;
|
||||
}
|
||||
40
tests/modules/services/nvibrant/dithering.nix
Normal file
40
tests/modules/services/nvibrant/dithering.nix
Normal 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"
|
||||
'';
|
||||
}
|
||||
46
tests/modules/services/nvibrant/merged.nix
Normal file
46
tests/modules/services/nvibrant/merged.nix
Normal 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"
|
||||
'';
|
||||
}
|
||||
54
tests/modules/services/nvibrant/multigpu.nix
Normal file
54
tests/modules/services/nvibrant/multigpu.nix
Normal 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"
|
||||
'';
|
||||
}
|
||||
39
tests/modules/services/nvibrant/vibrancy.nix
Normal file
39
tests/modules/services/nvibrant/vibrancy.nix
Normal 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"
|
||||
'';
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue