{ config, lib, pkgs, ... }: with lib; let cfg = config; speak = cfg.speak; in rec { options.speak = { enable = mkOption { type = types.bool; default = false; description = '' Enable NixOS speak module. ''; }; installPackages = mkOption { type = types.bool; default = true; description = '' Add spech related packages to environment. ''; }; user = mkOption { type = types.str; default = "root"; description = '' User which runs services like espeakup and speech-dispatcher. User needs to be in audio group. ''; }; config = { speakup = mkOption { type = types.attrs; default = {}; example = '' { synth = "soft"; punc_level = "0"; reading_punc = "0"; soft = { rate = "6"; }; } ''; }; }; }; config = mkIf speak.enable rec { boot.kernelModules = [ "speakup_soft" ]; nixpkgs.config.packageOverrides = { espeakup = pkgs.callPackage ../pkgs/espeakup.nix {}; }; # needed for orca services.gnome3.at-spi2-core.enable = mkDefault (cfg.services.xserver.enable); # allow users in the audio group to use and configure speakup_soft services.udev.extraRules = '' KERNEL=="synth", SUBSYSTEM=="misc", GROUP="audio", MODE="0660" KERNEL=="softsynth", SUBSYSTEM=="misc", GROUP="audio", MODE="0660" KERNEL=="softsynthu", SUBSYSTEM=="misc", GROUP="audio", MODE="0660" ''; # See https://aur.archlinux.org/cgit/aur.git/tree/espeakup.service?h=espeakup-git systemd.services = { speakup-configure = let # This whole thing still feels a little bit awkward pathList = collect (v: isList v) (mapAttrsRecursive (p: v: p) speak.config.speakup); mkCommand = p: v: "echo ${v} > /sys/accessibility/speakup/${p}"; commandList = (map (l: (mkCommand (concatStringsSep "/" l) (attrByPath l "0" speak.config.speakup))) pathList); in { enable = true; description = "configure speakup."; wantedBy = [ "default.target" ]; wants = [ "systemd-udev-settle.service" ]; after = [ "systemd-udev-settle.service" "sound.target" ]; script = (concatStringsSep "\n" commandList); serviceConfig.Type = "oneshot"; }; espeakup = { enable = (speak.user == "root"); description = "Software speech output system wide settings."; wantedBy = [ "default.target" ]; wants = [ "systemd-udev-settle.service" ]; after = [ "systemd-udev-settle.service" "sound.target" ]; serviceConfig = { Type = "forking"; ExecStart = "${pkgs.espeakup}/bin/espeakup"; ExecStop = "${pkgs.utillinux.bin}/bin/kill -HUP $MAINPID"; Restart= "always"; }; }; }; systemd.user.services.espeakup = { enable = (speak.user != "root"); description = "Software speech output system wide settings."; wantedBy = [ "default.target" ]; wants = [ "systemd-udev-settle.service" ]; after = [ "systemd-udev-settle.service" "sound.target" ]; serviceConfig = { Type = "forking"; ExecStart = "${pkgs.espeakup}/bin/espeakup"; ExecStop = "${pkgs.utillinux.bin}/bin/kill -HUP $MAINPID"; Restart= "always"; }; }; environment.systemPackages = optionals speak.installPackages (with pkgs; [ (lowPrio espeak-classic) espeak-ng espeakup ]); }; }