Auto-Adjust Key Repeat Speed and Delay on Linux Using udevd


Contents:



If you're using Linux Mint 22 (Cinnamon edition) and an external keyboard, you may have encountered an issue where a custom key repeat delay and speed (see System Settings > Keyboard) reset to their defaults every time you reconnect the keyboard. While you can manually toggle the System Settings > Keyboard > Enable key repeat switch to apply the custom settings, doing this each time can be a hassle. Fortunately, there's a better way to fix this: using udevd, a userspace daemon that is notified by the kernel of device events and subsequently triggers a specified action. As udevd is part of systemd you can inspect its status via sudo systemctl status systemd-udevd.

In this case, we want to tell udevd to adjust the key repeat delay and speed whenever the keyboard is connected. To do this, we create a udev rule in /etc/udev/rules.d/. Note that the filename must end with .rules. The rule specifies

  • a trigger: the event to listen for
  • an action: the action to take when the event occurs


Step 1: Defining the Trigger

The following part of the rule tells udevd to execute the action iff a device with vendor ID 05ac and model ID 0250 is added (i.e. plugged in) to the usb bus:

ACTION=="add", SUBSYSTEM=="usb", ENV{ID_VENDOR_ID}=="05ac", ENV{ID_MODEL_ID}=="0250", # ... action ...

These IDs are specific to my keyboard, but finding them for another keyboard is quite simple. All you have to do is run sudo udevadm monitor --property and then reconnect your keyboard. This command outputs all events that udevd receives, including the above details. The following shows an excerpt of the output when I reconnected my keyboard:

# ...

KERNEL[35166.830490] remove   /devices/pci0000:00/0000:00:08.1/0000:c3:00.3/usb1/1-1/1-1.4/1-1.4:1.0/0003:05AC:0250.0023/input/input64/event15 (input)
ACTION=remove
DEVPATH=/devices/pci0000:00/0000:00:08.1/0000:c3:00.3/usb1/1-1/1-1.4/1-1.4:1.0/0003:05AC:0250.0023/input/input64/event15
SUBSYSTEM=input
DEVNAME=/dev/input/event15
SEQNUM=6655
MAJOR=13
MINOR=79

# ...

UDEV  [35172.162511] add      /devices/pci0000:00/0000:00:08.1/0000:c3:00.3/usb1/1-1/1-1.4 (usb)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:08.1/0000:c3:00.3/usb1/1-1/1-1.4
SUBSYSTEM=usb
DEVNAME=/dev/bus/usb/001/027
DEVTYPE=usb_device
PRODUCT=5ac/250/101
TYPE=0/0/0
BUSNUM=001
DEVNUM=027
SEQNUM=6681
USEC_INITIALIZED=35171931990
ID_BUS=usb
ID_MODEL=Keychron_K3
ID_MODEL_ENC=Keychron\x20K3
ID_MODEL_ID=0250
ID_SERIAL=Keychron_Keychron_K3
ID_VENDOR=Keychron
ID_VENDOR_ENC=Keychron
ID_VENDOR_ID=05ac

# ...

The first snippet was generated when I unplugged the keyboard. As you can see, this triggered an remove event in the input device subsystem. Similarly, when I plugged the keyboard in again, we see an add event in the usb subsystem. The vendor and model ID for the rule from above are extracted from here.

If your keyboard supports multiple connection types (e.g., USB and Bluetooth), you have to monitor both types and identify unique device identifers for each. In my case, I got the following details when connecting over Bluetooth:

# ...

UDEV  [35852.464109] add      /devices/pci0000:00/0000:00:08.1/0000:c3:00.3/usb1/1-3/1-3.1/1-3.1:1.0/bluetooth/hci0/hci0:1/0005:05AC:0250.0029/input/input70 (input)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:08.1/0000:c3:00.3/usb1/1-3/1-3.1/1-3.1:1.0/bluetooth/hci0/hci0:1/0005:05AC:0250.0029/input/input70
SUBSYSTEM=input
PRODUCT=5/5ac/250/101
NAME="Keychron K3"
PHYS="8c:3b:4a:a2:72:bc"
UNIQ="dc:2c:26:26:ba:91"

# ...

The UNIQ ID is suitable for use in the rule (you can put it into the same *.rules file as the USB rule):

ACTION=="add", SUBSYSTEM=="input", ENV{UNIQ}=="\"dc:2c:26:26:ba:91\"", # ... action ...


Step 2: Defining the Action

Now that we've defined the trigger in the rule, let's add the action. The following shows the complete *.rules file:

# /etc/udev/rules.d/adjust-keyboard-rate-delay.rules

# USB
ACTION=="add", SUBSYSTEM=="usb", ENV{ID_VENDOR_ID}=="05ac", ENV{ID_MODEL_ID}=="0250", RUN+="/usr/bin/su <your_name> -c /home/<your_name>/path/to/adjust-keyboard-rate-delay.sh"

# Bluetooth
ACTION=="add", SUBSYSTEM=="input", ENV{UNIQ}=="\"dc:2c:26:26:ba:91\"", RUN+="/usr/bin/su <your_name> -c /home/<your_name>/path/to/adjust-keyboard-rate-delay.sh"

It's just calling adjust-keyboard-rate-delay.sh (make sure to chmod +x the script) as a specific user; make sure to replace <your_name> with your username. Let's look at the script in more detail.

Assuming you use the X.Org Window System, /usr/bin/xset r rate <delay> <rate> can be used to set the delay and rate from the command line. The <delay> is given in milliseconds and the <rate> in the number of repeats per second. Check your current setting with xset -q | grep "auto repeat delay".

Unfortunately, we cannot just execute this command as is, as there's a bug (see here and here for details) that causes X.org to overwrite the key delay and rate just after udevd sets it. The (ugly but works) fix is to wait until X.org finished overwriting the settings (two seconds seems to be sufficient) and then do the adjustments:

#!/bin/sh

(
    sleep 2
    export XAUTORITY=/home/<your_name>/.Xauthority
    export DISPLAY=:0
    /usr/bin/xset r rate 230 30
) &

And that's it! The key repeat delay and rate should now be automatically adjusted to your custom settings each time the keyboard is connected.


Helpful links:


(OverTheWire Leviathan)