Kensington Expert Trackball on Linux, bluetooth connected, remapped on udev binding event

I had to fix this bad boy properly under Linux and here's a quick guide how I did it.

Kensington Expert Trackball on Linux, bluetooth connected, remapped on udev binding event

Well yes, I had to find a shorter title for this post, but it is what it is. The Kensington Expert Trackball, once you try it you'll never come back to use those funky ergonomic and/or gaming mice which now I hated so much by my wrist.

And I use GNU/Linux. So, I gotta say goodbye to KensingtonWorks™, the customization software of my favorite input device, therefore, I had to deal with two problems:

  • Button remapping: the factory configuration is just not for me, I had to fix that.
  • Configuration volatility: I decided to use the Bluetooth connection instead of relying on the RF dongle connection. I realized that whenever you lose the connection (i.e. sending the laptop asleep) the configuration is lost. I had to trigger the remapping process once the connection is established again.

Button remapping

Thanks to ArtiomSu we have a solution that runs out of the box with the Kensington Expert Trackball on X11 and using the dongle. I'll go straight to the point. This is the mapping applied after launching the script (which makes use of xinput).

______________   _________  ________________
| back       |   |       |  | right click  |
--------------   |       |  ----------------
______________   |       |  ________________
| left click |   |       |  | middle click |
--------------   ---------  ----------------
Desired button mapping
#!/bin/bash

mouse_name="Kensington Expert Wireless TB Mouse"

check=$(xinput | grep "$mouse_name")

if [[ ! -z "$check" ]]; then
	mouse_id=$(xinput | grep "$mouse_name" | sed 's/^.*id=\([0-9]*\)[ \t].*$/\1/')
	# swap right and back button then swap middle and back button
	xinput set-button-map $mouse_id 1 8 2 4 5 6 7 3 9
	# enable better scrolling 
	xinput set-prop $mouse_id "libinput Natural Scrolling Enabled" 1
	# disable acceliration for the ball
	xinput set-prop $mouse_id "libinput Accel Profile Enabled" 0, 1

	# allow scrolling by holding middle mouse button and using the ball to scroll ( really smooth and fast ). 
	xinput set-prop $mouse_id "libinput Scroll Method Enabled" 0, 0, 1
	# allow the remmaped middle mouse to be used for middle mouse scroll
	xinput set-prop $mouse_id "libinput Button Scrolling Button" 3
fi
ArtiomSu's proposed script

So, the point here that's we have to do a quick adjustment, if you connect the device using bluetooth, the device identifier name is not "Kensington Expert Wireless TB Mouse" but it is "Expert Wireless TB Mouse". See xinput output:

Xinput output

'mkay, just change that, run the script, and if you're on X11 everything will go fine.

Stick with the remapping

First thing first, we have to find the event we want to use to run the mapping script. We will use udevadm for that, including the --environment flag to get also the preset device environment variables:

$ udevadm monitor --environment --udev

And we will go through a lot of udev add events, because our device is using several subsystems (input, bluetooth hid).

UDEV  [113687.919077] add      /devices/virtual/misc/uhid/0005:047D:8019.002E/hidraw/hidraw7 (hidraw)
ACTION=add
DEVPATH=/devices/virtual/misc/uhid/0005:047D:8019.002E/hidraw/hidraw7
SUBSYSTEM=hidraw
DEVNAME=/dev/hidraw7
SEQNUM=13101
USEC_INITIALIZED=113687918931
MAJOR=237
MINOR=7

UDEV  [113687.949323] add      /devices/virtual/misc/uhid/0005:047D:8019.002E/input/input142/event24 (input)
ACTION=add
DEVPATH=/devices/virtual/misc/uhid/0005:047D:8019.002E/input/input142/event24
SUBSYSTEM=input
DEVNAME=/dev/input/event24
SEQNUM=13097
USEC_INITIALIZED=113687949197
ID_INPUT=1
ID_INPUT_MOUSE=1
ID_BUS=bluetooth
ID_INPUT_TRACKBALL=1
MOUSE_DPI=400@125
LIBINPUT_DEVICE_GROUP=5/47d/8019:0c:96:e6:90:a7:24
MAJOR=13
MINOR=88

