Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

alsa: allow 'sound.mediaKeys.enable' to work even when 'hardware.pulseaudio.enable' is used #67227

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
102 changes: 78 additions & 24 deletions nixos/modules/services/audio/alsa.nix
Expand Up @@ -4,10 +4,36 @@
with lib;

let

inherit (pkgs) alsaUtils;

pulseaudioEnabled = config.hardware.pulseaudio.enable;
inherit (pkgs) alsaUtils pulseaudio;

cfg = config.sound;
pulseCfg = config.hardware.pulseaudio;
pulseaudioEnabled = pulseCfg.enable;

volumeCmds =
let
inherit (cfg.mediaKeys) volumeStep;
amixer = "${alsaUtils}/bin/amixer";
pactl = "${pulseaudio}/bin/pactl";
in
{
pulseVolume =
{
up = "${pactl} set-sink-volume @DEFAULT_SINK@ +${volumeStep}";
down = "${pactl} set-sink-volume @DEFAULT_SINK@ -${volumeStep}";
mute = "${pactl} set-sink-mute @DEFAULT_SINK@ toggle";
micMute = "${pactl} set-source-mute @DEFAULT_SOURCE@ toggle";
userService = true;
};
alsaVolume =
{
up = "${amixer} -q set Master ${volumeStep}%+ unmute";
down = "${amixer} -q set Master ${volumeStep}%- unmute";
mute = "${amixer} -q set Master toggle";
micMute = "${amixer} -q set Capture toggle";
userService = false;
};
};

in

Expand Down Expand Up @@ -83,24 +109,26 @@ in

###### implementation

