diff --git a/src/evdev.c b/src/evdev.c index a0298872..4cd3cfaa 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); + 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 6aa98f54..9196bd20 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 04645a7e..82970e25 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,