1
0
Fork 0
mirror of https://github.com/nix-community/home-manager.git synced 2025-11-08 11:36:05 +01:00
home-manager/modules/services/picom.nix
Dixon Sean Low Yan Feng 0bc5f414f8 picom: add extraConfig to options
The picom module's `services.picom.settings` converts Nix expressions
to the libconfig format. However, that option currently does not have
a way to specify libconfig lists (enclosed in parentheses `()`), and
can only specify arrays (enclosed in square brackets `[]`). This makes
it impossible to specify certain picom configuration options, such as
animations (which are specified using lists).

This commit adds an `extraConfig` option to the picom module as an
escape hatch so that users can still specify picom options that are not
currently representable through `services.picom.settings`.
2025-10-25 21:15:52 -05:00

436 lines
9.7 KiB
Nix

{
config,
options,
lib,
pkgs,
...
}:
let
inherit (builtins)
elemAt
isAttrs
length
mapAttrs
;
inherit (lib)
boolToString
concatMapStringsSep
concatStringsSep
escape
literalExpression
mapAttrsToList
mkEnableOption
mkRenamedOptionModule
mkRemovedOptionModule
mkDefault
mkIf
mkOption
types
getExe
;
cfg = config.services.picom;
opt = options.services.picom;
pairOf =
x:
with types;
addCheck (listOf x) (y: length y == 2)
// {
description = "pair of ${x.description}";
};
mkDefaultAttrs = mapAttrs (n: v: mkDefault v);
# Basically a tinkered lib.generators.mkKeyValueDefault
# It either serializes a top-level definition "key: { values };"
# or an expression "key = { values };"
mkAttrsString =
top:
mapAttrsToList (
k: v:
let
sep = if (top && isAttrs v) then ": " else " = ";
in
"${escape [ sep ] k}${sep}${mkValueString v};"
);
# This serializes a Nix expression to the libconfig format.
mkValueString =
v:
if types.bool.check v then
boolToString v
else if types.int.check v then
toString v
else if types.float.check v then
toString v
else if types.str.check v then
''"${escape [ ''"'' ] v}"''
else if builtins.isList v then
"[ ${concatMapStringsSep " , " mkValueString v} ]"
else if types.attrs.check v then
"{ ${concatStringsSep " " (mkAttrsString false v)} }"
else
throw ''
invalid expression used in option services.picom.settings:
${v}
'';
toConf = attrs: concatStringsSep "\n" (mkAttrsString true attrs);
configFile = concatStringsSep "\n" [
(toConf cfg.settings)
cfg.extraConfig
];
in
{
imports = [
(mkRemovedOptionModule [
"services"
"picom"
"refreshRate"
] "The option `refresh-rate` has been deprecated by upstream.")
(mkRemovedOptionModule [
"services"
"picom"
"experimentalBackends"
] "The option `--experimental-backends` has been removed by upstream.")
(mkRemovedOptionModule [
"services"
"picom"
"extraOptions"
] "This option has been replaced by `services.picom.settings`.")
(mkRenamedOptionModule
[ "services" "picom" "opacityRule" ]
[
"services"
"picom"
"opacityRules"
]
)
];
options.services.picom = {
enable = mkEnableOption "Picom X11 compositor";
fade = mkOption {
type = types.bool;
default = false;
description = ''
Fade windows in and out.
'';
};
fadeDelta = mkOption {
type = types.ints.positive;
default = 10;
example = 5;
description = ''
Time between fade animation step (in ms).
'';
};
fadeSteps = mkOption {
type = pairOf (types.numbers.between 1.0e-2 1);
default = [
2.8e-2
3.0e-2
];
example = [
4.0e-2
4.0e-2
];
description = ''
Opacity change between fade steps (in and out).
'';
};
fadeExclude = mkOption {
type = types.listOf types.str;
default = [ ];
example = [
"window_type *= 'menu'"
"name ~= 'Firefox$'"
"focused = 1"
];
description = ''
List of conditions of windows that should not be faded.
See `picom(1)` man page for more examples.
'';
};
shadow = mkOption {
type = types.bool;
default = false;
description = ''
Draw window shadows.
'';
};
shadowOffsets = mkOption {
type = pairOf types.int;
default = [
(-15)
(-15)
];
example = [
(-10)
(-15)
];
description = ''
Left and right offset for shadows (in pixels).
'';
};
shadowOpacity = mkOption {
type = types.numbers.between 0 1;
default = 0.75;
example = 0.8;
description = ''
Window shadows opacity.
'';
};
shadowExclude = mkOption {
type = types.listOf types.str;
default = [ ];
example = [
"window_type *= 'menu'"
"name ~= 'Firefox$'"
"focused = 1"
];
description = ''
List of conditions of windows that should have no shadow.
See `picom(1)` man page for more examples.
'';
};
activeOpacity = mkOption {
type = types.numbers.between 0 1;
default = 1.0;
example = 0.8;
description = ''
Opacity of active windows.
'';
};
inactiveOpacity = mkOption {
type = types.numbers.between 0.1 1;
default = 1.0;
example = 0.8;
description = ''
Opacity of inactive windows.
'';
};
menuOpacity = mkOption {
type = types.numbers.between 0 1;
default = 1.0;
example = 0.8;
description = ''
Opacity of dropdown and popup menu.
'';
};
wintypes = mkOption {
type = types.attrs;
default = {
popup_menu = {
opacity = cfg.menuOpacity;
};
dropdown_menu = {
opacity = cfg.menuOpacity;
};
};
defaultText = literalExpression ''
{
popup_menu = { opacity = config.${opt.menuOpacity}; };
dropdown_menu = { opacity = config.${opt.menuOpacity}; };
}
'';
example = { };
description = ''
Rules for specific window types.
'';
};
opacityRules = mkOption {
type = types.listOf types.str;
default = [ ];
example = [
"95:class_g = 'URxvt' && !_NET_WM_STATE@:32a"
"0:_NET_WM_STATE@:32a *= '_NET_WM_STATE_HIDDEN'"
];
description = ''
Rules that control the opacity of windows, in format PERCENT:PATTERN.
'';
};
backend = mkOption {
type = types.enum [
"egl"
"glx"
"xrender"
"xr_glx_hybrid"
];
default = "xrender";
description = ''
Backend to use: `egl`, `glx`, `xrender` or `xr_glx_hybrid`.
'';
};
vSync = mkOption {
type = types.bool;
default = false;
description = ''
Enable vertical synchronization.
'';
};
extraArgs = mkOption {
type = with types; listOf str;
default = [ ];
example = literalExpression ''[ "--legacy-backends" ]'';
description = ''
Extra arguments to be passed to the picom executable.
'';
};
package = lib.mkPackageOption pkgs "picom" { };
settings =
with types;
let
scalar =
oneOf [
bool
int
float
str
]
// {
description = "scalar types";
};
libConfig =
oneOf [
scalar
(listOf libConfig)
(attrsOf libConfig)
]
// {
description = "libconfig type";
};
topLevel = attrsOf libConfig // {
description = ''
libconfig configuration. The format consists of an attributes
set (called a group) of settings. Each setting can be a scalar type
(boolean, integer, floating point number or string), a list of
scalars or a group itself
'';
};
in
mkOption {
type = topLevel;
default = { };
example = literalExpression ''
blur =
{ method = "gaussian";
size = 10;
deviation = 5.0;
};
'';
description = ''
Picom settings. Use this option to configure Picom settings not exposed
in a NixOS option or to bypass one. For the available options see the
CONFIGURATION FILES section at `picom(1)`.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
example = ''
animations = (
{
triggers = [ "open", "show" ];
preset = "slide-in";
direction = "up";
duration = 0.2;
}
)
'';
description = ''
Extra configuration lines to append to the picom configuration file.
'';
};
};
config = mkIf cfg.enable {
assertions = [
(lib.hm.assertions.assertPlatform "services.picom" pkgs lib.platforms.linux)
];
services.picom.settings = mkDefaultAttrs {
# fading
fading = cfg.fade;
fade-delta = cfg.fadeDelta;
fade-in-step = elemAt cfg.fadeSteps 0;
fade-out-step = elemAt cfg.fadeSteps 1;
fade-exclude = cfg.fadeExclude;
# shadows
shadow = cfg.shadow;
shadow-offset-x = elemAt cfg.shadowOffsets 0;
shadow-offset-y = elemAt cfg.shadowOffsets 1;
shadow-opacity = cfg.shadowOpacity;
shadow-exclude = cfg.shadowExclude;
# opacity
active-opacity = cfg.activeOpacity;
inactive-opacity = cfg.inactiveOpacity;
wintypes = cfg.wintypes;
opacity-rule = cfg.opacityRules;
# other options
backend = cfg.backend;
vsync = cfg.vSync;
};
home.packages = [ cfg.package ];
xdg.configFile."picom/picom.conf".text = configFile;
systemd.user.services.picom = {
Unit = {
Description = "Picom X11 compositor";
After = [ "graphical-session.target" ];
PartOf = [ "graphical-session.target" ];
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
Service = {
ExecStart = concatStringsSep " " (
[
"${getExe cfg.package}"
"--config ${config.xdg.configFile."picom/picom.conf".source}"
]
++ cfg.extraArgs
);
Restart = "always";
RestartSec = 3;
};
};
};
meta.maintainers = with lib.maintainers; [ thiagokokada ];
}