config = mkIf config.sound.enable {
config = mkIf cfg.enable {

environment.systemPackages = [ alsaUtils ];

environment.etc = mkIf (!pulseaudioEnabled && config.sound.extraConfig != "")
environment.etc = mkIf (!pulseaudioEnabled && cfg.extraConfig != "")
[
{ source = pkgs.writeText "asound.conf" config.sound.extraConfig;
{
source = pkgs.writeText "asound.conf" cfg.extraConfig;
target = "asound.conf";
}
];

# ALSA provides a udev rule for restoring volume settings.
services.udev.packages = [ alsaUtils ];

boot.kernelModules = optional config.sound.enableOSSEmulation "snd_pcm_oss";
boot.kernelModules = optional cfg.enableOSSEmulation "snd_pcm_oss";

systemd.services.alsa-store =
{ description = "Store Sound Card State";
{
description = "Store Sound Card State";
wantedBy = [ "multi-user.target" ];
unitConfig.RequiresMountsFor = "/var/lib/alsa";
unitConfig.ConditionVirtualization = "!systemd-nspawn";
Expand All @@ -112,23 +140,49 @@ in
};
};

services.actkbd = mkIf config.sound.mediaKeys.enable {
services.actkbd = mkIf cfg.mediaKeys.enable {
enable = true;
bindings = [
# "Mute" media key
{ keys = [ 113 ]; events = [ "key" ]; command = "${alsaUtils}/bin/amixer -q set Master toggle"; }

# "Lower Volume" media key
{ keys = [ 114 ]; events = [ "key" "rep" ]; command = "${alsaUtils}/bin/amixer -q set Master ${config.sound.mediaKeys.volumeStep}- unmute"; }

# "Raise Volume" media key
{ keys = [ 115 ]; events = [ "key" "rep" ]; command = "${alsaUtils}/bin/amixer -q set Master ${config.sound.mediaKeys.volumeStep}+ unmute"; }

# "Mic Mute" media key
{ keys = [ 190 ]; events = [ "key" ]; command = "${alsaUtils}/bin/amixer -q set Capture toggle"; }
];
bindings =
let
volume =
if pulseaudioEnabled
then volumeCmds.pulseVolume
else volumeCmds.alsaVolume;
in
[
# "Mute" media key
{
userMode = volume.userService;
keys = [ 113 ];
events = [ "key" ];
command = "${volume.mute}";
}

# "Mic Mute" media key
{
userMode = volume.userService;
keys = [ 190 ];
events = [ "key" ];
command = "${volume.micMute}";
}

# "Lower Volume" media key
{
userMode = volume.userService;
keys = [ 114 ];
events = [ "key" "rep" ];
command = "${volume.down}";
}

# "Raise Volume" media key
{
userMode = volume.userService;
keys = [ 115 ];
events = [ "key" "rep" ];
command = "${volume.up}";
}
];
};

};

}
76 changes: 60 additions & 16 deletions nixos/modules/services/hardware/actkbd.nix
Expand Up @@ -6,18 +6,47 @@ let

cfg = config.services.actkbd;

configFile = pkgs.writeText "actkbd.conf" ''
isUserBindings = bool:
builtins.filter (a: a.userMode == bool) cfg.bindings;

mkConfigFile = isUser:
pkgs.writeText "actkbd.conf" ''
${concatMapStringsSep "\n"
({ keys, events, attributes, command, ... }:
''${concatMapStringsSep "+" toString keys}:${concatStringsSep "," events}:${concatStringsSep "," attributes}:${command}''
)
cfg.bindings}
${cfg.extraConfig}
'';
( isUserBindings isUser )}
${cfg.extraConfig}'';

mkSystemdService = config:
{
enable = true;
restartIfChanged = true;
unitConfig = {
Description = "actkbd on %I";
ConditionPathExists = "%I";
};
serviceConfig = {
Type = "forking";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe forking could be changed to simple and -D removed from the ExecStart -line.

ExecStart = "${pkgs.actkbd}/bin/actkbd -D -c ${config} -d %I";
};
};

bindingCfg = { ... }: {
options = {

userMode = mkOption {
type = types.bool;
default = false;
description = ''
whether to deploy this binding as a systemd user service; useful for
some programs which require user privileges. This is rarely needed and
should only be enabled if it is absolutely required.

E.g. <option>sound.mediaKeys</option> enables this when <option>hardware.pulseaudio.enable</option> is set.
'';
};

keys = mkOption {
type = types.listOf types.int;
description = "List of keycodes to match.";
Expand Down Expand Up @@ -107,21 +136,36 @@ in
services.udev.packages = lib.singleton (pkgs.writeTextFile {
name = "actkbd-udev-rules";
destination = "/etc/udev/rules.d/61-actkbd.rules";
text = ''
ACTION=="add", SUBSYSTEM=="input", KERNEL=="event[0-9]*", ENV{ID_INPUT_KEY}=="1", TAG+="systemd", ENV{SYSTEMD_WANTS}+="actkbd@$env{DEVNAME}.service"
text = let actkbdVar = "actkbd@$env{DEVNAME}.service";
in ''
ACTION=="add" \
, SUBSYSTEM=="input" \
, KERNEL=="event[0-9]*" \
, ENV{ID_INPUT_KEY}=="1" \
, TAG+="systemd" \
, ENV{SYSTEMD_WANTS}+="${actkbdVar}" \
, ENV{SYSTEMD_USER_WANTS}+="${actkbdVar}"
'';
});

systemd.services."actkbd@" = {
enable = true;
restartIfChanged = true;
unitConfig = {
Description = "actkbd on %I";
ConditionPathExists = "%I";
};
serviceConfig = {
Type = "forking";
ExecStart = "${pkgs.actkbd}/bin/actkbd -D -c ${configFile} -d %I";
systemd.services = mkIf ( isUserBindings false != [] )
{ "actkbd@" = ( mkSystemdService (mkConfigFile false) );
};

systemd.user = mkIf ( isUserBindings true != [] )
{ services."actkbd@" = mkSystemdService (mkConfigFile true);

# this is needed because input devices initialize at boot and systemd
# only starts device dependant services on their initialization
services."start-actkbd" =
{ enable = true;
unitConfig =
{ Description = "user instances of actkbd";
Type = "idle";
};
script =
"${pkgs.systemd}/bin/systemctl --user start 'actkbd@*.service' --all";
wantedBy = [ "default.target" ];
};
};

Expand Down