Control USB mouse acceleratoin separately from touchpad speed

This is also a short udev tutorial.

I often work on my laptop, sometimes with a USB mouse plugged (in this case a Logitech G9). On the laptop’s touchpad I appreciate mouse acceleration, but on the USB mouse I cannot stand it. Is there an easy way to fix this in Linux, automatically? Depends – on whether you find udev easy to work with or not.

Brief overview of the steps below:

What will happen at the end:

  1. You plug in the mouse
  2. udev receives a «mouse plugged» event and looks for a suitable rule to execute
  3. Our rule tells udev to run a script that changes the mouse acceleration

Step 1: Testing without udev

At first it is important to ensure that the commands to change the mouse acceleration actually work. This is done with xinput (you may need to install it first) as described on askubuntu.com[1]. The following command sets the mouse acceleration to 1/1 which means no acceleration.

$ xinput set-ptr-feedback device-id 4 1 1

In my case the Logitech G9 reports with two entries in the xinput list, and changing only one of them does not have any effect. If your USB mouse reports with two entries as well, make sure you change the mouse acceleration for both of them too.

Mouse acceleration should be off now (or, more general, the way you want it – you can also change the mouse speed and various other parameters, see the xinput manpage for this). If it is still on, xset m 0 might help.

Step 2: Recognizing the USB mouse with udev

To get an idea of what kind of events udev receives, run

udevadm monitor

in a terminal and see what happens when you plug/unplug an USB mouse or USB stick, change the screen brightness (on laptops), disable/enable wireless devices (with the little switch you often find on laptops). (Monitoring can be stopped with Ctrl+C .)

udev can take actions when it receives an event, the most common one being to create a syslink in the /dev directory, used to always have the same device path to e.g. your personal USB stick. This is also useful for debugging. The action important in our case is running a custom command or script which will change the mouse acceleration.

Now we could just go on and create a udev rule that runs xinput – but this does not work out of the box. First, we need to write a script that gets the xinput id(s) for the mouse and runs xinput on them. Secondly, it has proven to be good practise to first check if the udev rule to match the device is correct in order to know, if a rule did not execute, whether it was a matching problem (wrong conditions to identify the device, so they did not match), a syntax error in the rule (like unterminated quotation marks), or a bug in the script.

Therefore the next small step is to let udev create a symbolic link in /dev to test the new rule. For this we have to find out a way to identify the device. The command udevadm info -a -n name provides the necessary information. The device name was /dev/usb/hiddev0 in my case, when unknown the path can be used as well; that’s the one printed in the terminal when having udevadm monitor running. (Not using the shortest entries proved to be a better choice since the parent device properties are listed as well.)

UDEV [438.300412] add /class/usb (class) UDEV [438.340618] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb) UDEV [438.342517] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.1 (usb) UDEV [438.345036] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb) UDEV [438.345649] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.1/0003:046D:C048.0004 (hid) UDEV [438.346857] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/0003:046D:C048.0003 (hid)

The information output then looks as follows (note the -p when using a path instead of a name for the device) – take the one where the product name appears:

$ udevadm info -a -p /devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.2 [...] looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2': KERNELS=="2-1.2" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{configuration}=="U50.00_B0029" ATTRS{bNumInterfaces}==" 2" ATTRS{bConfigurationValue}=="1" ATTRS{bmAttributes}=="a0" ATTRS{bMaxPower}==" 98mA" ATTRS{urbnum}=="26205" ATTRS{idVendor}=="046d" ATTRS{idProduct}=="c048" ATTRS{bcdDevice}=="5000" ATTRS{bDeviceClass}=="00" ATTRS{bDeviceSubClass}=="00" ATTRS{bDeviceProtocol}=="00" ATTRS{bNumConfigurations}=="1" ATTRS{bMaxPacketSize0}=="8" ATTRS{speed}=="12" ATTRS{busnum}=="2" ATTRS{devnum}=="4" ATTRS{devpath}=="1.2" ATTRS{version}==" 2.00" ATTRS{maxchild}=="0" ATTRS{quirks}=="0x0" ATTRS{avoid_reset_quirk}=="0" ATTRS{authorized}=="1" ATTRS{manufacturer}=="Logitech" ATTRS{product}=="G9 Laser Mouse" ATTRS{serial}=="AC993E820D0029"

