Disable internal keyboard when external keyboard is connected

Photo by Chris J. Davis / Unsplash

TL;DR;

Check the Github Gist I made for this post.

You should try put your HHKB on your laptop

I really do enjoy putting my HHKB on my laptop while I'm working with it, this habbit follows me when I was using my Macbook pro with butterfly keyboard. This keyboard feels good at first, however, lack of ESC and multiple issues made me can not stand it anymore. Some day I found out there's alot of people putting their HHKB on Macbook and it sit flush, moreover, because some magic coincidence design of HHKB, it will sit flush on Macbook pro without touching any keys. Soon I follow this, I have the most satisfying typing experience in my life. This habbits follows me for couple of years.

Time goes by, my Macbook pro can't catch up my workspace anymore, so I bought a Asus G14 2021 with top-spec at the time, full on Arch mode. Sadly when I try to put my HHKB on it keyboard doesn't fit on inbuilt keyboard, so if you going to put your HHKB on your laptop like on Macbook pro, you will have to disable built-in keyboard.

Let the scripting begins

At first I wrote a simple script manage to disable my built-in keyboard.

#!/bin/bash

# use $ xinput list to check your keyboard name
deviceName="Asus Keyboard"
# awk is the command to split return the string which is greped with command grep.
# In '{print $4 }', $n is the number of column of the string.
# You should be test it before put this into command down below.
builtInId=$(xinput list | grep $deviceName | awk '{ print $4 }' | grep -Eo '[0-9]{1,2}' | sort | head -n 1)

case $1 in
    add)  
      xinput disable $builtInId
      # dbus-launch is needed for send notification.
      dbus-launch notify-send "HHKB Connected..." \ "Internal keyboard disabled !";;
    remove)  
      xinput enable $builtInId
      dbus-launch notify-send "HHKB Disconnected..." \ "Internal keyboard enabled !";;
esac

This script is able to be run by direct execution, however I often found myself to run the command before I put my keyboard on the laptop. I start to search for an automation solution. There are not a single step-by-step tutorial for this kind of situation. After some digging on the internet I decide to put some work with udev.

What is udev?

According to well explaination on opensource.com:

Udev is the Linux subsystem that supplies your computer with device events.

In another word, we can make a script to run whatever we need it to, whether it's connected, disconnected or even state chaging. Consider it like a event trigger for external devices.

What we should do here is we are going to create a rules. First, user rules should located at /etc/udev/rules.d/. Second, file name should formate like xx-name.rules, xx present as number from 00 to 99, udev loads it's config with accending order by these two digits.

From Arch Wiki you can see how to make a simple rules. The concept is the rule have to pin-point the device. Normally ID_VENDOR_ID and ID_MODEL_ID will do the job, although there are some exceptions, so you should read through whole wiki before complaining the rule doesn't trigger.

The structure of the rule is quite simple:

# When HHKB is connected
ACTION=="add", SUBSYSTEM=="usb", ENV{XAUTHORITY}="/home/user/.Xauthority", ENV{DISPLAY}=":0", ATTRS{idVendor}=="0853", ATTRS{idProduct}=="0100", RUN+="/home/<username>/bin/hhkb.sh add"
# When HHKB is disconnected.
ACTION=="remove", SUBSYSTEM=="usb", ENV{XAUTHORITY}="/home/user/.Xauthority", ENV{DISPLAY}=":0", ATTRS{idVendor}=="0853", ATTRS{idProduct}=="0100", RUN+="/home/<username>/bin/hhkb.sh remove"

You can see I've add two more ENV parameters. Both of them are for dunst to display notification if script is triggered. When udev trigger a rule, it will run as root in non-Xorg environment, therefore we need to give udev current user's Xorg cookie (which is .Xauthority located inside ~ directory), let root access the user environment so it know what DISPLAY we are going to use (most of the case you can do echo $DISPLAY to check). You can see .Xauthority as a login cookie for current user, and DISPLAY as the monitor desire to display notification.

Ubuntu version above 10.10 seems remove .Xauthority from ~, try echo $XAUTHORITY to see if you can locate the cookie file.

There are some posts online says that you can also add these two environment variable inside your script, however I find myself it is more confortable to let it sit inside rules files since I'm the only one using this machine.

Script is ready, Rules are set, now what?

First of all, put your script into ~/bin or any directory inside your PATH, remember to chmod +x hhkb.sh, after that xx-hhkb-events.rules to /etc/udev/conf.d/. After two files are ready, run udevadm control --reload as root. Unplug your keyboard and see the magic happens!

mv xx-hhkb-events.rules /etc/udev/conf.d/
mv hhkb.sh ~/bin
chmod +x ~/bin/hhkb.sh

It worked! I wanna do some more automation!

There are tons of automation can be done with udev, such as battery level warning, temperature warning, even disable your trackpad when your mice / dungle is plugged in. All you need to do is mock the rules file above, and change the idVendor and idProduct to your desire device, write the script and you are good to go.

You can check all the attributes by using udevadm info --attribute-walk --path=<device>, and by using udevadm monitor --environment, unplug and see the environment value.

Sidenote

My laptop have two "Asus Keyboard" list inside xinput list, one is for function keys and one is for keyboard. Make sure you disable the right one or both of them, from my case I just disable the keyboard but not function keys is enough for me.

Bonus

If you are using HHKB and also like to use it's layout when using built-in keyboard, add setxkbmap -option (reset mapping) inside add case and setxkbmap -option ctrl:swapcaps,altwin:swap_lalt_lwin inside remove case.