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: