Hi!
Using i3wm? Have a fancy keyboard with a nice CapsLock LED, but never actually press CapsLock (not as a remapped Ctrl)?
Me too! Let's convert it into a keyboard layout indicator then!
You can use method described below for things other than keyboard layout and for other LEDs. Happy hacking!
Requirements
-
i3
- targeted window manager, we will use its IPC to subscribe to window change event -
perl
withAnyEvent::I3
- to communicate with i3 IPC -
bash
withgrep
,sed
andxargs
utils -
brightnessctl
- to control LED -
xkb-switch
- to monitor keyboard layout change event -
i3-subscribe.pl
- perl script for i3 IPC (source) -
toggle_capslock_led.sh
- our bash script for toggling LED -
i3ipc_capslock_led.sh
- our bash script to start listening for window and layout changes
All scripts below are supposed to be put into ~/Scripts
directory - change this path if you need to.
General method
We have a bash script, which checks current keyboard layout and turns the LED on when specified layout is active.
We call this script:
- when the layout changes
- when we go to other window (as each window has its own keyboard layout state)
This is the script (toggle_capslock_led.sh
) with some comments inside:
#!/usr/bin/env bash
# Lights up capslock LED when specified layout is on.
# Works for LED on all connected keyboards (e.g. laptop's and external one).
# You can also set this to your specific input name, which can be found with: brightnessctl --list
# LED_NAMES="input7::capslock"
LED_NAMES=$(brightnessctl --list | grep '::capslock' | sed 's/Device .\(.*\). of.*/\1/g')
# The layout on which we want LED to be on.
# Get available layout names with: xkb-switch -l
ON_LAYOUT="us"
CURRENT_LAYOUT=$(xkb-switch -p)
VALUE=0
if [[ "$CURRENT_LAYOUT" == "$ON_LAYOUT" ]]; then
VALUE=1
fi
echo "$LED_NAMES" | while read name; do
brightnessctl --device="$name" set $VALUE > /dev/null
done
Talking to i3 IPC
Below is the i3-subscribe.pl
script for talking to i3 with perl.
To run it you will need to install AnyEvent::I3
for perl.
If you are on NixOS - leave script as it is (it uses nix-shell with perl540Packages.AnyEventI3).
If you aren't on NixOS - replace first two lines with this one: #!/usr/bin/env perl
.
#!/usr/bin/env nix-shell
#!nix-shell -i perl -p perl perl540Packages.AnyEventI3
# Subscribe to i3wm events via i3ipc.
# Source: https://faq.i3wm.org/question/5721/how-do-i-subscribe-to-i3-events-using-bash-easily.1.html
#
# Usage example for bash:
# i3subscribe window workspace | while read -r event; do
# ...
# done
BEGIN { $| = 1 } # flush \n
use strict;
use warnings;
use Data::Dumper;
use AnyEvent::I3;
use v5.10;
my $i3 = i3();
$i3->connect->recv or die "Error connecting to i3";
sub subscribe {
my $ev = $_[0];
my $dump = $_[1];
if($i3->subscribe({
$ev => sub {
my ($msg) = @_;
say "$ev:$msg->{'change'}";
if($dump) {
print Dumper($msg);
}
}
})->recv->{success}) {
say "Successfully subscribed to $ev-event";
}
}
my $nextArg = shift;
if(!$nextArg) {
say "Subscribe to i3-events";
say "Usage: $0 workspace|output|mode|window|barconfig_update|binding [dump]";
say "Example: $0 workspace dump window binding dump";
exit 1;
}
while($nextArg) {
my $arg = $nextArg;
$nextArg = shift;
my $dump = 0;
if($nextArg and $nextArg eq "dump") {
$dump = 1;
$nextArg = shift;
}
subscribe("$arg", $dump);
}
AE::cv->recv;
Toggle LED
This is i3ipc_capslock_led.sh
which starts needed listeners - add this script to autostart and that's it:
#!/usr/bin/env bash
# Executes `toggle_capslock_led.sh` script on window change.
# Source: https://faq.i3wm.org/question/5721/how-do-i-subscribe-to-i3-events-using-bash-easily.1.html
# Run LED switcher on layout change
xkb-switch -W | xargs -I{} ~/Scripts/toggle_capslock_led.sh &
# Run LED switcher on window or workspace change
~/Scripts/i3-subscribe.pl window workspace | while read -r event; do
~/Scripts/toggle_capslock_led.sh
done
Bye!