mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-02-04 04:40:25 +01:00
evdev: switch to a normalized transformation matrix
The big change here is the requirement to have the translation component in a device-normalized coordinate space. Without that, we cannot reliably rotate as the coordinate space is effectively unknown and may differ between the axes. This affects any rotation matrix or translation matrix, pure scale matrices were working just fine since they're unit-less. Requiring the matrix in device-normalized space makes it possible for libinput to rotate or otherwise handle the matrix independent of the screen resolution. The rotation matrix is documented in a bit more detail to make it easier for users to figure it out. This changes the definition of the WL_CALIBRATION property (which is currently broken). Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Reviewed-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
parent
748d094c9c
commit
13972efd82
3 changed files with 85 additions and 14 deletions
71
src/evdev.c
71
src/evdev.c
|
|
@ -150,20 +150,10 @@ evdev_device_led_update(struct evdev_device *device, enum libinput_led leds)
|
|||
static void
|
||||
transform_absolute(struct evdev_device *device, int32_t *x, int32_t *y)
|
||||
{
|
||||
int32_t tx, ty;
|
||||
|
||||
if (!device->abs.apply_calibration)
|
||||
return;
|
||||
|
||||
tx = *x * device->abs.calibration[0] +
|
||||
*y * device->abs.calibration[1] +
|
||||
device->abs.calibration[2];
|
||||
|
||||
ty = *x * device->abs.calibration[3] +
|
||||
*y * device->abs.calibration[4] +
|
||||
device->abs.calibration[5];
|
||||
*x = tx;
|
||||
*y = ty;
|
||||
matrix_mult_vec(&device->abs.calibration, x, y);
|
||||
}
|
||||
|
||||
static inline double
|
||||
|
|
@ -913,6 +903,8 @@ evdev_device_create(struct libinput_seat *seat,
|
|||
device->pending_event = EVDEV_NONE;
|
||||
device->devname = libevdev_get_name(device->evdev);
|
||||
|
||||
matrix_init_identity(&device->abs.calibration);
|
||||
|
||||
if (evdev_configure_device(device) == -1)
|
||||
goto err;
|
||||
|
||||
|
|
@ -986,8 +978,61 @@ void
|
|||
evdev_device_calibrate(struct evdev_device *device,
|
||||
const float calibration[6])
|
||||
{
|
||||
device->abs.apply_calibration = 1;
|
||||
memcpy(device->abs.calibration, calibration, sizeof device->abs.calibration);
|
||||
struct matrix scale,
|
||||
translate,
|
||||
transform;
|
||||
double sx, sy;
|
||||
|
||||
matrix_from_farray6(&transform, calibration);
|
||||
device->abs.apply_calibration = !matrix_is_identity(&transform);
|
||||
|
||||
if (!device->abs.apply_calibration) {
|
||||
matrix_init_identity(&device->abs.calibration);
|
||||
return;
|
||||
}
|
||||
|
||||
sx = device->abs.absinfo_x->maximum - device->abs.absinfo_x->minimum + 1;
|
||||
sy = device->abs.absinfo_y->maximum - device->abs.absinfo_y->minimum + 1;
|
||||
|
||||
/* The transformation matrix is in the form:
|
||||
* [ a b c ]
|
||||
* [ d e f ]
|
||||
* [ 0 0 1 ]
|
||||
* Where a, e are the scale components, a, b, d, e are the rotation
|
||||
* component (combined with scale) and c and f are the translation
|
||||
* component. The translation component in the input matrix must be
|
||||
* normalized to multiples of the device width and height,
|
||||
* respectively. e.g. c == 1 shifts one device-width to the right.
|
||||
*
|
||||
* We pre-calculate a single matrix to apply to event coordinates:
|
||||
* M = Un-Normalize * Calibration * Normalize
|
||||
*
|
||||
* Normalize: scales the device coordinates to [0,1]
|
||||
* Calibration: user-supplied matrix
|
||||
* Un-Normalize: scales back up to device coordinates
|
||||
* Matrix maths requires the normalize/un-normalize in reverse
|
||||
* order.
|
||||
*/
|
||||
|
||||
/* Un-Normalize */
|
||||
matrix_init_translate(&translate,
|
||||
device->abs.absinfo_x->minimum,
|
||||
device->abs.absinfo_y->minimum);
|
||||
matrix_init_scale(&scale, sx, sy);
|
||||
matrix_mult(&scale, &translate, &scale);
|
||||
|
||||
/* Calibration */
|
||||
matrix_mult(&transform, &scale, &transform);
|
||||
|
||||
/* Normalize */
|
||||
matrix_init_translate(&translate,
|
||||
-device->abs.absinfo_x->minimum/sx,
|
||||
-device->abs.absinfo_y->minimum/sy);
|
||||
matrix_init_scale(&scale, 1.0/sx, 1.0/sy);
|
||||
matrix_mult(&scale, &translate, &scale);
|
||||
|
||||
/* store final matrix in device */
|
||||
matrix_mult(&device->abs.calibration, &transform, &scale);
|
||||
}
|
||||
|
||||
int
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ struct evdev_device {
|
|||
int32_t seat_slot;
|
||||
|
||||
int apply_calibration;
|
||||
float calibration[6];
|
||||
struct matrix calibration;
|
||||
} abs;
|
||||
|
||||
struct {
|
||||
|
|
|
|||
|
|
@ -1374,6 +1374,32 @@ libinput_device_get_keys(struct libinput_device *device,
|
|||
* [ d e f ] * [ y ]
|
||||
* [ 0 0 1 ] [ 1 ]
|
||||
* @endcode
|
||||
*
|
||||
* The translation component (c, f) is expected to be normalized to the
|
||||
* device coordinate range. For example, the matrix
|
||||
* @code
|
||||
* [ 1 0 1 ]
|
||||
* [ 0 1 -1 ]
|
||||
* [ 0 0 1 ]
|
||||
* @endcode
|
||||
* moves all coordinates by 1 device-width to the right and 1 device-height
|
||||
* up.
|
||||
*
|
||||
* The rotation matrix for rotation around the origin is defined as
|
||||
* @code
|
||||
* [ cos(a) -sin(a) 0 ]
|
||||
* [ sin(a) cos(a) 0 ]
|
||||
* [ 0 0 1 ]
|
||||
* @endcode
|
||||
* Note that any rotation requires an additional translation component to
|
||||
* translate the rotated coordinates back into the original device space.
|
||||
* The rotation matrixes for 90, 180 and 270 degrees clockwise are:
|
||||
* @code
|
||||
* 90 deg cw: 180 deg cw: 270 deg cw:
|
||||
* [ 0 -1 1] [ -1 0 1] [ 0 1 0 ]
|
||||
* [ 1 0 0] [ 0 -1 1] [ -1 0 1 ]
|
||||
* [ 0 0 1] [ 0 0 1] [ 0 0 1 ]
|
||||
* @endcode
|
||||
*/
|
||||
void
|
||||
libinput_device_calibrate(struct libinput_device *device,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue