Bind brightness/volume and other special Fn keys in a tty
I am running Debian stretch on a Lenovo laptop with only a window manager (i.e. no desktop). Out of the box, this configuration of Debian ignores the keys with special Fn functions, such as volume up, mute microphone, brightness up, etc.
To solve the problem under X, I bound these special keys using xbindkeys
(in
.xsession) to various scripts that perform the desired actions, e.g. invoke
amixer to up the volume on the master channel, etc. This setup appears to run
my scripts in sub-shells and so far has worked fine.
The problem arises when I try to accomplish the same effect in a tty running
Bash. So far I have tried binding the special the keys to script invocations
using loadkeys
(in a systemd
service).
Here is an example of what I submit to loadkeys
:
plain keycode 114 = F110
string F110 = "my_volume_script_file_name some_parameters\n"
plain keycode 115 = F120
string F110 = "my_volume_script_file_name other_parameters\n"
plain keycode 113 = F130
string F110 = "my_volume_script_file_name yet_other_parameters\n"
When I press one of the mapped keys, this setup appears to submit my invocations to stdin of whatever is running in the tty's foreground. If it is an interactive shell, then everything works as expected (although seeing the command invocation on the screen is annoying). If anything else is running in the foreground, then of course the text submitted by the keystroke is not interpreted as a script invocation, and the desired effect does not happen.
I am wondering whethere there is anything I can bind to my keys that would cause Bash to interpret the text as a command to run in a sub-shell as opposed to submitting it to the foreground process' stdin. Heck, if there were a way to convince it to submit the text to a different file descriptor, I could probably do something useful with that too.
By the way, I looked at the Bash manual section on Readline, but found nothing applicable there.
My main goal, or course, is for special keys to work in a tty, regardless of what is running in the foreground. Are there other approaches anyone can think of?
This question is a slightly modified version of Edward Ross' at Unix & Linux.
1 answer
The special Fn keys typically trigger ACPI events, for which the ACPI daemon can trigger actions.
Install ACPI daemon (apt install acpid
) and make sure it is started and enabled. If it isn't,
systemctl start acpid
systemctl enable acpid
You can read its manual page and inspect files under /etc/acpi
to
easily figure it all out. But here is a summary.
Run acpi_listen
and press the special Fn keys. For brightness keys, volume keys
and reconnecting the AC adapter, I get
$ acpi_listen
676AA15E-6A47- 000000bc 00000000
video/brightnessdown BRTDN 00000087 00000000
676AA15E-6A47- 000000bc 00000000
video/brightnessup BRTUP 00000086 00000000
676AA15E-6A47- 000000bc 00000000
676AA15E-6A47- 000000bc 00000000
button/volumedown VOLDN 00000080 00000000 K
button/volumeup VOLUP 00000080 00000000 K
ac_adapter ACPI0003:00 00000080 00000000
battery PNP0C0A:00 00000080 00000001
ac_adapter ACPI0003:00 00000080 00000001
battery PNP0C0A:00 00000080 00000001
Ignore garbage lines, just take note of the relevant identifiers, and look inside
/etc/acpi/acpi_handler.sh
.
The set $*
statement turns each space delimited field into a positional
argument. I.e.,
button/volumeup VOLUP 00000080 00000000 K
^^^^^^^^^^^^^^^ ^^^^^ ^^^^^^^^ ^^^^^^^^ ^
$1 $2 $3 $4 $5
A working example:
if [ $# != 1 ]; then
exit 1
fi
set $*
case "$1" in
button/volumeup) amixer -q set Master 5%+ ;;
button/volumedown) amixer -q set Master 5%- ;;
video/brightnessup) xbacklight -inc 6 ;;
video/brightnessdown) xbacklight -dec 6 ;;
ac_adapter)
case "$4" in
*1) adapter_connected ;;
*0) adapter_disconnected ;;
esac
;;
esac
Then restart the daemon:
systemctl restart acpid
And the bindings will work.
See also: Arch Wiki: acpid.
0 comment threads