UDEV  [113687.952895] add      /devices/virtual/misc/uhid/0005:047D:8019.002E/input/input143/event25 (input)
ACTION=add
DEVPATH=/devices/virtual/misc/uhid/0005:047D:8019.002E/input/input143/event25
SUBSYSTEM=input
DEVNAME=/dev/input/event25
SEQNUM=13100
USEC_INITIALIZED=113687952723
ID_INPUT=1
ID_INPUT_KEY=1
ID_BUS=bluetooth
LIBINPUT_DEVICE_GROUP=5/47d/8019:0c:96:e6:90:a7:24
MAJOR=13
MINOR=89
TAGS=:power-switch:
CURRENT_TAGS=:power-switch:

UDEV  [113688.151725] bind     /devices/virtual/misc/uhid/0005:047D:8019.002E (hid)
ACTION=bind
DEVPATH=/devices/virtual/misc/uhid/0005:047D:8019.002E
SUBSYSTEM=hid
DRIVER=hid-generic
HID_ID=0005:0000047D:00008019
HID_NAME=Expert Wireless TB
HID_PHYS=0c:96:e6:90:a7:24
HID_UNIQ=fe:3c:3b:38:28:9f
MODALIAS=hid:b0005g0001v0000047Dp00008019
SEQNUM=13102
USEC_INITIALIZED=113687954002
Udevadm monitor output

see that second entry? We can investigate a specific device and walk back up to the chain on parent devices:

$ udevadm info -ap /devices/virtual/misc/uhid/0005:047D:8019.002E/input/input142/event24

Here's the output and look, there's our mouse:

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/virtual/misc/uhid/0005:047D:8019.002E/input/input142/event24':
    KERNEL=="event24"
    SUBSYSTEM=="input"
    DRIVER==""
    ATTR{power/control}=="auto"
    ATTR{power/runtime_active_time}=="0"
    ATTR{power/runtime_status}=="unsupported"
    ATTR{power/runtime_suspended_time}=="0"

  looking at parent device '/devices/virtual/misc/uhid/0005:047D:8019.002E/input/input142':
    KERNELS=="input142"
    SUBSYSTEMS=="input"
    DRIVERS==""
    ATTRS{capabilities/abs}=="0"
    ATTRS{capabilities/ev}=="17"
    ATTRS{capabilities/ff}=="0"
    ATTRS{capabilities/key}=="1f0000 0 0 0 0"
    ATTRS{capabilities/led}=="0"
    ATTRS{capabilities/msc}=="10"
    ATTRS{capabilities/rel}=="1943"
    ATTRS{capabilities/snd}=="0"
    ATTRS{capabilities/sw}=="0"
    ATTRS{id/bustype}=="0005"
    ATTRS{id/product}=="8019"
    ATTRS{id/vendor}=="047d"
    ATTRS{id/version}=="0001"
    ATTRS{inhibited}=="0"
    ATTRS{name}=="Expert Wireless TB Mouse"
    ATTRS{phys}=="0c:96:e6:90:a7:24"
    ATTRS{power/control}=="auto"
    ATTRS{power/runtime_active_time}=="0"
    ATTRS{power/runtime_status}=="unsupported"
    ATTRS{power/runtime_suspended_time}=="0"
    ATTRS{properties}=="0"
    ATTRS{uniq}=="fe:3c:3b:38:28:9f"

  looking at parent device '/devices/virtual/misc/uhid/0005:047D:8019.002E':
    KERNELS=="0005:047D:8019.002E"
    SUBSYSTEMS=="hid"
    DRIVERS=="hid-generic"
    ATTRS{country}=="00"
    ATTRS{power/control}=="auto"
    ATTRS{power/runtime_active_time}=="0"
    ATTRS{power/runtime_status}=="unsupported"
    ATTRS{power/runtime_suspended_time}=="0"

  looking at parent device '/devices/virtual/misc/uhid':
    KERNELS=="uhid"
    SUBSYSTEMS=="misc"
    DRIVERS==""
    ATTRS{power/control}=="auto"
    ATTRS{power/runtime_active_time}=="0"
    ATTRS{power/runtime_status}=="unsupported"
    ATTRS{power/runtime_suspended_time}=="0"
