mirror of
https://github.com/nix-community/home-manager.git
synced 2026-06-05 21:02:51 +00:00
`literalExpression` is intended just to signify code that needs to stay a string that gets represented exactly as-is for docs. It has been misused heavily and people get confused repeatedly on when or not to use it because of the rampant misuse.
380 lines
9.2 KiB
Nix
380 lines
9.2 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
let
|
|
inherit (lib)
|
|
concatStringsSep
|
|
literalExpression
|
|
mkIf
|
|
mkOption
|
|
optionalString
|
|
types
|
|
;
|
|
|
|
cfg = config.services.kanshi;
|
|
|
|
directivesTag = types.attrTag {
|
|
profile = mkOption {
|
|
type = profileModule;
|
|
description = ''
|
|
profile attribute set.
|
|
'';
|
|
};
|
|
output = mkOption {
|
|
type = outputModule;
|
|
description = ''
|
|
output attribute set.
|
|
'';
|
|
};
|
|
include = mkOption {
|
|
type = types.str;
|
|
description = ''
|
|
Include as another file from _path_.
|
|
Expands shell syntax (see *wordexp*(3) for details).
|
|
'';
|
|
};
|
|
};
|
|
|
|
tagToStr =
|
|
x:
|
|
if x ? profile then
|
|
profileStr x.profile
|
|
else if x ? output then
|
|
outputStr x.output
|
|
else if x ? include then
|
|
''include "${x.include}"''
|
|
else
|
|
throw "Unknown tags ${lib.attrNames x}";
|
|
|
|
directivesStr = concatStringsSep "\n" (map tagToStr cfg.settings);
|
|
|
|
oldDirectivesStr = ''
|
|
${concatStringsSep "\n" (lib.mapAttrsToList (n: v: profileStr (v // { name = n; })) cfg.profiles)}
|
|
${cfg.extraConfig}
|
|
'';
|
|
|
|
outputModule = types.submodule {
|
|
options = {
|
|
|
|
criteria = mkOption {
|
|
type = types.str;
|
|
description = ''
|
|
The criteria can either be an output name, an output description or "*".
|
|
The latter can be used to match any output.
|
|
|
|
On
|
|
{manpage}`sway(1)`,
|
|
output names and descriptions can be obtained via
|
|
`swaymsg -t get_outputs`.
|
|
'';
|
|
};
|
|
|
|
status = mkOption {
|
|
type = types.nullOr (
|
|
types.enum [
|
|
"enable"
|
|
"disable"
|
|
]
|
|
);
|
|
default = null;
|
|
description = ''
|
|
Enables or disables the specified output.
|
|
'';
|
|
};
|
|
|
|
mode = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
example = "1920x1080@60Hz";
|
|
description = ''
|
|
<width>x<height>[@<rate>[Hz]]
|
|
|
|
Configures the specified output to use the specified mode.
|
|
Modes are a combination of width and height (in pixels) and
|
|
a refresh rate (in Hz) that your display can be configured to use.
|
|
'';
|
|
};
|
|
|
|
position = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
example = "1600,0";
|
|
description = ''
|
|
<x>,<y>
|
|
|
|
Places the output at the specified position in the global coordinates
|
|
space.
|
|
'';
|
|
};
|
|
|
|
scale = mkOption {
|
|
type = types.nullOr types.float;
|
|
default = null;
|
|
example = 2;
|
|
description = ''
|
|
Scales the output by the specified scale factor.
|
|
'';
|
|
};
|
|
|
|
transform = mkOption {
|
|
type = types.nullOr (
|
|
types.enum [
|
|
"normal"
|
|
"90"
|
|
"180"
|
|
"270"
|
|
"flipped"
|
|
"flipped-90"
|
|
"flipped-180"
|
|
"flipped-270"
|
|
]
|
|
);
|
|
default = null;
|
|
description = ''
|
|
Sets the output transform.
|
|
'';
|
|
};
|
|
|
|
alias = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
example = "laptopMonitor";
|
|
description = ''
|
|
Defines an alias for the output
|
|
'';
|
|
};
|
|
|
|
adaptiveSync = mkOption {
|
|
type = types.nullOr types.bool;
|
|
default = null;
|
|
example = true;
|
|
description = ''
|
|
Enables or disables adaptive synchronization
|
|
(aka. Variable Refresh Rate).
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
outputStr =
|
|
{
|
|
criteria,
|
|
status,
|
|
mode,
|
|
position,
|
|
scale,
|
|
transform,
|
|
adaptiveSync,
|
|
alias,
|
|
...
|
|
}:
|
|
''output "${criteria}"''
|
|
+ optionalString (status != null) " ${status}"
|
|
+ optionalString (mode != null) " mode ${mode}"
|
|
+ optionalString (position != null) " position ${position}"
|
|
+ optionalString (scale != null) " scale ${toString scale}"
|
|
+ optionalString (transform != null) " transform ${transform}"
|
|
+ optionalString (adaptiveSync != null) " adaptive_sync ${if adaptiveSync then "on" else "off"}"
|
|
+ optionalString (alias != null) " alias \$${alias}";
|
|
|
|
profileModule = types.submodule {
|
|
options = {
|
|
outputs = mkOption {
|
|
type = types.listOf outputModule;
|
|
default = [ ];
|
|
description = ''
|
|
Outputs configuration.
|
|
'';
|
|
};
|
|
|
|
name = mkOption {
|
|
type = types.str;
|
|
default = "";
|
|
description = ''
|
|
Profile name
|
|
'';
|
|
};
|
|
|
|
exec = mkOption {
|
|
type = with types; coercedTo str lib.singleton (listOf str);
|
|
default = [ ];
|
|
example = "[ \${pkg.sway}/bin/swaymsg workspace 1, move workspace to eDP-1 ]";
|
|
description = ''
|
|
Commands executed after the profile is successfully applied.
|
|
Note that if you provide multiple commands, they will be
|
|
executed asynchronously with no guaranteed ordering.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
profileStr =
|
|
{ outputs, exec, ... }@args:
|
|
''
|
|
profile ${args.name or ""} {
|
|
${concatStringsSep "\n " (map outputStr outputs ++ map (cmd: "exec ${cmd}") exec)}
|
|
}
|
|
'';
|
|
in
|
|
{
|
|
|
|
meta.maintainers = [ ];
|
|
|
|
options.services.kanshi = {
|
|
enable = lib.mkEnableOption "kanshi, a Wayland daemon that automatically configures outputs";
|
|
|
|
package = lib.mkPackageOption pkgs "kanshi" { };
|
|
|
|
profiles = mkOption {
|
|
type = types.attrsOf profileModule;
|
|
default = { };
|
|
description = ''
|
|
Attribute set of profiles.
|
|
'';
|
|
example = {
|
|
undocked = {
|
|
outputs = [
|
|
{
|
|
criteria = "eDP-1";
|
|
}
|
|
];
|
|
};
|
|
docked = {
|
|
outputs = [
|
|
{
|
|
criteria = "eDP-1";
|
|
}
|
|
{
|
|
criteria = "Some Company ASDF 4242";
|
|
transform = "90";
|
|
}
|
|
];
|
|
};
|
|
};
|
|
};
|
|
|
|
extraConfig = mkOption {
|
|
type = types.lines;
|
|
default = "";
|
|
description = ''
|
|
Extra configuration lines to append to the kanshi
|
|
configuration file.
|
|
'';
|
|
};
|
|
|
|
settings = mkOption {
|
|
type = types.listOf directivesTag;
|
|
default = [ ];
|
|
description = ''
|
|
Ordered list of directives.
|
|
See kanshi(5) for information.
|
|
'';
|
|
example = literalExpression ''
|
|
[
|
|
{ include = "path/to/included/files"; }
|
|
{ output.criteria = "eDP-1";
|
|
output.scale = 2;
|
|
}
|
|
{ profile.name = "undocked";
|
|
profile.outputs = [
|
|
{
|
|
criteria = "eDP-1";
|
|
}
|
|
];
|
|
}
|
|
{ profile.name = "docked";
|
|
profile.outputs = [
|
|
{
|
|
criteria = "eDP-1";
|
|
}
|
|
{
|
|
criteria = "Some Company ASDF 4242";
|
|
transform = "90";
|
|
}
|
|
];
|
|
}
|
|
]
|
|
'';
|
|
};
|
|
|
|
systemdTarget = mkOption {
|
|
type = types.str;
|
|
default = config.wayland.systemd.target;
|
|
defaultText = literalExpression "config.wayland.systemd.target";
|
|
description = ''
|
|
Systemd target to bind to.
|
|
'';
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable (
|
|
lib.mkMerge [
|
|
{
|
|
assertions = [
|
|
(lib.hm.assertions.assertPlatform "services.kanshi" pkgs lib.platforms.linux)
|
|
{
|
|
assertion = (cfg.profiles == { } && cfg.extraConfig == "") || (lib.length cfg.settings) == 0;
|
|
message = "Cannot mix kanshi.settings with kanshi.profiles or kanshi.extraConfig";
|
|
}
|
|
{
|
|
assertion =
|
|
let
|
|
profiles = lib.filter (x: x ? profile) cfg.settings;
|
|
in
|
|
lib.length (lib.filter (x: lib.any (a: a ? alias && a.alias != null) x.profile.outputs) profiles)
|
|
== 0;
|
|
message = "Output kanshi.*.output.alias can only be defined on global scope";
|
|
}
|
|
];
|
|
}
|
|
|
|
(mkIf (cfg.profiles != { }) {
|
|
warnings = [
|
|
"kanshi.profiles option is deprecated. Use kanshi.settings instead."
|
|
];
|
|
})
|
|
|
|
(mkIf (cfg.extraConfig != "") {
|
|
warnings = [
|
|
"kanshi.extraConfig option is deprecated. Use kanshi.settings instead."
|
|
];
|
|
})
|
|
|
|
{
|
|
home.packages = [ cfg.package ];
|
|
|
|
xdg.configFile."kanshi/config" =
|
|
let
|
|
generatedConfigStr =
|
|
if cfg.profiles == { } && cfg.extraConfig == "" then directivesStr else oldDirectivesStr;
|
|
in
|
|
mkIf (generatedConfigStr != "") { text = generatedConfigStr; };
|
|
|
|
systemd.user.services.kanshi = {
|
|
Unit = {
|
|
Description = "Dynamic output configuration";
|
|
Documentation = "man:kanshi(1)";
|
|
ConditionEnvironment = "WAYLAND_DISPLAY";
|
|
PartOf = cfg.systemdTarget;
|
|
Requires = cfg.systemdTarget;
|
|
After = cfg.systemdTarget;
|
|
};
|
|
|
|
Service = {
|
|
Type = "simple";
|
|
ExecStart = "${cfg.package}/bin/kanshi";
|
|
Restart = "always";
|
|
};
|
|
|
|
Install = {
|
|
WantedBy = [ cfg.systemdTarget ];
|
|
};
|
|
};
|
|
}
|
|
]
|
|
);
|
|
}
|