diff options
author | Peter Hutterer <peter.hutterer@who-t.net> | 2014-07-14 11:34:38 +1000 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2014-07-14 13:40:08 +1000 |
commit | d385afd324f15d1903ba1a5abadb59e8f5299540 (patch) | |
tree | 151c7c185e2aa3b628a574d128e3afcb051f60ba | |
parent | d70fc32e12ffb257903c280c0c81c1ebb2be0734 (diff) |
dix: apply pointer acceleration between mapping to phys and back to screenwip/touchpad-resolution-scaling
Mapping the device-relative deltas to something within the desktop ratio means
stretching dx or dy. This can cause different accelerations for the same input
data depending on the screen size. Split this up and convert to physical
movements first, accelerate that, then convert back to map to the screen data.
Double the magic factor, because otherwise we're too slow, magic of 8 feels
about alright for both the touchpad and a Wacom I5 in relative mode.
Note to those looking at this in the future: ideally we should convert the
delta into equivalent deltas on a standard 400 DPI USB mouse:
in scale_for_device_resolution:
double xmagic = 25.4/400000.0 * xres;
x = valuator_mask_get_double(mask, 0) / xmagic;
valuator_mask_set_double(mask, 0, x);
The deltas would thus correspond to the deltas coming from a mouse. That can
be accelerated in the same way, and then converted back.
in map_to_screen:
double xmagic = 25.4/400000.0 * xres;
x = valuator_mask_get_double(mask, 0) * xmagic;
x = x/xres * screen_res/screenInfo.width * xrange;
valuator_mask_set_double(mask, 0, x);
This should provide the same "feel" of pointer acceleration on both devices if
we have the same acceleration methods. Synaptics uses it's own acceleration
method however, so we need to trick around in the server here until we fix it
simultaneously in both packages, at which point we only have Wacom to worry
about and mummy can I go home yet?
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
-rw-r--r-- | dix/getevents.c | 77 |
1 files changed, 62 insertions, 15 deletions
diff --git a/dix/getevents.c b/dix/getevents.c index 59f95f0d1..ffc563f26 100644 --- a/dix/getevents.c +++ b/dix/getevents.c @@ -777,6 +777,9 @@ add_to_scroll_valuator(DeviceIntPtr dev, ValuatorMask *mask, int valuator, doubl what we do here is a hack to make touchpads usable. for a given relative motion vector in device coordinates: 1. calculate physical movement on the device in metres + 1.5. apply pointer acceleration on the physical movement + + [see map_into_screen for steps 2-5] 2. calculate pixel vector that is the same physical movement on the screen (times some magic number to provide sensible base speed) 3. calculate what percentage this vector is of the current screen @@ -799,18 +802,16 @@ scale_for_device_resolution(DeviceIntPtr dev, ValuatorMask *mask) ValuatorClassPtr v = dev->valuator; int xrange = v->axes[0].max_value - v->axes[0].min_value + 1; int yrange = v->axes[1].max_value - v->axes[1].min_value + 1; - int xres, yres; - /* If we have multiple screens with different dpi, it gets complicated: - we have to map which screen we're on and then take the dpi of that - screen to be somewhat accurate. */ - const ScreenPtr s = screenInfo.screens[0]; - const double screen_res = 1000.0 * s->width/s->mmWidth; /* units/m */ - - /* some magic multiplier, so unaccelerated movement of x mm on the - device reflects x * magic mm on the screen */ - const double magic = 4; + /* we're converting to mm here before applying an acceleration. that + will feel too slow for touchpads, so apply some magic multiplier + here to make it feel right. Wacom tablets in relative mode will feel + too fast then, but that's the price we pay for now. This can really + only be fixed by dropping the synaptics-specific acceleration + profile. + */ + const double magic = 8; if (v->axes[0].resolution != 0 && v->axes[1].resolution != 0) { xres = v->axes[0].resolution; @@ -829,13 +830,41 @@ scale_for_device_resolution(DeviceIntPtr dev, ValuatorMask *mask) if (valuator_mask_isset(mask, 0)) { x = valuator_mask_get_double(mask, 0); - x = magic * x/xres * screen_res/screenInfo.width * xrange; + x = magic * x/xres; + valuator_mask_set_double(mask, 0, x); + } + + if (valuator_mask_isset(mask, 1)) { + y = valuator_mask_get_double(mask, 1); + y = magic * y/yres; + valuator_mask_set_double(mask, 1, y); + } +} + +/* See comment in scale_for_device_resolution, this is steps 2-5 */ +static void +map_into_screen(DeviceIntPtr dev, ValuatorMask *mask) +{ + double x, y; + ValuatorClassPtr v = dev->valuator; + /* If we have multiple screens with different dpi, it gets complicated: + we have to map which screen we're on and then take the dpi of that + screen to be somewhat accurate. */ + const ScreenPtr s = screenInfo.screens[0]; + const double screen_res = 1000.0 * s->width/s->mmWidth; /* units/m */ + + int xrange = v->axes[0].max_value - v->axes[0].min_value + 1; + int yrange = v->axes[1].max_value - v->axes[1].min_value + 1; + + if (valuator_mask_isset(mask, 0)) { + x = valuator_mask_get_double(mask, 0); + x = x * screen_res/screenInfo.width * xrange; valuator_mask_set_double(mask, 0, x); } if (valuator_mask_isset(mask, 1)) { y = valuator_mask_get_double(mask, 1); - y = magic * y/yres * screen_res/screenInfo.height * yrange; + y = y * screen_res/screenInfo.height * yrange; valuator_mask_set_double(mask, 1, y); } } @@ -1479,21 +1508,39 @@ fill_pointer_events(InternalEvent *events, DeviceIntPtr pDev, int type, set_raw_valuators(raw, &mask, raw->valuators.data); } else { + int is_absolute_device = 0; ValuatorClassPtr v = pDev->valuator; transformRelative(pDev, &mask); - /* for abs devices in relative mode, we've just scaled wrong, since we - mapped the device's shape into the screen shape. Undo this. */ + /* read the comment in scale_for_device_resolution and weep */ if (v && v->numAxes > 1 && v->axes[0].min_value < v->axes[0].max_value && - v->axes[1].min_value < v->axes[1].max_value) { + v->axes[1].min_value < v->axes[1].max_value) + is_absolute_device = 1;; + + if (is_absolute_device) { scale_for_device_resolution(pDev, &mask); + /* mask is now in mm, not units */ } + /* We've just changed dx/dy based on the device and screen + resolution. Depending on the desktop size this gives us a + stretched or compressed dx/dy, which then kicks in differently + for pointer acceleration. Solution: + * scale for device resolution so we have units in mm + * accelerate pointer + * scale back into what the screen res is + */ + /* now accelerate, either the mm or the dx/dy in units for + pure relative devices */ if (flags & POINTER_ACCELERATE) accelPointer(pDev, &mask, ms); + if (is_absolute_device) { + map_into_screen(pDev, &mask); + } + if ((flags & POINTER_NORAW) == 0 && raw) set_raw_valuators(raw, &mask, raw->valuators.data); |