mirror of
https://github.com/nix-community/home-manager
synced 2024-11-22 12:43:14 +00:00
twmn: add module
This module allows to configure and start the twmn daemon.
This commit is contained in:
parent
11c0e5d188
commit
63dccc4e60
9 changed files with 483 additions and 0 deletions
3
.github/CODEOWNERS
vendored
3
.github/CODEOWNERS
vendored
|
@ -370,6 +370,9 @@
|
||||||
/modules/services/trayer.nix @AndreasMager
|
/modules/services/trayer.nix @AndreasMager
|
||||||
/tests/modules/services/trayer @AndreasMager
|
/tests/modules/services/trayer @AndreasMager
|
||||||
|
|
||||||
|
/modules/services/twmn.nix @Austreelis
|
||||||
|
/tests/modules/services/twmn @Austreelis
|
||||||
|
|
||||||
/modules/services/udiskie.nix @rycee
|
/modules/services/udiskie.nix @rycee
|
||||||
|
|
||||||
/modules/services/unison.nix @pacien
|
/modules/services/unison.nix @pacien
|
||||||
|
|
|
@ -13,6 +13,12 @@
|
||||||
github = "amesgen";
|
github = "amesgen";
|
||||||
githubId = 15369874;
|
githubId = 15369874;
|
||||||
};
|
};
|
||||||
|
austreelis = {
|
||||||
|
email = "github@accounts.austreelis.net";
|
||||||
|
github = "Austreelis";
|
||||||
|
githubId = 56743515;
|
||||||
|
name = "Morgane Austreelis";
|
||||||
|
};
|
||||||
CarlosLoboxyz = {
|
CarlosLoboxyz = {
|
||||||
name = "Carlos Lobo";
|
name = "Carlos Lobo";
|
||||||
email = "86011416+CarlosLoboxyz@users.noreply.github.com";
|
email = "86011416+CarlosLoboxyz@users.noreply.github.com";
|
||||||
|
|
|
@ -2395,6 +2395,14 @@ in
|
||||||
A new module is available: 'programs.kodi'.
|
A new module is available: 'programs.kodi'.
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
time = "2022-02-03T23:23:49+00:00";
|
||||||
|
condition = hostPlatform.isLinux;
|
||||||
|
message = ''
|
||||||
|
A new module is available: 'services.twmn'.
|
||||||
|
'';
|
||||||
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -236,6 +236,7 @@ let
|
||||||
./services/tahoe-lafs.nix
|
./services/tahoe-lafs.nix
|
||||||
./services/taskwarrior-sync.nix
|
./services/taskwarrior-sync.nix
|
||||||
./services/trayer.nix
|
./services/trayer.nix
|
||||||
|
./services/twmn.nix
|
||||||
./services/udiskie.nix
|
./services/udiskie.nix
|
||||||
./services/unclutter.nix
|
./services/unclutter.nix
|
||||||
./services/unison.nix
|
./services/unison.nix
|
||||||
|
|
380
modules/services/twmn.nix
Normal file
380
modules/services/twmn.nix
Normal file
|
@ -0,0 +1,380 @@
|
||||||
|
{ config, lib, pkgs, stdenv, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
|
||||||
|
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
|
||||||
|
<link xlink:href="https://doc.qt.io/qt-5/qeasingcurve.html#Type-enum">
|
||||||
|
QEasingCurve documentation</link>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
duration = mkOption {
|
||||||
|
type = types.ints.unsigned;
|
||||||
|
default = 1000;
|
||||||
|
example = 618;
|
||||||
|
description = "The animation duration in milliseconds.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
in {
|
||||||
|
meta.maintainers = [ 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
|
||||||
|
<link xlink:href="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. <literal>lightgray</literal>)
|
||||||
|
are supported.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
font = {
|
||||||
|
package = mkOption {
|
||||||
|
type = types.nullOr types.package;
|
||||||
|
default = null;
|
||||||
|
example = literalExpression "pkgs.dejavu_fonts";
|
||||||
|
description = ''
|
||||||
|
Notification text's font package. If <literal>null</literal> then
|
||||||
|
the font is assumed to already be available in your profile.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
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 <literal>null</literal>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
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.
|
||||||
|
<literal>lightgray</literal>) 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
|
||||||
|
<literal>top_center</literal> or <literal>bottom_center</literal>,
|
||||||
|
horizontally otherwise.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#################
|
||||||
|
# Implementation
|
||||||
|
|
||||||
|
config = 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 = 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 = 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}]
|
||||||
|
${concatStringsSep "\n" (mapAttrsToList mkLine conf)}
|
||||||
|
'';
|
||||||
|
in concatStringsSep "\n" (mapAttrsToList mkSection conf) + "\n";
|
||||||
|
|
||||||
|
systemd.user.services.twmnd = {
|
||||||
|
Unit = {
|
||||||
|
Description = "twmn daemon";
|
||||||
|
After = [ "graphical-session-pre.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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -156,6 +156,7 @@ import nmt {
|
||||||
./modules/services/sxhkd
|
./modules/services/sxhkd
|
||||||
./modules/services/syncthing
|
./modules/services/syncthing
|
||||||
./modules/services/trayer
|
./modules/services/trayer
|
||||||
|
./modules/services/twmn
|
||||||
./modules/services/window-managers/bspwm
|
./modules/services/window-managers/bspwm
|
||||||
./modules/services/window-managers/herbstluftwm
|
./modules/services/window-managers/herbstluftwm
|
||||||
./modules/services/window-managers/i3
|
./modules/services/window-managers/i3
|
||||||
|
|
32
tests/modules/services/twmn/basic-configuration.conf
Normal file
32
tests/modules/services/twmn/basic-configuration.conf
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
[gui]
|
||||||
|
always_on_top=true
|
||||||
|
background_color=black
|
||||||
|
bounce=true
|
||||||
|
bounce_duration=271
|
||||||
|
font=Noto Sans
|
||||||
|
font_size=16
|
||||||
|
font_variant=italic
|
||||||
|
foreground_color=#FF00FF
|
||||||
|
height=20
|
||||||
|
in_animation=27
|
||||||
|
in_animation_duration=314
|
||||||
|
max_length=80
|
||||||
|
offset_x=+20
|
||||||
|
offset_y=-60
|
||||||
|
opacity=80
|
||||||
|
out_animation=13
|
||||||
|
out_animation_duration=168
|
||||||
|
position=center
|
||||||
|
screen=0
|
||||||
|
|
||||||
|
[icons]
|
||||||
|
critical=/path/icon/critical
|
||||||
|
info=/path/icon/info
|
||||||
|
warning=/path/icon/warning
|
||||||
|
|
||||||
|
[main]
|
||||||
|
duration=4242
|
||||||
|
host=example.com
|
||||||
|
port=9006
|
||||||
|
sound_command=/path/sound/command
|
||||||
|
|
51
tests/modules/services/twmn/basic-configuration.nix
Normal file
51
tests/modules/services/twmn/basic-configuration.nix
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
{
|
||||||
|
config = {
|
||||||
|
services.twmn = {
|
||||||
|
enable = true;
|
||||||
|
duration = 4242;
|
||||||
|
host = "example.com";
|
||||||
|
port = 9006;
|
||||||
|
screen = 0;
|
||||||
|
soundCommand = "/path/sound/command";
|
||||||
|
icons.critical = "/path/icon/critical";
|
||||||
|
icons.info = "/path/icon/info";
|
||||||
|
icons.warning = "/path/icon/warning";
|
||||||
|
text = {
|
||||||
|
color = "#FF00FF";
|
||||||
|
font.family = "Noto Sans";
|
||||||
|
font.size = 16;
|
||||||
|
font.variant = "italic";
|
||||||
|
maxLength = 80;
|
||||||
|
};
|
||||||
|
window = {
|
||||||
|
alwaysOnTop = true;
|
||||||
|
color = "black";
|
||||||
|
height = 20;
|
||||||
|
offset.x = 20;
|
||||||
|
offset.y = -60;
|
||||||
|
opacity = 80;
|
||||||
|
position = "center";
|
||||||
|
animation = {
|
||||||
|
easeIn.curve = 27;
|
||||||
|
easeIn.duration = 314;
|
||||||
|
easeOut.curve = 13;
|
||||||
|
easeOut.duration = 168;
|
||||||
|
bounce.enable = true;
|
||||||
|
bounce.duration = 271;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
test.stubs.twmn = { };
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
serviceFile="home-files/.config/systemd/user/twmnd.service"
|
||||||
|
assertFileExists "$serviceFile"
|
||||||
|
assertFileRegex "$serviceFile" 'X-Restart-Triggers=.*twmn\.conf'
|
||||||
|
assertFileRegex "$serviceFile" 'ExecStart=@twmn@/bin/twmnd'
|
||||||
|
assertFileExists "home-files/.config/twmn/twmn.conf"
|
||||||
|
assertFileContent "home-files/.config/twmn/twmn.conf" \
|
||||||
|
${./basic-configuration.conf}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
1
tests/modules/services/twmn/default.nix
Normal file
1
tests/modules/services/twmn/default.nix
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{ twmn-basic-configuration = ./basic-configuration.nix; }
|
Loading…
Reference in a new issue