[PATCH libinput 07/11] evdev: switch to a normalized transformation matrix
Hans de Goede
hdegoede at redhat.com
Thu Aug 28 06:07:26 PDT 2014
Hi,
On 08/27/2014 06:31 AM, Peter Hutterer wrote:
> 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 at who-t.net>
> ---
> src/evdev.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++-----------
> src/evdev.h | 2 +-
> src/libinput.h | 26 +++++++++++++++++++++
> 3 files changed, 85 insertions(+), 14 deletions(-)
>
> diff --git a/src/evdev.c b/src/evdev.c
> index a029887..4cd3cfa 100644
> --- a/src/evdev.c
> +++ b/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);
Erm, "!device->abs.apply_calibration" == "!!matrix_is_identity(&transform)" ==
"matrix_is_identity(&transform)", so your initializing the matrix to identity
here when it already is identity.
Otherwise looks good.
Regards,
Hans
> + 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
> diff --git a/src/evdev.h b/src/evdev.h
> index 6aa98f5..9196bd2 100644
> --- a/src/evdev.h
> +++ b/src/evdev.h
> @@ -73,7 +73,7 @@ struct evdev_device {
> int32_t seat_slot;
>
> int apply_calibration;
> - float calibration[6];
> + struct matrix calibration;
> } abs;
>
> struct {
> diff --git a/src/libinput.h b/src/libinput.h
> index 04645a7..82970e2 100644
> --- a/src/libinput.h
> +++ b/src/libinput.h
> @@ -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,
>
More information about the wayland-devel
mailing list