Udevadm info output

What matters at this point is the latest binding event, which is what makes our device ready to be used with the Operating System through the bluetooth stack:

UDEV  [113688.151725] bind     /devices/virtual/misc/uhid/0005:047D:8019.002E (hid)
ACTION=bind
DEVPATH=/devices/virtual/misc/uhid/0005:047D:8019.002E
SUBSYSTEM=hid
DRIVER=hid-generic
HID_ID=0005:0000047D:00008019
HID_NAME=Expert Wireless TB
HID_PHYS=0c:96:e6:90:a7:24
HID_UNIQ=fe:3c:3b:38:28:9f
MODALIAS=hid:b0005g0001v0000047Dp00008019
SEQNUM=13102
USEC_INITIALIZED=113687954002
Bind event in udev for the Kensington Expert Trackball

Time to write down the udev rule, but first, we need to modify again our script. Udev runs as root, so we have to tell the script to run the commands using the session of the user currently logged in, right? You have to add the env variables XAUTHORITY, DISPLAY and DBUS_SESSION_BUS_ADDRESS replacing the values YOUR_USER (mimnix) and YOUR_USER_UID (1000).

#!/bin/bash
export XAUTHORITY=/home/YOUR_USER/.Xauthority
export DISPLAY=:0
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/YOUR_USER_UID/bus"

mouse_name="Expert Wireless TB Mouse"

check=$(xinput | grep "$mouse_name")
notify-send "$mouse_name connected!"

if [[ ! -z "$check" ]]; then
        echo lol
        mouse_id=$(xinput | grep "$mouse_name" | sed 's/^.*id=\([0-9]*\)[ \t].*$/\1/')
        # swap right and back button then swap middle and back button
        xinput set-button-map $mouse_id 1 8 2 4 5 6 7 3 9
        # enable better scrolling 
        xinput set-prop $mouse_id "libinput Natural Scrolling Enabled" 1
        # disable acceliration for the ball
        xinput set-prop $mouse_id "libinput Accel Profile Enabled" 0, 1

        # allow scrolling by holding middle mouse button and using the ball to scroll ( really smooth and fast ). 
        xinput set-prop $mouse_id "libinput Scroll Method Enabled" 0, 0, 1
        # allow the remmaped middle mouse to be used for middle mouse scroll
        xinput set-prop $mouse_id "libinput Button Scrolling Button" 3
        xinput set-prop $mouse_id "libinput Middle Emulation Enabled" 1
#       xinput set-prop $mouse_id "libinput Drag Lock Buttons" 2
fi

To add some fashion, let's send a cool notification using notify-send. Save and quit, save the script as /usr/local/bin/Kensington_Expert_Setup.sh, chmod +x it.

Let's write the udev rule according to the binding event (remember to replace the YOUR_USER placeholder with your username):

ACTION=="bind", KERNEL=="0005:047D:8019.*", SUBSYSTEM=="hid", RUN+="/usr/bin/su YOUR_USER -c /usr/local/bin/Kensington_Expert_Setup.sh"
Udev rule

Save it on /etc/udev/rules.d/80-kensington_expert.rules

Run the following command to apply the changes to udev:

$ sudo udevadm control --reload

From now on, enjoy your productivity.

Next steps

I'm a wayland user... I have to abandon xinput and configure the same device using libinput. When I'm done you'll find another Fix'nChips article in this blog. >_