at 21.11-pre 9.1 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.services.picom; 8 9 pairOf = x: with types; 10 addCheck (listOf x) (y: length y == 2) 11 // { description = "pair of ${x.description}"; }; 12 13 floatBetween = a: b: with types; 14 let 15 # toString prints floats with hardcoded high precision 16 floatToString = f: builtins.toJSON f; 17 in 18 addCheck float (x: x <= b && x >= a) 19 // { description = "a floating point number in " + 20 "range [${floatToString a}, ${floatToString b}]"; }; 21 22 mkDefaultAttrs = mapAttrs (n: v: mkDefault v); 23 24 # Basically a tinkered lib.generators.mkKeyValueDefault 25 # It either serializes a top-level definition "key: { values };" 26 # or an expression "key = { values };" 27 mkAttrsString = top: 28 mapAttrsToList (k: v: 29 let sep = if (top && isAttrs v) then ":" else "="; 30 in "${escape [ sep ] k}${sep}${mkValueString v};"); 31 32 # This serializes a Nix expression to the libconfig format. 33 mkValueString = v: 34 if types.bool.check v then boolToString v 35 else if types.int.check v then toString v 36 else if types.float.check v then toString v 37 else if types.str.check v then "\"${escape [ "\"" ] v}\"" 38 else if builtins.isList v then "[ ${concatMapStringsSep " , " mkValueString v} ]" 39 else if types.attrs.check v then "{ ${concatStringsSep " " (mkAttrsString false v) } }" 40 else throw '' 41 invalid expression used in option services.picom.settings: 42 ${v} 43 ''; 44 45 toConf = attrs: concatStringsSep "\n" (mkAttrsString true cfg.settings); 46 47 configFile = pkgs.writeText "picom.conf" (toConf cfg.settings); 48 49in { 50 51 imports = [ 52 (mkAliasOptionModule [ "services" "compton" ] [ "services" "picom" ]) 53 ]; 54 55 options.services.picom = { 56 enable = mkOption { 57 type = types.bool; 58 default = false; 59 description = '' 60 Whether or not to enable Picom as the X.org composite manager. 61 ''; 62 }; 63 64 experimentalBackends = mkOption { 65 type = types.bool; 66 default = false; 67 description = '' 68 Whether to use the unstable new reimplementation of the backends. 69 ''; 70 }; 71 72 fade = mkOption { 73 type = types.bool; 74 default = false; 75 description = '' 76 Fade windows in and out. 77 ''; 78 }; 79 80 fadeDelta = mkOption { 81 type = types.ints.positive; 82 default = 10; 83 example = 5; 84 description = '' 85 Time between fade animation step (in ms). 86 ''; 87 }; 88 89 fadeSteps = mkOption { 90 type = pairOf (floatBetween 0.01 1); 91 default = [ 0.028 0.03 ]; 92 example = [ 0.04 0.04 ]; 93 description = '' 94 Opacity change between fade steps (in and out). 95 ''; 96 }; 97 98 fadeExclude = mkOption { 99 type = types.listOf types.str; 100 default = []; 101 example = [ 102 "window_type *= 'menu'" 103 "name ~= 'Firefox$'" 104 "focused = 1" 105 ]; 106 description = '' 107 List of conditions of windows that should not be faded. 108 See <literal>picom(1)</literal> man page for more examples. 109 ''; 110 }; 111 112 shadow = mkOption { 113 type = types.bool; 114 default = false; 115 description = '' 116 Draw window shadows. 117 ''; 118 }; 119 120 shadowOffsets = mkOption { 121 type = pairOf types.int; 122 default = [ (-15) (-15) ]; 123 example = [ (-10) (-15) ]; 124 description = '' 125 Left and right offset for shadows (in pixels). 126 ''; 127 }; 128 129 shadowOpacity = mkOption { 130 type = floatBetween 0 1; 131 default = 0.75; 132 example = 0.8; 133 description = '' 134 Window shadows opacity. 135 ''; 136 }; 137 138 shadowExclude = mkOption { 139 type = types.listOf types.str; 140 default = []; 141 example = [ 142 "window_type *= 'menu'" 143 "name ~= 'Firefox$'" 144 "focused = 1" 145 ]; 146 description = '' 147 List of conditions of windows that should have no shadow. 148 See <literal>picom(1)</literal> man page for more examples. 149 ''; 150 }; 151 152 activeOpacity = mkOption { 153 type = floatBetween 0 1; 154 default = 1.0; 155 example = 0.8; 156 description = '' 157 Opacity of active windows. 158 ''; 159 }; 160 161 inactiveOpacity = mkOption { 162 type = floatBetween 0.1 1; 163 default = 1.0; 164 example = 0.8; 165 description = '' 166 Opacity of inactive windows. 167 ''; 168 }; 169 170 menuOpacity = mkOption { 171 type = floatBetween 0 1; 172 default = 1.0; 173 example = 0.8; 174 description = '' 175 Opacity of dropdown and popup menu. 176 ''; 177 }; 178 179 wintypes = mkOption { 180 type = types.attrs; 181 default = { popup_menu = { opacity = cfg.menuOpacity; }; dropdown_menu = { opacity = cfg.menuOpacity; }; }; 182 example = {}; 183 description = '' 184 Rules for specific window types. 185 ''; 186 }; 187 188 opacityRules = mkOption { 189 type = types.listOf types.str; 190 default = []; 191 example = [ 192 "95:class_g = 'URxvt' && !_NET_WM_STATE@:32a" 193 "0:_NET_WM_STATE@:32a *= '_NET_WM_STATE_HIDDEN'" 194 ]; 195 description = '' 196 Rules that control the opacity of windows, in format PERCENT:PATTERN. 197 ''; 198 }; 199 200 backend = mkOption { 201 type = types.enum [ "glx" "xrender" "xr_glx_hybrid" ]; 202 default = "xrender"; 203 description = '' 204 Backend to use: <literal>glx</literal>, <literal>xrender</literal> or <literal>xr_glx_hybrid</literal>. 205 ''; 206 }; 207 208 vSync = mkOption { 209 type = with types; either bool 210 (enum [ "none" "drm" "opengl" "opengl-oml" "opengl-swc" "opengl-mswc" ]); 211 default = false; 212 apply = x: 213 let 214 res = x != "none"; 215 msg = "The type of services.picom.vSync has changed to bool:" 216 + " interpreting ${x} as ${boolToString res}"; 217 in 218 if isBool x then x 219 else warn msg res; 220 221 description = '' 222 Enable vertical synchronization. Chooses the best method 223 (drm, opengl, opengl-oml, opengl-swc, opengl-mswc) automatically. 224 The bool value should be used, the others are just for backwards compatibility. 225 ''; 226 }; 227 228 refreshRate = mkOption { 229 type = types.ints.unsigned; 230 default = 0; 231 example = 60; 232 description = '' 233 Screen refresh rate (0 = automatically detect). 234 ''; 235 }; 236 237 settings = with types; 238 let 239 scalar = oneOf [ bool int float str ] 240 // { description = "scalar types"; }; 241 242 libConfig = oneOf [ scalar (listOf libConfig) (attrsOf libConfig) ] 243 // { description = "libconfig type"; }; 244 245 topLevel = attrsOf libConfig 246 // { description = '' 247 libconfig configuration. The format consists of an attributes 248 set (called a group) of settings. Each setting can be a scalar type 249 (boolean, integer, floating point number or string), a list of 250 scalars or a group itself 251 ''; 252 }; 253 254 in mkOption { 255 type = topLevel; 256 default = { }; 257 example = literalExample '' 258 blur = 259 { method = "gaussian"; 260 size = 10; 261 deviation = 5.0; 262 }; 263 ''; 264 description = '' 265 Picom settings. Use this option to configure Picom settings not exposed 266 in a NixOS option or to bypass one. For the available options see the 267 CONFIGURATION FILES section at <literal>picom(1)</literal>. 268 ''; 269 }; 270 }; 271 272 config = mkIf cfg.enable { 273 services.picom.settings = mkDefaultAttrs { 274 # fading 275 fading = cfg.fade; 276 fade-delta = cfg.fadeDelta; 277 fade-in-step = elemAt cfg.fadeSteps 0; 278 fade-out-step = elemAt cfg.fadeSteps 1; 279 fade-exclude = cfg.fadeExclude; 280 281 # shadows 282 shadow = cfg.shadow; 283 shadow-offset-x = elemAt cfg.shadowOffsets 0; 284 shadow-offset-y = elemAt cfg.shadowOffsets 1; 285 shadow-opacity = cfg.shadowOpacity; 286 shadow-exclude = cfg.shadowExclude; 287 288 # opacity 289 active-opacity = cfg.activeOpacity; 290 inactive-opacity = cfg.inactiveOpacity; 291 292 wintypes = cfg.wintypes; 293 294 opacity-rule = cfg.opacityRules; 295 296 # other options 297 backend = cfg.backend; 298 vsync = cfg.vSync; 299 refresh-rate = cfg.refreshRate; 300 }; 301 302 systemd.user.services.picom = { 303 description = "Picom composite manager"; 304 wantedBy = [ "graphical-session.target" ]; 305 partOf = [ "graphical-session.target" ]; 306 307 # Temporarily fixes corrupt colours with Mesa 18 308 environment = mkIf (cfg.backend == "glx") { 309 allow_rgb10_configs = "false"; 310 }; 311 312 serviceConfig = { 313 ExecStart = "${pkgs.picom}/bin/picom --config ${configFile}" 314 + (optionalString cfg.experimentalBackends " --experimental-backends"); 315 RestartSec = 3; 316 Restart = "always"; 317 }; 318 }; 319 320 environment.systemPackages = [ pkgs.picom ]; 321 }; 322 323 meta.maintainers = with lib.maintainers; [ rnhmjoj ]; 324 325}