Linux Crap - Remapping mouse buttons under Wayland

Welcome to Linux Crap, where I post about Linux problems and how I end up solving them. Today, I’m going to be talking about remapping mouse buttons under Wayland using udev/hwdb.

The problem to solve

I have a Logitech M575 trackball wireless mouse. It’s great, but it has these two extra buttons that are mapped to Forward and Back that I never use. I’d like to remap them to PageUp and PageDown.

This seems like something very doable in Linux, but most of the guides for this revolve around Xorg and I’m living in present times, baby! How do we get this done under Wayland?

The answer is to do our remapping via hwdb, a part of udev. The manpages for this are a bit sparse and the tips and tricks to get this done are spread across forums, Reddit, StackOverflow, ArchWiki, and elsewhere. So let’s make a guide!

The guide

1. Finding the device

We need some info about the device in order to write our custom hwdb file that will contain our button remappings. This is trickier than it seems, given that we’re using a wireless mouse.

The output of lsusb only shows us the receiver and not the mouse itself.

<snip>
Bus 006 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 006 Device 002: ID 0bda:5411 Realtek Semiconductor Corp. RTS5411 Hub
Bus 006 Device 003: ID 05e3:0610 Genesys Logic, Inc. Hub
Bus 006 Device 004: ID 0bda:1100 Realtek Semiconductor Corp. HID Device
Bus 006 Device 005: ID 046d:c52b Logitech, Inc. Unifying Receiver
<snip>

We need to a different tool: evtest. Running this as root will scan all of the event devices and give us the info we need.

No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0:  Lid Switch
/dev/input/event1:  Sleep Button
/dev/input/event10: KBDFans TOFU60 Consumer Control
/dev/input/event11: KBDFans TOFU60 Keyboard
/dev/input/event12: ThinkPad Extra Buttons
/dev/input/event13: Logitech ERGO M575
/dev/input/event14: PC Speaker
/dev/input/event15: HD-Audio Generic HDMI/DP,pcm=3
/dev/input/event16: HD-Audio Generic HDMI/DP,pcm=7
/dev/input/event17: HD-Audio Generic HDMI/DP,pcm=8
/dev/input/event18: HD-Audio Generic HDMI/DP,pcm=9
/dev/input/event19: HD-Audio Generic Mic
/dev/input/event2:  Power Button
/dev/input/event20: HD-Audio Generic Headphone
/dev/input/event3:  AT Translated Set 2 keyboard
/dev/input/event4:  TPPS/2 Elan TrackPoint
/dev/input/event5:  ELAN06A0:00 04F3:3231 Touchpad
/dev/input/event6:  KBDFans TOFU60
/dev/input/event7:  Video Bus
/dev/input/event8:  KBDFans TOFU60 Mouse
/dev/input/event9:  KBDFans TOFU60 System Control
Select the device event number [0-20]:

There it is, at /dev/input/event13.

2. Getting the device information

Continuing in evtest, we’ll select event device 13. It will output some information and then monitor the events coming from the device (button presses and mouse movement).

Select the device event number [0-20]: 13
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x46d product 0x4096 version 0x111
Input device name: "Logitech ERGO M575"
Supported events:
<snip>
Testing ... (interrupt to exit)
Event: time 1764874139.592299, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90005
Event: time 1764874139.592299, type 1 (EV_KEY), code 276 (BTN_EXTRA), value 1
Event: time 1764874139.592299, -------------- SYN_REPORT ------------
Event: time 1764874139.728259, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90005
Event: time 1764874139.728259, type 1 (EV_KEY), code 276 (BTN_EXTRA), value 0
Event: time 1764874139.728259, -------------- SYN_REPORT ------------
Event: time 1764874141.086386, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90004
Event: time 1764874141.086386, type 1 (EV_KEY), code 275 (BTN_SIDE), value 1
Event: time 1764874141.086386, -------------- SYN_REPORT ------------
Event: time 1764874141.214986, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90004
Event: time 1764874141.214986, type 1 (EV_KEY), code 275 (BTN_SIDE), value 0
Event: time 1764874141.214986, -------------- SYN_REPORT ------------

Take note of the “Input device ID” and “Input device name”. We’ll use one of these to create our hwdb entry.

The events shown here are pressing and releasing the “forward” button, BTN_EXTRA, and the “back” button, BTN_SIDE. Note how each press also reports an MSC_SCAN with a value as well. This is the key scancode that we’ll use when remapping these keys in the hwdb file.

3. Writing the hwdb file

Create a new file, /etc/udev/hwdb.d/61-custom-keymaps.hwdb and we’ll use the output from evtest to create an entry for our device.

First, we’ll need to start a section with a device matcher. We can either use the device ID or the name. It’s up to you. I had trouble using both at the same time, however!

Matching on device ID

