diff --git a/hosts/common/programs/default.nix b/hosts/common/programs/default.nix index e09ee632..d4513008 100644 --- a/hosts/common/programs/default.nix +++ b/hosts/common/programs/default.nix @@ -13,6 +13,7 @@ ./element-desktop.nix ./epiphany.nix ./evince.nix + ./feedbackd.nix ./firefox.nix ./fontconfig.nix ./fractal.nix diff --git a/hosts/common/programs/feedbackd.nix b/hosts/common/programs/feedbackd.nix new file mode 100644 index 00000000..0572b75e --- /dev/null +++ b/hosts/common/programs/feedbackd.nix @@ -0,0 +1,68 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.sane.programs.feedbackd; +in +{ + sane.programs.feedbackd = { + package = pkgs.rmDbusServices pkgs.feedbackd; + + configOption = with lib; mkOption { + type = types.submodule { + options.proxied = mkOption { + type = types.bool; + default = false; + description = '' + whether to use a sound theme in which common application events are muted + with the intent that a proxy (notification daemon) with knowledge of this + modification will "speak" on behalf of all applications. + ''; + }; + }; + default = {}; + }; + + fs.".config/feedbackd/themes/proxied.json".symlink.text = builtins.toJSON { + name = "proxied"; + parent-theme = "default"; + profiles = [ + { + name = "full"; + feedbacks = [ + # forcibly disable normal events which we'd prefer for the notification daemon (e.g. swaync) to handle + { + event-name = "message-new-instant"; + type = "Dummy"; + } + { + event-name = "proxied-message-new-instant"; + type = "Sound"; + effect = "message-new-instant"; + } + ]; + } + ]; + }; + + services.feedbackd = { + description = "feedbackd audio/vibration/led controller"; + wantedBy = [ "default.target" ]; + serviceConfig = { + ExecStart = "${cfg.package}/libexec/feedbackd"; + Type = "simple"; + Restart = "on-failure"; + RestartSec = "10s"; + }; + environment = lib.mkIf cfg.config.proxied { + FEEDBACK_THEME = "/home/colin/.config/feedbackd/themes/proxied.json"; + }; + }; + }; + + services.udev.packages = lib.mkIf cfg.enabled [ + # ships udev rules for `feedbackd` group to be able to control vibrator and LEDs + cfg.package + ]; + users.groups = lib.mkIf cfg.enabled { + feedbackd = {}; + }; +} diff --git a/hosts/common/programs/swaynotificationcenter.nix b/hosts/common/programs/swaynotificationcenter.nix index dcae3baf..f179f4c3 100644 --- a/hosts/common/programs/swaynotificationcenter.nix +++ b/hosts/common/programs/swaynotificationcenter.nix @@ -15,6 +15,30 @@ # - thread: # - buttons-grid and menubar: { config, lib, pkgs, ... }: +let + cfg = config.sane.programs.swaynotificationcenter; + fbcli-wrapper = pkgs.writeShellApplication { + name = "swaync-fbcli"; + runtimeInputs = [ + config.sane.programs.feedbackd.package + cfg.package + ]; + text = '' + # if in Do Not Disturb, don't do any feedback + # TODO: better solution is to actually make use of feedbackd profiles. + # i.e. set profile to `quiet` when in DnD mode + if [ "$(swaync-client --get-dnd)" = "true" ]; then + exit + fi + + # feedbackd stops playback when the caller exits + # and fbcli will exit immediately if it has no stdin. + # so spoof a stdin: + true | fbcli "$@" + ''; + }; + fbcli = "${fbcli-wrapper}/bin/swaync-fbcli"; +in { sane.programs.swaynotificationcenter = { configOption = with lib; mkOption { @@ -33,6 +57,7 @@ }; # prevent dbus from automatically activating swaync so i can manage it as a systemd service instead package = pkgs.rmDbusServices pkgs.swaynotificationcenter; + suggestedPrograms = [ "feedbackd" ]; fs.".config/swaync/style.css".symlink.text = '' /* avoid black-on-black text that the default style ships */ window { @@ -70,15 +95,12 @@ hide-on-action = true; script-fail-notify = true; scripts = { - # example-script = { - # exec = "echo 'Do something...'"; - # urgency = "Normal"; - # }; - # example-action-script": { - # exec = "echo 'Do something actionable!'"; - # urgency = "Normal"; - # run-on = "action"; - # }; + sound-im = { + # trigger notification sound on behalf of these IM clients. + # TODO: dispatch calls separately! + exec = "${fbcli} --event proxied-message-new-instant"; + app-name = "(Element|discord|Dino)"; + }; }; notification-visibility = { # match incoming notifications and decide if they should be visible. @@ -122,7 +144,7 @@ widget-config = { backlight = { label = "󰃝 "; - device = config.sane.programs.swaynotificationcenter.config.backlight; + device = cfg.config.backlight; }; dnd = { text = "Do Not Disturb"; @@ -151,7 +173,7 @@ # seems that's not possible without defining an entire nix-native service (i.e. this). description = "Swaync desktop notification daemon"; wantedBy = [ "default.target" ]; - serviceConfig.ExecStart = "${config.sane.programs.swaynotificationcenter.package}/bin/swaync"; + serviceConfig.ExecStart = "${cfg.package}/bin/swaync"; serviceConfig.Type = "simple"; # serviceConfig.BusName = "org.freedesktop.Notifications"; serviceConfig.Restart = "on-failure"; @@ -159,4 +181,9 @@ environment.G_MESSAGES_DEBUG = "all"; }; }; + + 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; + }; } diff --git a/hosts/common/users/colin.nix b/hosts/common/users/colin.nix index 67da58fe..d864749b 100644 --- a/hosts/common/users/colin.nix +++ b/hosts/common/users/colin.nix @@ -17,7 +17,7 @@ extraGroups = [ "dialout" # required for modem access (moby) "export" # to read filesystem exports (servo) - "feedbackd" + "feedbackd" # moby, so `fbcli` can control vibrator and LEDs "input" # for /dev/input/: sxmo "media" # servo, for /var/lib/uninsane/media "networkmanager"