mirror of
https://github.com/nix-community/home-manager.git
synced 2025-11-08 11:36:05 +01:00
Some files don't need nesting and can be root level again to reduce conflicts with other PRs. Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
381 lines
9.3 KiB
Nix
381 lines
9.3 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 = [ lib.hm.maintainers.nurelin ];
|
|
|
|
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 = literalExpression ''
|
|
{
|
|
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 informations.
|
|
'';
|
|
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 ];
|
|
};
|
|
};
|
|
}
|
|
]
|
|
);
|
|
}
|