evdev:input:b0003v046Dp4096*

This uses the kernel modalias of the input device. You can see how it maps to the “Input device ID” that we got from evtest. The format is b<bus>v<vendor>p<product>e<version> with the values being 4-digit uppercase. We’re ending with an asterisk to match all versions of this device, in case of a hardware revision that might change the version value.

You can also get this straight from sysfs: cat /sys/class/input/event13/device/modalias.

Matching on the name

evdev:input:name:Logitech ERGO M575:*

Pretty straightforward.

Remapping the buttons

After our device matcher, we will indent one space and add our mapping directives.

evdev:input:b0003v046Dp4096*
 KEYBOARD_KEY_90005=pageup
 KEYBOARD_KEY_90005=pagedown

The directive is in the format KEYBOARD_KEY_<scancode>=<key name>.

The scancodes were found in Step 2 above.

You can find the key names in input-event-codes.h. The key names are downcased with the KEY_ prefix removed. For example, KEY_RIGHTSHIFT becomes rightshift. KEY_HOME becomes home. BTN_MIDDLE becomes btn_middle.

4. Applying the new configuration

Let’s regenerate the db and have udev pick up the changes.

As root, run systemd-hwdb -s update && udevadm trigger

Finally, unplug and re-plug your device.

5. Verifying

Check that the buttons are correctly mapped with good old evtest:

evtest /dev/input/event13

Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x46d product 0x4096 version 0x111
Input device name: "Logitech ERGO M575"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 104 (KEY_PAGEUP)
    Event code 109 (KEY_PAGEDOWN)
    Event code 272 (BTN_LEFT)
    Event code 273 (BTN_RIGHT)
    Event code 274 (BTN_MIDDLE)
    Event code 277 (BTN_FORWARD)
    Event code 278 (BTN_BACK)
    Event code 279 (BTN_TASK)
    Event code 280 (?)
    Event code 281 (?)
    Event code 282 (?)
    Event code 283 (?)
    Event code 284 (?)
    Event code 285 (?)
    Event code 286 (?)
    Event code 287 (?)
  Event type 2 (EV_REL)
    Event code 0 (REL_X)
    Event code 1 (REL_Y)
    Event code 6 (REL_HWHEEL)
    Event code 8 (REL_WHEEL)
    Event code 11 (REL_WHEEL_HI_RES)
    Event code 12 (REL_HWHEEL_HI_RES)
  Event type 4 (EV_MSC)
    Event code 4 (MSC_SCAN)
Properties:
Testing ... (interrupt to exit)
Event: time 1764880014.202213, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90005
Event: time 1764880014.202213, type 1 (EV_KEY), code 104 (KEY_PAGEUP), value 1
Event: time 1764880014.202213, -------------- SYN_REPORT ------------
Event: time 1764880014.355970, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90005
Event: time 1764880014.355970, type 1 (EV_KEY), code 104 (KEY_PAGEUP), value 0
Event: time 1764880014.355970, -------------- SYN_REPORT ------------
Event: time 1764880015.415987, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90004
Event: time 1764880015.415987, type 1 (EV_KEY), code 109 (KEY_PAGEDOWN), value 1
Event: time 1764880015.415987, -------------- SYN_REPORT ------------
Event: time 1764880015.583805, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90004
Event: time 1764880015.583805, type 1 (EV_KEY), code 109 (KEY_PAGEDOWN), value 0
Event: time 1764880015.583805, -------------- SYN_REPORT ------------

Tada!!!!!1 Now, scan code 90005 fires PageUp and 90004 fires PageDown. :)

Bonus: Enabling trackball scrolling in GNOME

Wouldn’t it be nice to hold a button down and scroll using the trackball? Well you can (in GNOME anyway). You just need to create a udev rule to identify your device as a trackball and make some changes with gsettings.

Making the udev rule

First, create a udev rule: /etc/udev/rules.d/51-trackball-mouse.rules

ACTION=="add|change", SUBSYSTEM=="input", ATTRS{name}=="Logitech ERGO M575", ENV{ID_INPUT_TRACKBALL}="1"

We’re using the name in this instance, but you could alternatively go with the device ID. We’re setting the variable ID_INPUT_TRACKBALL to 1, which allows for some special trackball-only options in GNOME.

Then, as root, run: udevadm control --reload; udevadm trigger

Unplug and re-plug your device.

You can verify that the variable is set by running udevadm info /dev/input/event13.

Changing the GNOME settings

Using the gsettings CLI tool, we will turn on scroll wheel emulation:

gsettings set org.gnome.desktop.peripherals.trackball scroll-wheel-emulation-button 2

The default value is 0, which disables the feature. I found that button 1 is left click, button 2 is the scroll wheel, and button 3 is right click. The maximum value is 24, so I wonder how that mapping works out.