1
0
Fork 0
mirror of https://github.com/nix-community/home-manager.git synced 2025-11-08 11:36:05 +01:00
home-manager/modules/services/twmn.nix
Austin Horstman 7419250703
treewide: convert package options to use mkPackageOption (#7116)
This commit converts `package = mkOption` declarations throughout the
codebase to use the more modern and consistent `lib.mkPackageOption`
function.

Key changes:
- Simple package options: `mkOption { type = types.package; default = pkgs.foo; }`
  becomes `lib.mkPackageOption pkgs "foo" { }`
- Package set options: Uses correct package set as first argument with
  `pkgsText` parameter (e.g., `lib.mkPackageOption pkgs.vimPlugins "plugin" { pkgsText = "pkgs.vimPlugins"; }`)
- Removes redundant descriptions that just restate the package name
- Preserves examples and extra context where meaningful
- Handles submodule plugin options properly with `null` defaults

This modernizes the option declarations and makes them more consistent
with current nixpkgs patterns while maintaining full backward compatibility.

Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
2025-05-23 00:42:38 -05:00

379 lines
10 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
mkOption
mkEnableOption
types
literalExpression
;
cfg = config.services.twmn;
animationOpts = {
curve = mkOption {
type = types.ints.between 0 40;
default = 38;
example = 19;
description = ''
The qt easing-curve animation to use for the animation. See
[
QEasingCurve documentation](https://doc.qt.io/qt-5/qeasingcurve.html#Type-enum).
'';
};
duration = mkOption {
type = types.ints.unsigned;
default = 1000;
example = 618;
description = "The animation duration in milliseconds.";
};
};
in
{
meta.maintainers = [ lib.hm.maintainers.austreelis ];
options.services.twmn = {
enable = mkEnableOption "twmn, a tiling window manager notification daemon";
duration = mkOption {
type = types.ints.unsigned;
default = 3000;
example = 5000;
description = ''
The time each notification remains visible, in milliseconds.
'';
};
extraConfig = mkOption {
type = types.attrs;
default = { };
example = literalExpression ''{ main.activation_command = "\${pkgs.hello}/bin/hello"; }'';
description = ''
Extra configuration options to add to the twmnd config file. See
<https://github.com/sboli/twmn/blob/master/README.md>
for details.
'';
};
host = mkOption {
type = types.str;
default = "127.0.0.1";
example = "laptop.lan";
description = "Host address to listen on for notifications.";
};
icons = {
critical = mkOption {
type = types.nullOr types.path;
default = null;
description = "Path to the critical notifications' icon.";
};
info = mkOption {
type = types.nullOr types.path;
default = null;
description = "Path to the informative notifications' icon.";
};
warning = mkOption {
type = types.nullOr types.path;
default = null;
description = "Path to the warning notifications' icon.";
};
};
port = mkOption {
type = types.port;
default = 9797;
description = "UDP port to listen on for notifications.";
};
screen = mkOption {
type = types.nullOr types.int;
default = null;
example = 0;
description = ''
Screen number to display notifications on when using a multi-head
desktop.
'';
};
soundCommand = mkOption {
type = types.str;
default = "";
description = "Command to execute to play a notification's sound.";
};
text = {
color = mkOption {
type = types.str;
default = "#999999";
example = "lightgray";
description = ''
Notification's text color. RGB hex and keywords (e.g. `lightgray`)
are supported.
'';
};
font = {
package = lib.mkPackageOption pkgs "font" {
default = null;
example = "pkgs.dejavu_fonts";
nullable = true;
extraDescription = ''
Package providing the font to use for the notification text.
This package is only used if `font.package` is not null.
'';
};
family = mkOption {
type = types.str;
default = "Sans";
example = "Noto Sans";
description = "Notification text's font family.";
};
size = mkOption {
type = types.ints.unsigned;
default = 13;
example = 42;
description = "Notification text's font size.";
};
variant = mkOption {
# These are the font variant supported by twmn
# See https://github.com/sboli/twmn/blob/master/README.md?plain=1#L42
type = types.enum [
"oblique"
"italic"
"ultra-light"
"light"
"medium"
"semi-bold"
"bold"
"ultra-bold"
"heavy"
"ultra-condensed"
"extra-condensed"
"condensed"
"semi-condensed"
"semi-expanded"
"expanded"
"extra-expanded"
"ultra-expanded"
];
default = "medium";
example = "heavy";
description = "Notification text's font variant.";
};
};
maxLength = mkOption {
type = types.nullOr types.ints.unsigned;
default = null;
example = 80;
description = ''
Maximum length of the text before it is cut and suffixed with "...".
Never cuts if `null`.
'';
};
};
window = {
alwaysOnTop = mkEnableOption "forcing the notification window to always be on top";
animation = {
easeIn = mkOption {
type = types.submodule { options = animationOpts; };
default = { };
example = literalExpression ''
{
curve = 19;
duration = 618;
}
'';
description = "Options for the notification appearance's animation.";
};
easeOut = mkOption {
type = types.submodule { options = animationOpts; };
default = { };
example = literalExpression ''
{
curve = 19;
duration = 618;
}
'';
description = "Options for the notification disappearance's animation.";
};
bounce = {
enable = mkEnableOption "notification bounce when displaying next notification directly";
duration = mkOption {
type = types.ints.unsigned;
default = 500;
example = 618;
description = "The bounce animation duration in milliseconds.";
};
};
};
color = mkOption {
type = types.str;
default = "#000000";
example = "lightgray";
description = ''
Notification's background color. RGB hex and keywords (e.g.
`lightgray`) are supported.
'';
};
height = mkOption {
type = types.ints.unsigned;
default = 18;
example = 42;
description = ''
Height of the slide bar. Useful to match your tiling window
manager's bar.
'';
};
offset = {
x = mkOption {
type = types.int;
default = 0;
example = 50;
description = ''
Offset of the notification's slide starting point in pixels on the
horizontal axis (positive is rightward).
'';
};
y = mkOption {
type = types.int;
default = 0;
example = -100;
description = ''
Offset of the notification's slide starting point in pixels on the
vertical axis (positive is upward).
'';
};
};
opacity = mkOption {
type = types.ints.between 0 100;
default = 100;
example = 80;
description = "The notification window's opacity.";
};
position = mkOption {
type = types.enum [
"tr"
"top_right"
"tl"
"top_left"
"br"
"bottom_right"
"bl"
"bottom_left"
"tc"
"top_center"
"bc"
"bottom_center"
"c"
"center"
];
default = "top_right";
example = "bottom_left";
description = ''
Position of the notification slide. The notification will slide
in vertically from the border if placed in
`top_center` or `bottom_center`,
horizontally otherwise.
'';
};
};
};
#################
# Implementation
config = lib.mkIf cfg.enable {
assertions = [
(lib.hm.assertions.assertPlatform "services.twmn" pkgs lib.platforms.linux)
];
home.packages = lib.optional (!isNull cfg.text.font.package) cfg.text.font.package ++ [ pkgs.twmn ];
xdg.configFile."twmn/twmn.conf".text =
let
conf = lib.recursiveUpdate {
gui = {
always_on_top = if cfg.window.alwaysOnTop then "true" else "false";
background_color = cfg.window.color;
bounce = if cfg.window.animation.bounce.enable then "true" else "false";
bounce_duration = toString cfg.window.animation.bounce.duration;
font = cfg.text.font.family;
font_size = toString cfg.text.font.size;
font_variant = cfg.text.font.variant;
foreground_color = cfg.text.color;
height = toString cfg.window.height;
in_animation = toString cfg.window.animation.easeIn.curve;
in_animation_duration = toString cfg.window.animation.easeIn.duration;
max_length = toString (if isNull cfg.text.maxLength then -1 else cfg.text.maxLength);
offset_x = with cfg.window.offset; if x < 0 then toString x else "+${toString x}";
offset_y = with cfg.window.offset; if y < 0 then toString y else "+${toString y}";
opacity = toString cfg.window.opacity;
out_animation = toString cfg.window.animation.easeOut.curve;
out_animation_duration = toString cfg.window.animation.easeOut.duration;
position = cfg.window.position;
screen = toString cfg.screen;
};
# map null values to empty strings because formats.toml generator fails
# when encountering a null.
icons = lib.mapAttrs (_: toString) cfg.icons;
main = {
duration = toString cfg.duration;
host = cfg.host;
port = toString cfg.port;
sound_command = cfg.soundCommand;
};
} cfg.extraConfig;
mkLine = name: value: "${name}=${value}";
mkSection = section: conf: ''
[${section}]
${lib.concatStringsSep "\n" (lib.mapAttrsToList mkLine conf)}
'';
in
lib.concatStringsSep "\n" (lib.mapAttrsToList mkSection conf) + "\n";
systemd.user.services.twmnd = {
Unit = {
Description = "twmn daemon";
After = [ "graphical-session.target" ];
PartOf = [ "graphical-session.target" ];
X-Restart-Triggers = [ "${config.xdg.configFile."twmn/twmn.conf".source}" ];
};
Install.WantedBy = [ "graphical-session.target" ];
Service = {
ExecStart = "${pkgs.twmn}/bin/twmnd";
Restart = "on-failure";
Type = "simple";
StandardOutput = "null";
};
};
};
}