summary refs log tree commit diff
path: root/nixos/modules/services/x11/picom.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services/x11/picom.nix')
-rw-r--r--nixos/modules/services/x11/picom.nix335
1 files changed, 335 insertions, 0 deletions
diff --git a/nixos/modules/services/x11/picom.nix b/nixos/modules/services/x11/picom.nix
new file mode 100644
index 00000000000..b40e20bcd35
--- /dev/null
+++ b/nixos/modules/services/x11/picom.nix
@@ -0,0 +1,335 @@
+{ config, lib, options, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.picom;
+  opt = options.services.picom;
+
+  pairOf = x: with types;
+    addCheck (listOf x) (y: length y == 2)
+    // { description = "pair of ${x.description}"; };
+
+  floatBetween = a: b: with types;
+    let
+      # toString prints floats with hardcoded high precision
+      floatToString = f: builtins.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 = pkgs.writeText "picom.conf" (toConf cfg.settings);
+
+in {
+
+  imports = [
+    (mkAliasOptionModule [ "services" "compton" ] [ "services" "picom" ])
+  ];
+
+  options.services.picom = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether or not to enable Picom as the X.org composite manager.
+      '';
+    };
+
+    experimentalBackends = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to use the unstable new reimplementation of the backends.
+      '';
+    };
+
+    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 (floatBetween 0.01 1);
+      default = [ 0.028 0.03 ];
+      example = [ 0.04 0.04 ];
+      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 <literal>picom(1)</literal> 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 = floatBetween 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 <literal>picom(1)</literal> man page for more examples.
+      '';
+    };
+
+    activeOpacity = mkOption {
+      type = floatBetween 0 1;
+      default = 1.0;
+      example = 0.8;
+      description = ''
+        Opacity of active windows.
+      '';
+    };
+
+    inactiveOpacity = mkOption {
+      type = floatBetween 0.1 1;
+      default = 1.0;
+      example = 0.8;
+      description = ''
+        Opacity of inactive windows.
+      '';
+    };
+
+    menuOpacity = mkOption {
+      type = floatBetween 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 [ "glx" "xrender" "xr_glx_hybrid" ];
+      default = "xrender";
+      description = ''
+        Backend to use: <literal>glx</literal>, <literal>xrender</literal> or <literal>xr_glx_hybrid</literal>.
+      '';
+    };
+
+    vSync = mkOption {
+      type = with types; either bool
+        (enum [ "none" "drm" "opengl" "opengl-oml" "opengl-swc" "opengl-mswc" ]);
+      default = false;
+      apply = x:
+        let
+          res = x != "none";
+          msg = "The type of services.picom.vSync has changed to bool:"
+                + " interpreting ${x} as ${boolToString res}";
+        in
+          if isBool x then x
+          else warn msg res;
+
+      description = ''
+        Enable vertical synchronization. Chooses the best method
+        (drm, opengl, opengl-oml, opengl-swc, opengl-mswc) automatically.
+        The bool value should be used, the others are just for backwards compatibility.
+      '';
+    };
+
+    refreshRate = mkOption {
+      type = types.ints.unsigned;
+      default = 0;
+      example = 60;
+      description = ''
+       Screen refresh rate (0 = automatically detect).
+      '';
+    };
+
+    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 <literal>picom(1)</literal>.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    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;
+      refresh-rate     = cfg.refreshRate;
+    };
+
+    systemd.user.services.picom = {
+      description = "Picom composite manager";
+      wantedBy = [ "graphical-session.target" ];
+      partOf = [ "graphical-session.target" ];
+
+      # Temporarily fixes corrupt colours with Mesa 18
+      environment = mkIf (cfg.backend == "glx") {
+        allow_rgb10_configs = "false";
+      };
+
+      serviceConfig = {
+        ExecStart = "${pkgs.picom}/bin/picom --config ${configFile}"
+          + (optionalString cfg.experimentalBackends " --experimental-backends");
+        RestartSec = 3;
+        Restart = "always";
+      };
+    };
+
+    environment.systemPackages = [ pkgs.picom ];
+  };
+
+  meta.maintainers = with lib.maintainers; [ rnhmjoj ];
+
+}