Mouse Keys in Hyprland!

Who wants to use the mouse anyways?

Will ideally, no one should need to use the mouse, however “modern” software sometimes forces you use the cancer known as the mouse.

Cirno hates your mouse usage Cirno hates your mouse usage! (Picture by Tad Fibnonacci)

Step by step…

While you could scroll to the bottom and find the juicy copyable snippet, why not iterate together?

One can use ydotool to automate the mouse movements like so, note that I prefer vim’s hjkl input, and you might need to change your bindings, but the gist remains the same.

 bind  = $mainMod ctrl, h, exec, ydotool mousemove -- -40 0
 bind  = $mainMod ctrl, j, exec, ydotool mousemove -- 0 40
 bind  = $mainMod ctrl, k, exec, ydotool mousemove -- 0 -40
 bind  = $mainMod ctrl, l, exec, ydotool mousemove -- 40 0 
 bind  = $mainMod ctrl, u, exec, ydotool click 0xC0       # left  click
 bind  = $mainMod ctrl, i, exec, ydotool click 0xC2       # middle click 
 bind  = $mainMod ctrl, o, exec, ydotool click 0xC1       # right   click

Repetition when holding down the keys…

Now this works, but you need to constantly click to move the mouse, holding the button doesn’t repeatedly invoke the command.

This is easily solved by changing bind to bindl (refer to Hyprland documentation). We also don’t need the repetition with the clicks.

 bindl  = $mainMod ctrl, h, exec, ydotool mousemove -- -40 0
 bindl  = $mainMod ctrl, j, exec, ydotool mousemove -- 0 40
 bindl  = $mainMod ctrl, k, exec, ydotool mousemove -- 0 -40
 bindl  = $mainMod ctrl, l, exec, ydotool mousemove -- 40 0 
 bind  = $mainMod ctrl, u, exec, ydotool click 0xC0       # left  click
 bind  = $mainMod ctrl, i, exec, ydotool click 0xC2       # middle click 
 bind  = $mainMod ctrl, o, exec, ydotool click 0xC1       # right   click

Mouse Acceleration

Ok, this gets annoying to use after less than 5 seconds. Without acceleration this is unusable.

We can simply write a bash script to output a number (say 40) and increase the value i.e., accelerate, if we repetitively invoke the function. If we don’t invoke it, it will reset back to the baseline of 40.

$ bin/mouse_accel.sh
40

# wait between commands
$ bin/mouse_accel.sh; bin/mouse_accel.sh
40
60

# wait between commands
$ bin/mouse_accel.sh; bin/mouse_accel.sh; bin/mouse_accel.sh
40
60
80

# Note: Max speed is capped 200

I ChatGPT:

#!/bin/bash

STATE_FILE="/tmp/mouse_accel_state"
DEFAULT_SPEED=40
STEP=20
MAX_SPEED=200
TIME_THRESHOLD_MS=100

now_ms() {
    date +%s%3N  # current time in milliseconds
}

# Read previous state
if [[ -f "$STATE_FILE" ]]; then
    read -r last_time last_speed < "$STATE_FILE"
else
    last_time=0
    last_speed=$DEFAULT_SPEED
fi

current_time=$(now_ms)
delta=$((current_time - last_time))

if (( delta <= TIME_THRESHOLD_MS )); then
    # Accelerate
    new_speed=$((last_speed + STEP))
    [[ $new_speed -gt $MAX_SPEED ]] && new_speed=$MAX_SPEED
else
    # Reset
    new_speed=$DEFAULT_SPEED
fi

# Save new state
echo "$current_time $new_speed" > "$STATE_FILE"

# Output the new speed
echo "$new_speed"

And use it in my ydotool!

 bindl = $mainMod ctrl, h, exec, ydotool mousemove -- -$(~/bin/mouse_accel.sh) 0
 bindl = $mainMod ctrl, j, exec, ydotool mousemove -- 0 $(~/bin/mouse_accel.sh)
 bindl = $mainMod ctrl, k, exec, ydotool mousemove -- 0 -$(~/bin/mouse_accel.sh)
 bindl = $mainMod ctrl, l, exec, ydotool mousemove -- $(~/bin/mouse_accel.sh) 0 
 bind  = $mainMod ctrl, u, exec, ydotool click 0xC0       # left  click
 bind  = $mainMod ctrl, i, exec, ydotool click 0xC2       # middle click 
 bind  = $mainMod ctrl, o, exec, ydotool click 0xC1       # right   click

That’s all! Best of luck with your ricing :3