nix-files/hosts/common/programs/swaynotificationcenter/default.nix

300 lines
12 KiB
Nix
Raw Normal View History

# <https://github.com/ErikReider/SwayNotificationCenter>
# sway notification daemon
# alternative to mako, dunst, etc
#
# debugging:
# - `journalctl --user -u swaync`
# - `G_MESSAGES_DEBUG=all swaync`
# - reveal notification center: `swaync-client -t -sw`
#
# configuration:
# - defaults: /run/current-system/etc/profiles/per-user/colin/etc/xdg/swaync/
# - `man 5 swaync`
# - view document tree: `GTK_DEBUG=interactive swaync` (`systemctl stop --user swaync` first)
# - examples:
# - thread: <https://github.com/ErikReider/SwayNotificationCenter/discussions/183>
# - buttons-grid and menubar: <https://gist.github.com/JannisPetschenka/fb00eec3efea9c7fff8c38a01ce5d507>
2023-09-11 23:03:57 +00:00
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.swaynotificationcenter;
systemctl-toggle = pkgs.writeShellApplication {
name = "systemctl-toggle";
runtimeInputs = [
pkgs.systemd
];
text = ''
if systemctl is-active "$@"; then
systemctl stop "$@"
else
systemctl start "$@"
fi
'';
};
printIsActive = pkgs.writeShellApplication {
name = "print-is-active";
runtimeInputs = [
pkgs.systemd
];
text = ''
if systemctl is-active "$@"; then
echo true
else
echo false
fi
'';
};
in
{
sane.programs.swaynotificationcenter = {
configOption = with lib; mkOption {
type = types.submodule {
options = {
backlight = mkOption {
type = types.str;
default = "intel_backlight";
description = ''
name of entry in /sys/class/backlight which indicates the primary backlight.
'';
};
};
};
default = {};
};
2023-09-11 23:03:57 +00:00
# prevent dbus from automatically activating swaync so i can manage it as a systemd service instead
packageUnwrapped = pkgs.rmDbusServices pkgs.swaynotificationcenter;
2024-02-15 16:38:38 +00:00
sandbox.method = "bwrap";
sandbox.whitelistAudio = true;
sandbox.whitelistDbus = [
"user" # mpris; portal
"system" # backlight
];
sandbox.whitelistWayland = true;
sandbox.extraPaths = [
"/sys/class/backlight"
"/sys/devices"
];
sandbox.extraRuntimePaths = [
# systemd/private allows one to `systemctl --user {status,start,stop,...}`
# notably, it does *not* allow for `systemd-run` (that's dbus: org.freedesktop.systemd1.Manager.StartTransientUnit).
# that doesn't necessarily mean this is entirely safe against privilege escalation though.
# TODO: audit the safety of this systemd sandboxing.
# few alternatives:
# - superd
# - simply `xdg-open app://dino`, etc. `pkill` to stop, `pgrep` to query.
# - more robust: `xdg-open sane-service://start?service=dino`
# - still need `pgrep` to query if it's running, or have the service mark a pid file
# - dbus activation for each app
"systemd/private"
];
sandbox.extraConfig = [
# systemctl calls seem to require same pid namespace
"--sane-sandbox-keep-namespace" "pid"
2024-02-15 16:38:38 +00:00
];
# glib/gio applications support many notification backends ("portal", "gtk", "freedesktop", ...).
# swaync implements only the `org.freedesktop.Notifications` dbus interface ("freedesktop"/fdo).
# however gio applications may be tricked into using one of the other backends, particularly
# if xdg-desktop-portal-gtk is installed and GIO_USE_PORTALS=1.
# so, explicitly specify the desired backend.
# the glib code which consumes this is `g_notification_backend_new_default`, calling into `_g_io_module_get_default_type`.
env.GNOTIFICATION_BACKEND = "freedesktop";
suggestedPrograms = [ "feedbackd" ];
fs.".config/swaync/style.css".symlink.target = ./style.css;
fs.".config/swaync/config.json".symlink.text = builtins.toJSON {
"$schema" = "/etc/xdg/swaync/configSchema.json";
positionX = "right";
positionY = "top";
layer = "overlay";
control-center-layer = "top";
layer-shell = true;
cssPriority = "user"; # "application"|"user". "user" in order to override the system gtk theme.
control-center-margin-top = 0;
control-center-margin-bottom = 0;
control-center-margin-right = 0;
control-center-margin-left = 0;
notification-2fa-action = true;
notification-inline-replies = false;
notification-icon-size = 64;
notification-body-image-height = 100;
notification-body-image-width = 200;
2023-09-06 09:20:00 +00:00
timeout = 30;
timeout-low = 5;
timeout-critical = 0;
fit-to-screen = true; #< have notification center take full vertical screen space
# control-center-width:
# pinephone native display is 720 x 1440
# - for compositor scale=2.0 => 360
# - for compositor scale=1.8 => 400
# - for compositor scale=1.6 => 450
# if it's set to something wider than the screen, then it overflows and items aren't visible.
control-center-width = 360;
control-center-height = 600;
notification-window-width = 360;
keyboard-shortcuts = true;
image-visibility = "when-available";
2023-09-06 09:20:00 +00:00
transition-time = 100;
hide-on-clear = true; #< hide control center when clicking "clear all"
hide-on-action = true;
script-fail-notify = true;
2024-03-24 08:22:53 +00:00
scripts = import ./scripts.nix {
inherit pkgs;
};
widgets = [
# what to show in the notification center (and in which order).
# these are configurable further via `widget-config`.
# besides these listed, there are general-purpose UI tools:
# - label (show some text)
# - buttons-grid (labels which trigger actions when clicked)
# - menubar (tree of labels/actions)
"title"
"dnd"
"inhibitors"
"buttons-grid"
"backlight"
"volume"
"mpris"
"notifications"
];
widget-config = {
backlight = {
label = "󰃝 ";
device = cfg.config.backlight;
};
buttons-grid = {
actions =
# {
# type = "toggle";
# label = "feedbackd";
# command = "${systemctl-toggle}/bin/systemctl-toggle --user feedbackd";
# active = "${pkgs.systemd}/bin/systemctl is-active --user feedbackd.service";
# }
lib.optionals config.sane.programs.eg25-control.enabled [
{
type = "toggle";
label = ""; # GPS services; other icons: gps, ⌖, 🛰, 🌎, 
command = "/run/wrappers/bin/sudo ${systemctl-toggle}/bin/systemctl-toggle eg25-control-gps";
update-command = "${printIsActive}/bin/print-is-active eg25-control-gps.service";
active = true;
}
{
type = "toggle";
label = "󰺐"; # icons: 5g, 📡, 📱, ᯤ, ⚡, , 🌐, 📶, 🗼, 󰀂, , 󰺐, 󰩯
# modem and NetworkManager auto-establishes a connection when powered.
# though some things like `wg-home` VPN tunnel will remain routed over the old interface.
command = "/run/wrappers/bin/sudo ${systemctl-toggle}/bin/systemctl-toggle eg25-control-powered";
update-command = "${printIsActive}/bin/print-is-active eg25-control-powered.service";
active = true;
}
2023-11-14 01:31:30 +00:00
] ++ lib.optionals false [
2023-09-19 16:25:17 +00:00
{
type = "toggle";
2023-11-14 01:31:30 +00:00
label = "vpn::hn"; # route all traffic through servo; useful to debug moby's networking
2023-09-19 16:25:17 +00:00
command = "/run/wrappers/bin/sudo ${systemctl-toggle}/bin/systemctl-toggle wg-quick-vpn-servo";
update-command = "${printIsActive}/bin/print-is-active wg-quick-vpn-servo.service";
active = true;
2023-09-19 16:25:17 +00:00
}
] ++ lib.optionals config.sane.programs.calls.config.autostart [
{
type = "toggle";
label = "SIP";
2023-09-22 23:10:50 +00:00
command = "${systemctl-toggle}/bin/systemctl-toggle --user gnome-calls";
update-command = "${printIsActive}/bin/print-is-active --user gnome-calls";
active = true;
}
] ++ lib.optionals config.sane.programs."gnome.geary".enabled [
{
type = "toggle";
label = "󰇮"; # email (Geary); other icons: ✉, [E], 📧, 󰇮
command = "${systemctl-toggle}/bin/systemctl-toggle --user geary";
update-command = "${printIsActive}/bin/print-is-active --user geary";
active = true;
}
# ] ++ lib.optionals config.sane.programs.abaddon.enabled [
2024-03-16 11:17:47 +00:00
# # XXX: disabled in favor of dissent: abaddon has troubles auto-connecting at start
# {
# type = "toggle";
# label = "󰊴"; # Discord chat client; icons: 󰊴, 🎮
# command = "${systemctl-toggle}/bin/systemctl-toggle --user abaddon";
# update-command = "${printIsActive}/bin/print-is-active --user abaddon";
# active = true;
# }
2024-03-16 11:17:47 +00:00
] ++ lib.optionals config.sane.programs.dissent.enabled [
{
type = "toggle";
label = "󰊴"; # Discord chat client; icons: 󰊴, 🎮
2024-03-16 11:17:47 +00:00
command = "${systemctl-toggle}/bin/systemctl-toggle --user dissent";
update-command = "${printIsActive}/bin/print-is-active --user dissent";
active = true;
}
] ++ lib.optionals config.sane.programs.signal-desktop.enabled [
{
type = "toggle";
label = "💬"; # Signal messenger; other icons: 󰍦
command = "${systemctl-toggle}/bin/systemctl-toggle --user signal-desktop";
update-command = "${printIsActive}/bin/print-is-active --user signal-desktop";
active = true;
}
] ++ lib.optionals config.sane.programs.dino.enabled [
{
type = "toggle";
2023-10-02 06:12:24 +00:00
label = "XMPP"; # XMPP calls (jingle)
2023-09-22 23:10:50 +00:00
command = "${systemctl-toggle}/bin/systemctl-toggle --user dino";
update-command = "${printIsActive}/bin/print-is-active --user dino";
active = true;
}
] ++ lib.optionals config.sane.programs.fractal.enabled [
2023-10-02 06:12:24 +00:00
{
type = "toggle";
label = "[m]"; # Matrix messages
2023-10-02 06:12:24 +00:00
command = "${systemctl-toggle}/bin/systemctl-toggle --user fractal";
update-command = "${printIsActive}/bin/print-is-active --user fractal";
active = true;
2023-10-02 06:12:24 +00:00
}
];
};
dnd = {
text = "Do Not Disturb";
};
inhibitors = {
text = "Inhibitors";
button-text = "Clear All";
clear-all-button = true;
};
mpris = {
image-size = 48;
image-radius = 8;
};
title = {
text = "Notifications";
clear-all-button = true;
button-text = "Clear All";
};
volume = {
label = " ";
};
};
};
services.swaync = {
# swaync ships its own service, but i want to add `environment` variables and flags for easier debugging.
# seems that's not possible without defining an entire nix-native service (i.e. this).
description = "swaynotificationcenter (swaync) desktop notification daemon";
depends = [ "sound" ]; #< TODO: else it will NEVER see the pulse socket in its sandbox
partOf = [ "graphical-session" ];
command = "env G_MESSAGES_DEBUG=all swaync";
readiness.waitDbus = "org.freedesktop.Notifications";
};
2023-09-07 07:34:22 +00:00
};
sane.programs.feedbackd.config = lib.mkIf cfg.enabled {
# claim control over feedbackd: we'll proxy the sounds we want on behalf of notifying programs
proxied = true;
};
}