input_defuzz_abs_event considered harmful

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



There, now I have your attention, right? :)

TLDR: input_defuzz_abs_event() produces pointer jumps at certain slow
movement speeds and I don't know what to do about it.

While dealing with pointer jitter issues in libinput I stumbled into an
issue with how the fuzz value in the kernel is handled during
input_defuzz_abs_event(). Specifically, the response is nonlinear and can
lead to jumpy pointer movement for deltas d = fuzz and d = fuzz * 2.

Note, this bug affects a moving pointer, not a stationary one. For that the
defuzzing works as intended. Let's assume that with a perceived-constant
finger motion speed, you'll get deltas in the range (fuzz * 2) ± 3.

input_defuzz_abs_event() returns:
  d = fuzz * 2 - 3 → value returned: fuzz - 1
  d = fuzz * 2 - 2 → value returned: fuzz - 1
  d = fuzz * 2     → value returned: fuzz * 2
  d = fuzz * 2 + 1 → value returned: fuzz * 2 + 1
  d = fuzz * 2 + 2 → value returned: fuzz * 2 + 2

There's another jump at the fuzz ± 2 range. In concrete numbers for a fuzz
of 8, input deltas result in values:

  d = 6 → value 1
  d = 7 → value 1
  d = 8 → value 4
  d = 9 → value 4
  ...
  d = 14 → value 7
  d = 15 → value 7
  d = 16 → value 16 
  d = 17 → value 17 
  d = 18 → value 18 

i.e. at the fuzz and fuzz * 2 points we see a jump of approx size fuzz/2 and
fuzz, respectively. These are integer divisons, so some variance is expected.
Obviously the numbers change for higher fuzz values but 8 seems to be the
most common one.

A few more details and a python script to recreate this and play around with
it: https://bugs.freedesktop.org/show_bug.cgi?id=105303

In real terms, this means that a pointer moving at a percieved-constant
speed will provide deltas of [7, 7, 16, 7, 16, 16, 7, 16, 7]. And if we were
to rely on the fuzz in userspace as hint for our hysteresis, we'd end up
filtering the 7 values but letting the 16 values pass through. This gives us
pointer jumps.

Those jumps happen right at the slow speed where we're moving slow enough to
notice them. Ironically, slowing down to check what just happened will not
reproduce the problem. The defuzzed value is used as 'old' value for the
algorithm, so there's a feedback loop as well. So it's very hard to
reproduce consistently, you cannot really get a [7, 16, 7, 16] motion with
human input. But it's like bad kerning, once you see it you cannot unsee it
:)

I'm a bit out of ideas on how to deal with this:
1) improve the algorithm in the kernel so the response is more linear
2) figure out a way how to undo the kernel defuzz in userspace and
  do hysteresis based on the reconstructed raw value
3) provide some way of disabling in-kernel hysteres while leaving fuzz set
4) force the fuzz to 0, while remembering what it should be

Neither of them is ideal, but 4 seems the best, with caveats.

1) can remove (some of) the jumps but since we already get processed
   coordinates, doing the correct thing in libinput is harder
2) possibly possible, but will be lossy or inaccurate
3) would require some new ioctl and per-client behaviour. not ideal
4) this one is both the easiest and messiest. The approach would be:
 * a udev rule extracts the fuzz value into a AXIS_FUZZ=$fuzz property
 * use EVIOCSABS to set the fuzz to 0 (thus disabling kernel defuzzing), 
 * have libinput init the hysteresis based on AXIS_FUZZ
 * pray that no other input-handling clients relies on kernel fuzzing
   because we just changed the kernel device

Any suggestions on how to handle this would be appreciated.

Cheers,
  Peter
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux