Friday, December 5, 2014

pointer acceleration in libinput - building a DPI database for mice

click here to jump to the instructions

Mice have an optical sensor that tells them how far they moved in "mickeys". Depending on the sensor, a mickey is anywhere between 1/100 to 1/8200 of an inch or less. The current "standard" resolution is 1000 DPI, but older mice will have 800 DPI, 400 DPI etc. Resolutions above 1200 DPI are generally reserved for gaming mice with (usually) switchable resolution and it's an arms race between manufacturers in who can advertise higher numbers.

HW manufacturers are cheap bastards so of course the mice don't advertise the sensor resolution. Which means that for the purpose of pointer acceleration there is no physical reference. That delta of 10 could be a millimeter of mouse movement or a nanometer, you just can't know. And if pointer acceleration works on input without reference, it becomes useless and unpredictable. That is partially intended, HW manufacturers advertise that a lower resolution will provide more precision while sniping and a higher resolution means faster turns while running around doing rocket jumps. I personally don't think that there's much difference between 5000 and 8000 DPI anymore, the mouse is so sensitive that if you sneeze your pointer ends up next to Philae. But then again, who am I to argue with marketing types.

For us, useless and unpredictable is bad, especially in the use-case of everyday desktops. To work around that, libinput 0.7 now incorporates the physical resolution into pointer acceleration. And to do that we need a database, which will be provided by udev as of systemd 218 (unreleased at the time of writing). This database incorporates the various devices and their physical resolution, together with their sampling rate. udev sets the resolution as the MOUSE_DPI property that we can read in libinput and use as reference point in the pointer accel code. In the simplest case, the entry lists a single resolution with a single frequency (e.g. "MOUSE_DPI=1000@125"), for switchable gaming mice it lists a list of resolutions with frequencies and marks the default with an asterisk ("MOUSE_DPI=400@50 800@50 *1000@125 1200@125"). And you can and should help us populate the database so it gets useful really quickly.

How to add your device to the database

We use udev's hwdb for the database list. The upstream file is in /usr/lib/udev/hwdb.d/70-mouse.hwdb, the ruleset to trigger a match is in /usr/lib/udev/rules.d/70-mouse.rules. The easiest way to add a match is with the libevdev mouse-dpi-tool (version 1.3.2). Run it and follow the instructions. The output looks like this:

$ sudo ./tools/mouse-dpi-tool  /dev/input/event8
Mouse Lenovo Optical USB Mouse on /dev/input/event8
Move the device along the x-axis.
Pause 3 seconds before movement to reset, Ctrl+C to exit.
Covered distance in device units:      264 at frequency 125.0Hz |       |^C
Estimated sampling frequency: 125Hz
To calculate resolution, measure physical distance covered
and look up the matching resolution in the table below
      16mm     0.66in      400dpi
      11mm     0.44in      600dpi
       8mm     0.33in      800dpi
       6mm     0.26in     1000dpi
       5mm     0.22in     1200dpi
       4mm     0.19in     1400dpi
       4mm     0.17in     1600dpi
       3mm     0.15in     1800dpi
       3mm     0.13in     2000dpi
       3mm     0.12in     2200dpi
       2mm     0.11in     2400dpi

Entry for hwdb match (replace XXX with the resolution in DPI):
mouse:usb:v17efp6019:name:Lenovo Optical USB Mouse:
 MOUSE_DPI=XXX@125
Take those last two lines, add them to a local new file /etc/udev/hwdb.d/71-mouse.hwdb. Rebuild the hwdb, trigger it, and done:
$ sudo udevadm hwdb --update
$ sudo udevadm trigger /dev/input/event8
Leave out the device path if you're not on systemd 218 yet. Check if the property is set:
$ udevadm info /dev/input/event8 | grep MOUSE_DPI
E: MOUSE_DPI=1000@125
And that shows everything worked. Restart X/Wayland/whatever uses libinput and you're good to go. If it works, double-check the upstream instructions, then file a bug against systemd with those two lines and assign it to me.

Trackballs are a bit hard to measure like this, my suggestion is to check the manufacturer's website first for any resolution data.

Update 2014/12/06: trackball comment added, udevadm trigger comment for pre 218
Update 2015/08/26: udpated link to systemd bugzilla (now on github)

30 comments:

yOSHi314 said...

So, what would be the correct procedure for mice with switchable dpi?

I am assuming it's to provide the lowest value, or whatever the device defaults to once it's powered on.

Unknown said...

@yOSHi314: More specific instructions about multi-resolution mices are here:

http://cgit.freedesktop.org/systemd/systemd/tree/hwdb/70-mouse.hwdb

Robin said...

I really, really hope that acceleration will be optional. I can't stand it, and always disable it on my Windows computers.

Zan Lynx said...

I agree about disabling acceleration. Since my mouse can go to Philae if I sneeze, I don't need or want accel. :-)

Karl said...

Are the reasons why this in some cases will not work?

I identified my mouse to be event20 with "ls -l /dev/input/by-id". I ran the mouse-dpi-tool. Put the last two lines into /etc/udev/hwdb.d/71-mouse.hwdb with the correct dpi. Second line needs to start with whitespace otherwise hwdb update fails? I get "Error, DATA expected but got empty line in '/etc/udev/hwdb.d/71-mouse.hwdb':" if it doesn't start with whitespace. Ran the hwdb --update. "sudo udevadm trigger /dev/input/event20" doesn't work "Extraneous argument: '/dev/input/event20'" Just running trigger works. MOUSE_DPI doesn't show up in udevadm info for event20. Rebooted, still nothing.

Peter Hutterer said...

Karl: if the trigger command doesn't work, you don't have an up-to-date systemd which means you probably don't have the hwdb set up yet. If you manually put the rules/hwdb files in, just use udevadm trigger without any arguments.

djc.id.au said...

I used the mouse-dpi-tool on my mouse, which is a wireless Logitech M705 with a "unifying receiver" that supports many different Logitech devices. The rule it produces looks like this:

mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:101b:

I'm guessing the vid/pid in this rule is for the unifying receiver itself though -- so this rule will be useless, because someone can have the same unifying receiver with a different mouse attached.

I wonder if there is any way udev can match the specific mouse attached to the receiver?

Peter Hutterer said...

djc: I forgot to mention this: that's going to be fixed in 3.18 when it will be reliable. I'll amend that asap

Karl said...

@Peter
I'm using systemd 217 on Arch Linux, which I belive is the latest stable version. Do I need the development version?

Florian Hubold said...

@Peter: I've got one of those switchable gaming mice, and DPI switching works just fine since I got it over a year ago - so what would improve when I submit the measurements to udev hwdb? Is it only about changing the acceleration, too?

Peter Hutterer said...

Karl: from the post "And to do that we need a database, which will be provided by udev as of systemd 218 (unreleased at the time of writing). "

Florian: on switchable mice the default resolution must be marked, that's the one libinput picks and normalizes to 1000dpi. because we can't detect dpi changes at runtime, all other resolutions will be relatively faster or slower than that default one. That default one will now have acceleration applied to properly. But given that the default is likely to be around 1000dpi I doubt you'll see any changes anyway :)

Peter Hutterer said...

djc: sorry got the kernel versions wrong: it's fixed in 3.19, not 3.18, so we'll need double entries for all these devices (with the wireless PID in the name and the fixed one for 3.19)

kparal said...

Peter: If we want to gather a reasonable mouse properties database, that detection tool needs to be available pre-compiled. Most people don't know how to compile it or are too lazy to do it (myself included), but would happily provide the data if the tool could easily be downloaded and run (either standalone or as a part of some RPM package). Do you consider providing that? Thanks.

Peter Hutterer said...

kparal: it's already part of the libevdev-utils package in Fedora 21 and rawhide. Please get your distribution to ship it (I suspect they will anyway once the update to libevdev 1.3.2)

Unknown said...

This is a great step in the right direction. It really lends itself to trying to set the mouse to the highest DPI by default (someday), and then normalizing the pointer speed/acceleration to adjust for the increased DPI. The increased precision from having a higher DPI makes a noticeable difference for users that lower or disable acceleration.

Have you considered fully automating the submission process? The database would probably grow much more quickly if users can submit information without having to file a bug.

Peter Hutterer said...

Unfortunately, the amount of effort to make this automatic and useful for everyone is well beyond the time I have available.

kparal said...

Peter Hutterer: Yeah, I use Fedora. It would be a good idea to mention the fact that mouse-dpi-tool is a part of libevdev-utils in the blog post itself. That simplifies things a lot for people not familiar with this area.

lecbee said...

@Peter
Could you explain me how the measurement is done.
As far as I understand, I have to move the mouse, measure the distance on my screen (in centimeters), divide it by the "Covered distance in device units" and match that number with the second columns.

What the first column, stands for ?
What if the number obtained doesn't match exactly with a number in the second column ?

Peter Hutterer said...

lecbee: you need to measure the distance covered by the mouse on the table, not on the screen.

say you move 5 inches, find the line that says 5in in the second column and the third value is your DPI. You don't need to calculate anything at all.

The first number is mm, second in inches, but they're the same value (US vs rest of the world). if you measure in inches, ignore the first column.

If you can't find a matching DPI value, simply divide the units counted by the number of inches you moved and that's it.

Jacek Krüger said...

The mouse-dpi-tool always reports zero units for me. Running as root, under Xorg and VT, fedora libevdev-devel package and compiled from source. Always the same results.

# mouse-dpi-tool /dev/input/event6
Mouse AlpsPS/2 ALPS DualPoint TouchPad on /dev/input/event6
Move the device along the x-axis.
Pause 3 seconds before movement to reset, Ctrl+C to exit.
Covered distance in device units: 0 at frequency 142.9Hz \^C
Estimated sampling frequency: 142Hz
To calculate resolution, measure physical distance covered
and look up the matching resolution in the table below
0mm 0.00in 400dpi
0mm 0.00in 600dpi
0mm 0.00in 800dpi
0mm 0.00in 1000dpi
0mm 0.00in 1200dpi
0mm 0.00in 1400dpi
0mm 0.00in 1600dpi
0mm 0.00in 1800dpi
0mm 0.00in 2000dpi
0mm 0.00in 2200dpi
0mm 0.00in 2400dpi

Entry for hwdb match (replace XXX with the resolution in DPI):
mouse:unknown bus type:v0002p0008:name:AlpsPS/2 ALPS DualPoint TouchPad:
MOUSE_DPI=XXX@142

Peter Hutterer said...

Jacek: that's a touchpad, not a mouse. The tool only works on relative devices, touchpads usually supply us with the information through the firmware anyway.

Jacek Krüger said...

I thought that it also needs the resolution info since two finger scrolling is overly sensitive under libinput. Maybe firmware reports wrong info. Is there any way to query the data?

Peter Hutterer said...

Jacek: yeah, check with evtest or evemu-describe, grep for Resolution. if you think it's off, best to file a bug

ecloud said...

Would you please consider using modern metric units instead of inches? I think a pitch would make more sense too, so the database should specify how many millimeters per mickey instead of how many mickeys per inch.

Peter Hutterer said...

ecloud: all manufacturers advertise as DPI. The effort of going against that is not worth the gain.

lecbee said...

What to report if I don't get an exact value, like 882 DPI or so ?
Also, with the same mouse, sometimes mouse-dpi-tools is in 125 Hz and other time in 142 Hz.

Peter Hutterer said...

lecbee: check with the HW manufacturer first if they have an advertised rating. Also test on multiple surfaces, not all provide the best scanning conditions.

Same with the frequency, many mice have dynamic frequency scaling so use the highest one you can find.

Unknown said...

@Peter
Couldn't find out how to assign the bug to you. The bug and a patch are here:

https://bugs.freedesktop.org/show_bug.cgi?id=89743

Thank you

John Horan said...

I'm trying to add an entry for the Apple MagicMouse. I run the mouse-dpi-tool and at the end it gives me

mouse:bluetooth:v05acp030d:name:Johns-Mouse:
MOUSE_DPI=1600@1000

I've tried adding that as a rule, with or without the name, as obviously a more generic entry shouldn't contain it. But no matter it doesn't apply when I call udev trigger. I've tried doing the same to an old usb mouse I had lying around, and it works there. Not sure what I'm doing wrong here, so any advice would be appreciated.

Sandro Mathys said...

Most gaming mice are programmable, i.e. their DPI sensitivity settings can be changed. So for those, this database might do more harm than good as far as I understand it...