From b908e61dfad269d97376c1832eb9127e444953a3 Mon Sep 17 00:00:00 2001 From: Thiago Kenji Okada Date: Thu, 5 May 2022 22:56:43 +0100 Subject: [PATCH] picom: sync module with the NixOS version This brings a few advantages: - Use of float instead of strings to represent float values, - Use of structure settings, and - Better type checking for some settings Also add thiagokokada as codeowner of picom. --- .github/CODEOWNERS | 3 + docs/release-notes/rl-2211.adoc | 9 + modules/misc/news.nix | 14 + modules/services/picom.nix | 334 +++++++++--------- .../picom-basic-configuration-expected.conf | 41 +-- .../picom/picom-basic-configuration.nix | 12 +- 6 files changed, 218 insertions(+), 195 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ffd63ea0b..3457a4a65 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -386,6 +386,9 @@ /modules/services/pasystray.nix @pltanton +/modules/services/picom.nix @thiagokokada +/tests/modules/services/picom @thiagokokada + /modules/services/pbgopy.nix @ivarwithoutbones /tests/modules/services/pbgopy @ivarwithoutbones diff --git a/docs/release-notes/rl-2211.adoc b/docs/release-notes/rl-2211.adoc index 8f0052588..d972e8d55 100644 --- a/docs/release-notes/rl-2211.adoc +++ b/docs/release-notes/rl-2211.adoc @@ -75,6 +75,15 @@ Of course, you can move the assignment of <>, <>, and <> to some other file or simply place them in your `home.nix`. +* The `services.picom` module has been refactored to use structural +settings. ++ +As a result `services.picom.extraOptions` has been removed in favor of +<>. Also, `services.picom.blur*` were +removed since upstream changed the blur settings to be more flexible. +You can migrate the blur settings to use +<> instead. + [[sec-release-22.11-state-version-changes]] === State Version Changes diff --git a/modules/misc/news.nix b/modules/misc/news.nix index e11d81039..aaceacca0 100644 --- a/modules/misc/news.nix +++ b/modules/misc/news.nix @@ -583,6 +583,20 @@ in A new module is available: 'services.sctd'. ''; } + + { + time = "2022-07-12T08:59:50+00:00"; + condition = config.services.picom.enable; + message = '' + The 'services.picom' module has been refactored to use structural + settings. + + As a result 'services.picom.extraOptions' has been removed in favor of + 'services.picom.settings'. Also, 'services.picom.blur*' were removed + since upstream changed the blur settings to be more flexible. You can + migrate the blur settings to use 'services.picom.settings' instead. + ''; + } ]; }; } diff --git a/modules/services/picom.nix b/modules/services/picom.nix index 9ea052ec0..5f4e10f55 100644 --- a/modules/services/picom.nix +++ b/modules/services/picom.nix @@ -1,89 +1,77 @@ -{ config, lib, pkgs, ... }: +{ config, options, lib, pkgs, ... }: let - inherit (builtins) toJSON toString; + inherit (builtins) elemAt isAttrs isBool length mapAttrs toJSON; inherit (lib) - concatStringsSep elemAt literalExpression mkEnableOption mkIf mkOption - mkRemovedOptionModule optional optionalAttrs optionalString types; + boolToString concatMapStringsSep concatStringsSep escape literalExpression + mapAttrsToList mkEnableOption mkRemovedOptionModule mkDefault mkIf mkOption + optional types warn; cfg = config.services.picom; + opt = options.services.picom; - configFile = optionalString cfg.fade '' - # fading - fading = true; - fade-delta = ${toString cfg.fadeDelta}; - fade-in-step = ${elemAt cfg.fadeSteps 0}; - fade-out-step = ${elemAt cfg.fadeSteps 1}; - fade-exclude = ${toJSON cfg.fadeExclude}; - '' + optionalString cfg.shadow '' - - # shadows - shadow = true; - shadow-offset-x = ${toString (elemAt cfg.shadowOffsets 0)}; - shadow-offset-y = ${toString (elemAt cfg.shadowOffsets 1)}; - shadow-opacity = ${cfg.shadowOpacity}; - shadow-exclude = ${toJSON cfg.shadowExclude}; - '' + optionalString cfg.blur '' - - # blur - blur-background = true; - blur-background-exclude = ${toJSON cfg.blurExclude}; - '' + '' - - # opacity - active-opacity = ${cfg.activeOpacity}; - inactive-opacity = ${cfg.inactiveOpacity}; - inactive-dim = ${cfg.inactiveDim}; - opacity-rule = ${toJSON cfg.opacityRule}; - - wintypes: - { - dock = { shadow = ${toJSON (!cfg.noDockShadow)}; }; - dnd = { shadow = ${toJSON (!cfg.noDNDShadow)}; }; - popup_menu = { opacity = ${cfg.menuOpacity}; }; - dropdown_menu = { opacity = ${cfg.menuOpacity}; }; + pairOf = x: + with types; + addCheck (listOf x) (y: length y == 2) // { + description = "pair of ${x.description}"; }; - # other options - backend = ${toJSON cfg.backend}; - vsync = ${toJSON cfg.vSync}; - '' + cfg.extraOptions; + floatBetween = a: b: + with types; + let + # toString prints floats with hardcoded high precision + floatToString = f: toJSON f; + in addCheck float (x: x <= b && x >= a) // { + description = "a floating point number in " + + "range [${floatToString a}, ${floatToString b}]"; + }; + + 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 cfg.settings); + + configFile = toConf cfg.settings; in { + imports = [ + (mkRemovedOptionModule [ "services" "picom" "refreshRate" ] + "The option `refresh-rate` has been deprecated by upstream.") + (mkRemovedOptionModule [ "services" "picom" "extraOptions" ] + "This option has been replaced by `services.picom.settings`.") + ]; options.services.picom = { enable = mkEnableOption "Picom X11 compositor"; - blur = mkOption { - type = types.bool; - default = false; - description = '' - Enable background blur on transparent windows. - ''; - }; - - blurExclude = mkOption { - type = types.listOf types.str; - default = [ ]; - example = [ "class_g = 'slop'" "class_i = 'polybar'" ]; - description = '' - List of windows to exclude background blur. - See the - - picom - 1 - - man page for more examples. - ''; - }; - - experimentalBackends = mkOption { - type = types.bool; - default = false; - description = '' - Whether to use the new experimental backends. - ''; - }; + experimentalBackends = mkEnableOption "the new experimental backends"; fade = mkOption { type = types.bool; @@ -94,7 +82,7 @@ in { }; fadeDelta = mkOption { - type = types.int; + type = types.ints.positive; default = 10; example = 5; description = '' @@ -103,9 +91,9 @@ in { }; fadeSteps = mkOption { - type = types.listOf types.str; - default = [ "0.028" "0.03" ]; - example = [ "0.04" "0.04" ]; + type = pairOf (floatBetween 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). ''; @@ -117,12 +105,7 @@ in { example = [ "window_type *= 'menu'" "name ~= 'Firefox$'" "focused = 1" ]; description = '' List of conditions of windows that should not be faded. - See the - - picom - 1 - - man page for more examples. + See picom(1) man page for more examples. ''; }; @@ -135,20 +118,20 @@ in { }; shadowOffsets = mkOption { - type = types.listOf types.int; + type = pairOf types.int; default = [ (-15) (-15) ]; example = [ (-10) (-15) ]; description = '' - Horizontal and vertical offsets for shadows (in pixels). + Left and right offset for shadows (in pixels). ''; }; shadowOpacity = mkOption { - type = types.str; - default = "0.75"; - example = "0.8"; + type = floatBetween 0 1; + default = 0.75; + example = 0.8; description = '' - Window shadows opacity (number in range 0 - 1). + Window shadows opacity. ''; }; @@ -158,87 +141,72 @@ in { example = [ "window_type *= 'menu'" "name ~= 'Firefox$'" "focused = 1" ]; description = '' List of conditions of windows that should have no shadow. - See the - - picom - 1 - - man page for more examples. - ''; - }; - - noDockShadow = mkOption { - type = types.bool; - default = true; - description = '' - Avoid shadow on docks. - ''; - }; - - noDNDShadow = mkOption { - type = types.bool; - default = true; - description = '' - Avoid shadow on drag-and-drop windows. + See picom(1) man page for more examples. ''; }; activeOpacity = mkOption { - type = types.str; - default = "1.0"; - example = "0.8"; + type = floatBetween 0 1; + default = 1.0; + example = 0.8; description = '' Opacity of active windows. ''; }; - inactiveDim = mkOption { - type = types.str; - default = "0.0"; - example = "0.2"; - description = '' - Dim inactive windows. - ''; - }; - inactiveOpacity = mkOption { - type = types.str; - default = "1.0"; - example = "0.8"; + type = floatBetween 0.1 1; + default = 1.0; + example = 0.8; description = '' Opacity of inactive windows. ''; }; menuOpacity = mkOption { - type = types.str; - default = "1.0"; - example = "0.8"; + type = floatBetween 0 1; + default = 1.0; + example = 0.8; description = '' Opacity of dropdown and popup menu. ''; }; - opacityRule = mkOption { + 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 = [ "87:class_i ?= 'scratchpad'" "91:class_i ?= 'xterm'" ]; + example = [ + "95:class_g = 'URxvt' && !_NET_WM_STATE@:32a" + "0:_NET_WM_STATE@:32a *= '_NET_WM_STATE_HIDDEN'" + ]; description = '' - List of opacity rules. - See the - - picom - 1 - - man page for more examples. + Rules that control the opacity of windows, in format PERCENT:PATTERN. ''; }; backend = mkOption { - type = types.str; - default = "glx"; + type = types.enum [ "glx" "xrender" "xr_glx_hybrid" ]; + default = "xrender"; description = '' - Backend to use: glx or xrender. + Backend to use: glx, xrender or xr_glx_hybrid. ''; }; @@ -256,33 +224,75 @@ in { defaultText = literalExpression "pkgs.picom"; example = literalExpression "pkgs.picom"; description = '' - picom derivation to use. + Picom derivation to use. ''; }; - extraOptions = mkOption { - type = types.lines; - default = ""; - example = '' - unredir-if-possible = true; - dbe = true; - ''; - description = '' - Additional Picom configuration. - ''; - }; + 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). + ''; + }; }; - imports = [ - (mkRemovedOptionModule [ "services" "picom" "refreshRate" ] - "The option `refresh-rate` has been deprecated by upstream.") - ]; - 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 ]; @@ -307,4 +317,6 @@ in { }; }; }; + + meta.maintainers = with lib.maintainers; [ thiagokokada ]; } diff --git a/tests/modules/services/picom/picom-basic-configuration-expected.conf b/tests/modules/services/picom/picom-basic-configuration-expected.conf index 6a7ac09aa..c1e7d1912 100644 --- a/tests/modules/services/picom/picom-basic-configuration-expected.conf +++ b/tests/modules/services/picom/picom-basic-configuration-expected.conf @@ -1,33 +1,18 @@ -# fading +active-opacity = 1.000000; +backend = "xrender"; +dbe = true; +fade-delta = 5; +fade-exclude = [ "window_type *= 'menu'" , "name ~= 'Firefox$'" , "focused = 1" ]; +fade-in-step = 0.040000; +fade-out-step = 0.040000; fading = true; -fade-delta = 5; -fade-in-step = 0.04; -fade-out-step = 0.04; -fade-exclude = ["window_type *= 'menu'","name ~= 'Firefox$'","focused = 1"]; - -# shadows +inactive-opacity = 1.000000; +opacity-rule = [ ]; shadow = true; +shadow-exclude = [ "window_type *= 'menu'" , "name ~= 'Firefox$'" , "focused = 1" ]; shadow-offset-x = -10; shadow-offset-y = -15; -shadow-opacity = 0.8; -shadow-exclude = ["window_type *= 'menu'","name ~= 'Firefox$'","focused = 1"]; - -# opacity -active-opacity = 1.0; -inactive-opacity = 1.0; -inactive-dim = 0.0; -opacity-rule = []; - -wintypes: -{ - dock = { shadow = false; }; - dnd = { shadow = false; }; - popup_menu = { opacity = 1.0; }; - dropdown_menu = { opacity = 1.0; }; -}; - -# other options -backend = "xrender"; -vsync = true; +shadow-opacity = 0.800000; unredir-if-possible = true; -dbe = true; +vsync = true; +wintypes: { dropdown_menu = { opacity = 1.000000; }; popup_menu = { opacity = 1.000000; }; }; \ No newline at end of file diff --git a/tests/modules/services/picom/picom-basic-configuration.nix b/tests/modules/services/picom/picom-basic-configuration.nix index 41f1ba01f..67848f52f 100644 --- a/tests/modules/services/picom/picom-basic-configuration.nix +++ b/tests/modules/services/picom/picom-basic-configuration.nix @@ -5,20 +5,20 @@ enable = true; fade = true; fadeDelta = 5; - fadeSteps = [ "0.04" "0.04" ]; + fadeSteps = [ 4.0e-2 4.0e-2 ]; fadeExclude = [ "window_type *= 'menu'" "name ~= 'Firefox$'" "focused = 1" ]; shadow = true; shadowOffsets = [ (-10) (-15) ]; - shadowOpacity = "0.8"; + shadowOpacity = 0.8; shadowExclude = [ "window_type *= 'menu'" "name ~= 'Firefox$'" "focused = 1" ]; backend = "xrender"; vSync = true; - extraOptions = '' - unredir-if-possible = true; - dbe = true; - ''; + settings = { + "unredir-if-possible" = true; + "dbe" = true; + }; experimentalBackends = true; };