To identify the device using either the serial or idVendor and idProduct looks like a good idea for the new udev rule (finally!), which goes to:

/etc/udev/rules.d/42-hello-usb-mouse.rules

The number indicates the order in which udev rules are executed and can be chosen freely; see the other rules in this directory and /lib/udev/rules.d . The file must end with .rules or otherwise it will not work. Now one of those lines goes in there:

ATTRS{serial}=="AC993E820D0029", SYMLINK+="helloUsbMouse"
ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c048", SYMLINK+="helloUsbMouse"

The first line will most certainly match only the one mouse that is plugged right now, the second line should match all mouses of the same type (e.g. all Logitech G9 mouses).

To test the rule, the udev daemon needs to be re-started with:

# service udev restart

When unplugging and plugging the mouse now, there should be a new link called helloUsbMouse in /dev :

$ ls /dev/hello*
/dev/helloUsbMouse

If not, re-check the matching conditions. reactivated.net[2] provides detailled information about writing udev rules for all kinds of devices and also information on how udev works.

Step 3: Automatically setting the mouse acceleration with udev

Now we finally come to the point that runs xinput automatically when the mouse is plugged. This happens with the scripts listed below which should go to some directory_of_your_choice . A few words to them first:

directory_of_your_choice/mouse-accel-starter.sh:

#!/bin/bash # Takes one argument; 0 switches the touchpad on, 1 switches it off. /usr/bin/synclient TouchpadOff=$1 export DISPLAY=${DISPLAY} directory_of_your_choice/mouse-accel.sh &

You may want to run this script manually to check if it really works with directory_of_your_choice/mouse-accel-starter.sh 0 (or $$directory_of_your_choice/mouse-accel-starter.sh 1). udev will not show any error message if the script fails.

directory_of_your_choice/mouse-accel.sh

#!/bin/bash sleep 2 for i in $(xinput list |grep Logitech |sed -e 's/^.*id=\([0-9]*.\).*$/\1/') do echo "Found device: $i" xinput set-ptr-feedback $i 10 1 1 done

Obviously you will have to adjust the device name to search for (which is Logitech in this case).

/etc/udev/rules.d/42-mouse-accel.rules: (Note that there is exactly one line per rule, no more and no less.)

ACTION=="add", ATTRS{serial}=="AC993E820D0029", ENV{DISPLAY}=":0.0", ENV{XAUTHORITY}="/home/your-username/.Xauthority", ENV{ID_CLASS}="mouse", RUN+="directory_of_your_choice/mouse-accel-starter.sh 1"
ACTION=="remove", ATTRS{serial}=="AC993E820D0029", ENV{DISPLAY}=":0.0", ENV{XAUTHORITY}="/home/your-username/.Xauthority", ENV{ID_CLASS}="mouse", RUN+="directory_of_your_choice/mouse-accel-starter.sh 0"

The additional ACTION attribute causes the rule to match only when the mouse is either plugged or removed. This allows to re-enable the touchpad when it is unplugged. Do not forget to change your-username to match your home directory.

Summary

udev is not actually complicated, it is just a little hard to debug. I hope this tutorial gave a few tips on how to avoid, or find, them. If you search for alternative literature, additionally to the pages mentioned before, there is a short udev tutorial on the CCNY Robotics Lab Wiki[4]. German speaking readers may appreciate the German article on the ubuntuusers wiki[5].

Currently unresolved problems

The rule will only apply if your X server is started. So if the mouse is plugged when booting, the rule will match right at the beginning of the boot sequence which is long before the system even thinks of running anything related to X. The mouse therefore needs to be unplugged and plugged again after logging in to KDE/Unity/Gnome/etc.

Also, if you switch to text-based ttys (Ctrl-Alt-F1 and so on) and back to X the settings get lost, so again unplug and re-plug the mouse.

  1. ^ askubuntu.com: How can I set different sensitivities for two mice at the same time?
  2. ^ reactivated.net: Writing udev rules
  3. ^ archlinux.org: Synaptics
  4. ^ CCNY Robotics Lab Wiki: Writing udev Rules
  5. ^ wiki.ubuntuusers.de: udev