Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Notifications
Mark all as read
Q&A

Bind brightness/volume and other special Fn keys in a tty

+2
−0

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.

Why does this post require moderator attention?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

1 answer

You are accessing this answer with a direct link, so it's being shown above all other answers regardless of its score. You can return to the normal view.

+2
−0

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.

Why does this post require moderator attention?
You might want to add some details to your flag.

0 comment threads

Sign up to answer this question »

This community is part of the Codidact network. We have other communities too — take a look!

You can also join us in chat!

Want to advertise this community? Use our templates!

Like what we're doing? Support us! Donate