[Tutorial] Expanding the modifier keys

I use dual function modifier keys, tap for one thing, hold for the original function. So for example I tap left control to launch/hide my terminal. The left control is far easier to reach.

I have done this in the past with xcape, but this new method is more flexible.

Install the required AUR packages:

yay interception-dual-function-keys interception-tools

Create the service:

# /etc/systemd/system/udevmon.service

[Unit]
Description=udevmon
Wants=systemd-udev-settle.service
After=systemd-udev-settle.service

[Service]
ExecStart=/usr/bin/nice -n -20 /usr/bin/udevmon -c /etc/udevmon.yaml

[Install]
WantedBy=multi-user.target

Enable the service (it does nothing at the moment):

sudo systemctl enable --now udevmon

Create udev.yaml (referenced by the service):

# /etc/udevmon.yaml
- JOB: "intercept -g $DEVNODE | dual-function-keys -c /home/stevef/.dual-function-keys.yaml | uinput -d $DEVNODE"
  DEVICE:
    EVENTS:
      EV_KEY: [KEY_ENTER]

Please note the hardcoded path in the above file, needs to be edited.

Create the key definitions:

~/.dual-function-keys.yaml

TIMING:
  TAP_MILLISEC: 250
  DOUBLE_TAP_MILLISEC: 150

MAPPINGS:
  - KEY: KEY_ENTER
    TAP: KEY_ENTER
    HOLD: KEY_RIGHTCTRL

  - KEY: KEY_LEFTALT
    TAP: [KEY_LEFTMETA,KEY_W,]
    HOLD: KEY_LEFTALT

  - KEY: KEY_LEFTCTRL
    TAP: [KEY_LEFTMETA,KEY_T,]
    HOLD: KEY_LEFTCTRL

  - KEY: KEY_RIGHTCTRL
    TAP: KEY_ESC
    HOLD: KEY_RIGHTCTRL

  - KEY: KEY_CAPSLOCK
    TAP: KEY_ESC
    HOLD: KEY_LEFTCTRL

  - KEY: KEY_LEFTSHIFT
    TAP: [ KEY_LEFTSHIFT, KEY_9, ]
    HOLD: KEY_LEFTSHIFT

  - KEY: KEY_RIGHTSHIFT
    TAP: [ KEY_RIGHTSHIFT, KEY_0, ]
    HOLD: KEY_RIGHTSHIFT

  - KEY: KEY_LEFTMETA
    TAP: [ KEY_LEFTALT,KEY_F1, ]
    HOLD: KEY_LEFTMETA

  - KEY: KEY_RIGHTALT
    TAP: KEY_ESC  
    HOLD: KEY_HOME 

This is my file, which I have included to show what can be done. Taking the example of tap left control:

  - KEY: KEY_LEFTCTRL
    TAP: [KEY_LEFTMETA,KEY_T,]
    HOLD: KEY_LEFTCTRL

Tap generates super-t, hold is left control, so in my DE, I set the hotkey super-t to a script that launches/hides Alacritty.

The support script is:

#!/bin/bash
#Requires wmctrl + xdotool
NEEDED_WINDOW_CLASS="Alacritty.Alacritty"
LAUNCH_PROGRAM="alacritty -e tmux"
######################################################################################################
NEEDED_WINDOW_WINDOW_ID_HEX=`wmctrl -x -l | grep ${NEEDED_WINDOW_CLASS} | awk '{print $1}' | head -n 1`
NEEDED_WINDOW_WINDOW_ID_DEC=$((${NEEDED_WINDOW_WINDOW_ID_HEX}))
if [ -z "${NEEDED_WINDOW_WINDOW_ID_HEX}" ]; then
    ${LAUNCH_PROGRAM}
else
    echo "Found window ID:${NEEDED_WINDOW_WINDOW_ID_DEC}(0x${NEEDED_WINDOW_WINDOW_ID_HEX})"
    ACIVE_WINDOW_DEC=`xdotool getactivewindow`
    if [ "${ACIVE_WINDOW_DEC}" == "${NEEDED_WINDOW_WINDOW_ID_DEC}" ]; then
        xdotool windowminimize ${NEEDED_WINDOW_WINDOW_ID_DEC}
    else
        xdotool windowactivate ${NEEDED_WINDOW_WINDOW_ID_DEC}
    fi
fi

Requires wmctrl + xdotool to be installed and therefore does not work under wayland
Here are some of the windows classes for different terminals:

NEEDED_WINDOW_CLASS="terminator.Terminator"
NEEDED_WINDOW_CLASS="termite.Termite"
NEEDED_WINDOW_CLASS="kitty.kitty"
NEEDED_WINDOW_CLASS="sakura.Sakura"
NEEDED_WINDOW_CLASS="st.St"
NEEDED_WINDOW_CLASS="xst-256color.xst-256color"

xprop can be used to find the windows class:

xprop  | grep -i  class
WM_CLASS(STRING) = "konsole", "konsole"

Window class is konsole.konsole

Restart the service and see if it works:

sudo systemctl restart udevmon && systemctl status udevmon

Then tap the left control

I hope the above makes sense!!

Inspiration:

2 Likes

Some useful aliases:

alias ud+="sudo systemctl start udevmon"
alias ud++="ud- && sleep 2 && ud+ && systemctl status udevmon"
alias ud-="sudo systemctl stop udevmon"