From 4d2724d022e0e925495e6678cd2d8372d3a3d695 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 3 May 2016 15:48:12 +1000 Subject: [PATCH 1/3] evdev: de-duplicate the model property->model flag list Rather than a list where the only difference is the LIBINPUT_MODEL vs EVDEV_MODEL prefix, use a macro. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index bcd9c5bd..97a8fff6 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1715,26 +1715,29 @@ evdev_read_model_flags(struct evdev_device *device) const char *property; enum evdev_device_model model; } model_map[] = { - { "LIBINPUT_MODEL_LENOVO_X230", EVDEV_MODEL_LENOVO_X230 }, - { "LIBINPUT_MODEL_LENOVO_X220_TOUCHPAD_FW81", EVDEV_MODEL_LENOVO_X220_TOUCHPAD_FW81 }, - { "LIBINPUT_MODEL_CHROMEBOOK", EVDEV_MODEL_CHROMEBOOK }, - { "LIBINPUT_MODEL_SYSTEM76_BONOBO", EVDEV_MODEL_SYSTEM76_BONOBO }, - { "LIBINPUT_MODEL_SYSTEM76_GALAGO", EVDEV_MODEL_SYSTEM76_GALAGO }, - { "LIBINPUT_MODEL_SYSTEM76_KUDU", EVDEV_MODEL_SYSTEM76_KUDU }, - { "LIBINPUT_MODEL_CLEVO_W740SU", EVDEV_MODEL_CLEVO_W740SU }, - { "LIBINPUT_MODEL_APPLE_TOUCHPAD", EVDEV_MODEL_APPLE_TOUCHPAD }, - { "LIBINPUT_MODEL_WACOM_TOUCHPAD", EVDEV_MODEL_WACOM_TOUCHPAD }, - { "LIBINPUT_MODEL_ALPS_TOUCHPAD", EVDEV_MODEL_ALPS_TOUCHPAD }, - { "LIBINPUT_MODEL_SYNAPTICS_SERIAL_TOUCHPAD", EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD }, - { "LIBINPUT_MODEL_JUMPING_SEMI_MT", EVDEV_MODEL_JUMPING_SEMI_MT }, - { "LIBINPUT_MODEL_ELANTECH_TOUCHPAD", EVDEV_MODEL_ELANTECH_TOUCHPAD }, - { "LIBINPUT_MODEL_APPLE_INTERNAL_KEYBOARD", EVDEV_MODEL_APPLE_INTERNAL_KEYBOARD }, - { "LIBINPUT_MODEL_CYBORG_RAT", EVDEV_MODEL_CYBORG_RAT }, - { "LIBINPUT_MODEL_CYAPA", EVDEV_MODEL_CYAPA }, - { "LIBINPUT_MODEL_ALPS_RUSHMORE", EVDEV_MODEL_ALPS_RUSHMORE }, - { "LIBINPUT_MODEL_LENOVO_T450_TOUCHPAD", EVDEV_MODEL_LENOVO_T450_TOUCHPAD }, - { "LIBINPUT_MODEL_WOBBLY_TOUCHPAD", EVDEV_MODEL_WOBBLY_TOUCHPAD }, +#define MODEL(name) { "LIBINPUT_MODEL_" #name, EVDEV_MODEL_##name } + MODEL(LENOVO_X230), + MODEL(LENOVO_X230), + MODEL(LENOVO_X220_TOUCHPAD_FW81), + MODEL(CHROMEBOOK), + MODEL(SYSTEM76_BONOBO), + MODEL(SYSTEM76_GALAGO), + MODEL(SYSTEM76_KUDU), + MODEL(CLEVO_W740SU), + MODEL(APPLE_TOUCHPAD), + MODEL(WACOM_TOUCHPAD), + MODEL(ALPS_TOUCHPAD), + MODEL(SYNAPTICS_SERIAL_TOUCHPAD), + MODEL(JUMPING_SEMI_MT), + MODEL(ELANTECH_TOUCHPAD), + MODEL(APPLE_INTERNAL_KEYBOARD), + MODEL(CYBORG_RAT), + MODEL(CYAPA), + MODEL(ALPS_RUSHMORE), + MODEL(LENOVO_T450_TOUCHPAD), + MODEL(WOBBLY_TOUCHPAD), { NULL, EVDEV_MODEL_DEFAULT }, +#undef MODEL }; const struct model_map *m = model_map; uint32_t model_flags = 0; From 8a415e486a6df06ca198b503cd120205cbbc8c6e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 3 May 2016 15:03:40 +1000 Subject: [PATCH 2/3] Add tagging of trackballs Currently unused, but oh, the possibilities... The only thing we have to go on for trackballs at the moment is whether they have "Trackball" in the name string. All others need to be manually tagged. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 1 + src/evdev.h | 1 + test/litest-device-logitech-trackball.c | 2 +- test/litest.h | 1 + udev/90-libinput-model-quirks.hwdb | 6 ++++++ 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/evdev.c b/src/evdev.c index 97a8fff6..7abd8953 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1736,6 +1736,7 @@ evdev_read_model_flags(struct evdev_device *device) MODEL(ALPS_RUSHMORE), MODEL(LENOVO_T450_TOUCHPAD), MODEL(WOBBLY_TOUCHPAD), + MODEL(TRACKBALL), { NULL, EVDEV_MODEL_DEFAULT }, #undef MODEL }; diff --git a/src/evdev.h b/src/evdev.h index db6d83fa..2607fd85 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -116,6 +116,7 @@ enum evdev_device_model { EVDEV_MODEL_ALPS_RUSHMORE = (1 << 16), EVDEV_MODEL_LENOVO_T450_TOUCHPAD= (1 << 17), EVDEV_MODEL_WOBBLY_TOUCHPAD = (1 << 18), + EVDEV_MODEL_TRACKBALL = (1 << 19), }; struct mt_slot { diff --git a/test/litest-device-logitech-trackball.c b/test/litest-device-logitech-trackball.c index c3b59870..1a5d8966 100644 --- a/test/litest-device-logitech-trackball.c +++ b/test/litest-device-logitech-trackball.c @@ -53,7 +53,7 @@ static int events[] = { struct litest_test_device litest_logitech_trackball_device = { .type = LITEST_LOGITECH_TRACKBALL, - .features = LITEST_RELATIVE | LITEST_BUTTON, + .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_TRACKBALL, .shortname = "logitech trackball", .setup = litest_logitech_trackball_setup, .interface = NULL, diff --git a/test/litest.h b/test/litest.h index 747512ee..211b1761 100644 --- a/test/litest.h +++ b/test/litest.h @@ -228,6 +228,7 @@ enum litest_device_feature { LITEST_TABLET_PAD = 1 << 21, LITEST_RING = 1 << 22, LITEST_STRIP = 1 << 23, + LITEST_TRACKBALL = 1 << 24, }; struct litest_device { diff --git a/udev/90-libinput-model-quirks.hwdb b/udev/90-libinput-model-quirks.hwdb index 91ac2431..147fec25 100644 --- a/udev/90-libinput-model-quirks.hwdb +++ b/udev/90-libinput-model-quirks.hwdb @@ -146,3 +146,9 @@ libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnSystem76*pvrkudp1* ########################################## libinput:touchpad:input:b0003v056Ap* LIBINPUT_MODEL_WACOM_TOUCHPAD=1 + +########################################## +# Anything that has trackball in the name +########################################## +libinput:name:*Trackball*:dmi:* + LIBINPUT_MODEL_TRACKBALL=1 From d1a8a92184684474bff9d4c29a67da416d40fb26 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 3 May 2016 13:52:53 +1000 Subject: [PATCH 3/3] Add support for relative device rotation (trackball only) Trackballs are effectively stationary devices and can be positioned at any rotation. They are also employed by users with impaired dexterity which sometimes implies that they are positioned at an non-default angle to make the buttons easier to reach. Add a config option for rotation for trackball devices. Currently only supported for 90-degree angles, if there is a need we can add more angles later. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 68 ++++++++++ src/evdev.h | 7 ++ src/libinput-private.h | 10 ++ src/libinput-util.h | 21 ++++ src/libinput.c | 41 +++++++ src/libinput.h | 96 +++++++++++++++ src/libinput.sym | 7 ++ test/Makefile.am | 5 + test/trackball.c | 273 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 528 insertions(+) create mode 100644 test/trackball.c diff --git a/src/evdev.c b/src/evdev.c index 7abd8953..38ac3aa7 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -370,6 +370,22 @@ evdev_filter_defuzz_touch(struct evdev_device *device, struct mt_slot *slot) return false; } +static inline void +evdev_rotate_relative(struct evdev_device *device) +{ + struct evdev_dispatch *dispatch = device->dispatch; + struct device_coords rel = device->rel; + + if (!device->base.config.rotation) + return; + + /* loss of precision for non-90 degrees, but we only support 90 deg + * right now anyway */ + matrix_mult_vec(&dispatch->rotation.matrix, &rel.x, &rel.y); + + device->rel = rel; +} + static void evdev_flush_pending_event(struct evdev_device *device, uint64_t time) { @@ -394,6 +410,8 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time) if (!(device->seat_caps & EVDEV_DEVICE_POINTER)) break; + evdev_rotate_relative(device); + normalize_delta(device, &device->rel, &unaccel); raw.x = device->rel.x; raw.y = device->rel.y; @@ -1326,6 +1344,55 @@ evdev_init_natural_scroll(struct evdev_device *device) device->base.config.natural_scroll = &device->scroll.config_natural; } +static int +evdev_rotation_config_is_available(struct libinput_device *device) +{ + /* This function only gets called when we support rotation */ + return 1; +} + +static enum libinput_config_status +evdev_rotation_config_set_angle(struct libinput_device *device, + unsigned int degrees_cw) +{ + struct evdev_dispatch *dispatch = ((struct evdev_device*)device)->dispatch; + + dispatch->rotation.angle = degrees_cw; + matrix_init_rotate(&dispatch->rotation.matrix, degrees_cw); + + return LIBINPUT_CONFIG_STATUS_SUCCESS; +} + +static unsigned int +evdev_rotation_config_get_angle(struct libinput_device *device) +{ + struct evdev_dispatch *dispatch = ((struct evdev_device*)device)->dispatch; + + return dispatch->rotation.angle; +} + +static unsigned int +evdev_rotation_config_get_default_angle(struct libinput_device *device) +{ + return 0; +} + +static void +evdev_init_rotation(struct evdev_device *device, + struct evdev_dispatch *dispatch) +{ + if ((device->model_flags & EVDEV_MODEL_TRACKBALL) == 0) + return; + + dispatch->rotation.config.is_available = evdev_rotation_config_is_available; + dispatch->rotation.config.set_angle = evdev_rotation_config_set_angle; + dispatch->rotation.config.get_angle = evdev_rotation_config_get_angle; + dispatch->rotation.config.get_default_angle = evdev_rotation_config_get_default_angle; + dispatch->rotation.is_enabled = false; + matrix_init_identity(&dispatch->rotation.matrix); + device->base.config.rotation = &dispatch->rotation.config; +} + static struct evdev_dispatch * fallback_dispatch_create(struct libinput_device *device) { @@ -1356,6 +1423,7 @@ fallback_dispatch_create(struct libinput_device *device) evdev_init_calibration(evdev_device, dispatch); evdev_init_sendevents(evdev_device, dispatch); + evdev_init_rotation(evdev_device, dispatch); /* BTN_MIDDLE is set on mice even when it's not present. So * we can only use the absence of BTN_MIDDLE to mean something, i.e. diff --git a/src/evdev.h b/src/evdev.h index 2607fd85..8dafc2b4 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -289,6 +289,13 @@ struct evdev_dispatch { struct evdev_dispatch_interface *interface; struct libinput_device_config_calibration calibration; + struct { + bool is_enabled; + int angle; + struct matrix matrix; + struct libinput_device_config_rotation config; + } rotation; + struct { struct libinput_device_config_send_events config; enum libinput_config_send_events_mode current_mode; diff --git a/src/libinput-private.h b/src/libinput-private.h index b65ae93a..10522125 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -258,6 +258,15 @@ struct libinput_device_config_dwt { struct libinput_device *device); }; +struct libinput_device_config_rotation { + int (*is_available)(struct libinput_device *device); + enum libinput_config_status (*set_angle)( + struct libinput_device *device, + unsigned int degrees_cw); + unsigned int (*get_angle)(struct libinput_device *device); + unsigned int (*get_default_angle)(struct libinput_device *device); +}; + struct libinput_device_config { struct libinput_device_config_tap *tap; struct libinput_device_config_calibration *calibration; @@ -269,6 +278,7 @@ struct libinput_device_config { struct libinput_device_config_click_method *click_method; struct libinput_device_config_middle_emulation *middle_emulation; struct libinput_device_config_dwt *dwt; + struct libinput_device_config_rotation *rotation; }; struct libinput_device_group { diff --git a/src/libinput-util.h b/src/libinput-util.h index 82ab2b1e..701fe078 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -186,6 +186,12 @@ long_any_bit_set(unsigned long *array, size_t size) return 0; } +static inline double +deg2rad(int degree) +{ + return M_PI * degree / 180.0; +} + struct matrix { float val[3][3]; /* [row][col] */ }; @@ -227,6 +233,21 @@ matrix_init_translate(struct matrix *m, float x, float y) m->val[1][2] = y; } +static inline void +matrix_init_rotate(struct matrix *m, int degrees) +{ + double s, c; + + s = sin(deg2rad(degrees)); + c = cos(deg2rad(degrees)); + + matrix_init_identity(m); + m->val[0][0] = c; + m->val[0][1] = -s; + m->val[1][0] = s; + m->val[1][1] = c; +} + static inline int matrix_is_identity(const struct matrix *m) { diff --git a/src/libinput.c b/src/libinput.c index bcd0dcd0..7a9199dd 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -3688,3 +3688,44 @@ libinput_device_config_dwt_get_default_enabled(struct libinput_device *device) return device->config.dwt->get_default_enabled(device); } + +LIBINPUT_EXPORT int +libinput_device_config_rotation_is_available(struct libinput_device *device) +{ + if (!device->config.rotation) + return 0; + + return device->config.rotation->is_available(device); +} + +LIBINPUT_EXPORT enum libinput_config_status +libinput_device_config_rotation_set_angle(struct libinput_device *device, + unsigned int degrees_cw) +{ + if (!libinput_device_config_rotation_is_available(device)) + return degrees_cw ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED : + LIBINPUT_CONFIG_STATUS_SUCCESS; + + if (degrees_cw >= 360 || degrees_cw % 90) + return LIBINPUT_CONFIG_STATUS_INVALID; + + return device->config.rotation->set_angle(device, degrees_cw); +} + +LIBINPUT_EXPORT unsigned int +libinput_device_config_rotation_get_angle(struct libinput_device *device) +{ + if (!libinput_device_config_rotation_is_available(device)) + return 0; + + return device->config.rotation->get_angle(device); +} + +LIBINPUT_EXPORT unsigned int +libinput_device_config_rotation_get_default_angle(struct libinput_device *device) +{ + if (!libinput_device_config_rotation_is_available(device)) + return 0; + + return device->config.rotation->get_default_angle(device); +} diff --git a/src/libinput.h b/src/libinput.h index a93676ec..d972dd82 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -809,6 +809,9 @@ libinput_event_pointer_get_dy(struct libinput_event_pointer *event); * X resolution of the touchpad. See @ref motion_normalization for more * details. * + * Any rotation applied to the device also applies to unaccelerated motion + * (see libinput_device_config_rotation_set_angle()). + * * @note It is an application bug to call this function for events other than * @ref LIBINPUT_EVENT_POINTER_MOTION. * @@ -831,6 +834,9 @@ libinput_event_pointer_get_dx_unaccelerated( * X resolution of the touchpad. See @ref motion_normalization for more * details. * + * Any rotation applied to the device also applies to unaccelerated motion + * (see libinput_device_config_rotation_set_angle()). + * * @note It is an application bug to call this function for events other than * @ref LIBINPUT_EVENT_POINTER_MOTION. * @@ -1356,6 +1362,9 @@ libinput_event_gesture_get_dy(struct libinput_event_gesture *event); * details. Note that unaccelerated events are not equivalent to 'raw' events * as read from the device. * + * Any rotation applied to the device also applies to gesture motion + * (see libinput_device_config_rotation_set_angle()). + * * @return the unaccelerated relative x movement since the last event */ double @@ -1375,6 +1384,9 @@ libinput_event_gesture_get_dx_unaccelerated( * details. Note that unaccelerated events are not equivalent to 'raw' events * as read from the device. * + * Any rotation applied to the device also applies to gesture motion + * (see libinput_device_config_rotation_set_angle()). + * * @return the unaccelerated relative y movement since the last event */ double @@ -3337,6 +3349,7 @@ libinput_device_group_get_user_data(struct libinput_device_group *group); * - libinput_device_config_scroll_set_natural_scroll_enabled() * - libinput_device_config_left_handed_set() * - libinput_device_config_middle_emulation_set_enabled() + * - libinput_device_config_rotation_set_angle() * - All devices: * - libinput_device_config_send_events_set_mode() */ @@ -4649,6 +4662,89 @@ libinput_device_config_dwt_get_enabled(struct libinput_device *device); enum libinput_config_dwt_state libinput_device_config_dwt_get_default_enabled(struct libinput_device *device); +/** + * @ingroup config + * + * Check whether a device can have a custom rotation applied. + * + * @param device The device to configure + * @return Non-zero if a device can be rotated, zero otherwise. + * + * @see libinput_device_config_rotation_set_angle + * @see libinput_device_config_rotation_get_angle + * @see libinput_device_config_rotation_get_default_angle + */ +int +libinput_device_config_rotation_is_available(struct libinput_device *device); + +/** + * @ingroup config + * + * Set the rotation of a device in degrees clockwise off the logical neutral + * position. Any subsequent motion events are adjusted according to the + * given angle. + * + * The angle has to be in the range of [0, 360[ degrees, otherwise this + * function returns LIBINPUT_CONFIG_STATUS_INVALID. If the angle is a + * multiple of 360 or negative, the caller must ensure the correct ranging + * before calling this function. + * + * libinput guarantees that this function accepts multiples of 90 degrees. + * If a value is within the [0, 360[ range but not a multiple of 90 degrees, + * this function may return LIBINPUT_CONFIG_STATUS_INVALID if the underlying + * device or implementation does not support finer-grained rotation angles. + * + * The rotation angle is applied to all motion events emitted by the device. + * Thus, rotating the device also changes the angle required or presented by + * scrolling, gestures, etc. + * + * @param device The device to configure + * @param degrees_cw The angle in degrees clockwise + * @return A config status code. Setting a rotation of 0 degrees on a + * device that does not support rotation always succeeds. + * + * @see libinput_device_config_rotation_is_available + * @see libinput_device_config_rotation_get_angle + * @see libinput_device_config_rotation_get_default_angle + */ +enum libinput_config_status +libinput_device_config_rotation_set_angle(struct libinput_device *device, + unsigned int degrees_cw); + +/** + * @ingroup config + * + * Get the current rotation of a device in degrees clockwise off the logical + * neutral position. If this device does not support rotation, the return + * value is always 0. + * + * @param device The device to configure + * @return The angle in degrees clockwise + * + * @see libinput_device_config_rotation_is_available + * @see libinput_device_config_rotation_set_angle + * @see libinput_device_config_rotation_get_default_angle + */ +unsigned int +libinput_device_config_rotation_get_angle(struct libinput_device *device); + +/** + * @ingroup config + * + * Get the default rotation of a device in degrees clockwise off the logical + * neutral position. If this device does not support rotation, the return + * value is always 0. + * + * @param device The device to configure + * @return The default angle in degrees clockwise + * + * @see libinput_device_config_rotation_is_available + * @see libinput_device_config_rotation_set_angle + * @see libinput_device_config_rotation_get_angle + */ +unsigned int +libinput_device_config_rotation_get_default_angle(struct libinput_device *device); + #ifdef __cplusplus } #endif diff --git a/src/libinput.sym b/src/libinput.sym index ca1baba7..c6a0e4c9 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -252,3 +252,10 @@ LIBINPUT_1.3 { libinput_event_tablet_pad_get_time; libinput_event_tablet_pad_get_time_usec; } LIBINPUT_1.2; + +LIBINPUT_1.4 { + libinput_device_config_rotation_get_angle; + libinput_device_config_rotation_get_default_angle; + libinput_device_config_rotation_is_available; + libinput_device_config_rotation_set_angle; +} LIBINPUT_1.3; diff --git a/test/Makefile.am b/test/Makefile.am index 9b67818a..f0ac8559 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -80,6 +80,7 @@ run_tests = \ test-gestures \ test-pointer \ test-touch \ + test-trackball \ test-trackpoint \ test-udev \ test-path \ @@ -144,6 +145,10 @@ test_trackpoint_SOURCES = trackpoint.c test_trackpoint_LDADD = $(TEST_LIBS) test_trackpoint_LDFLAGS = -no-install +test_trackball_SOURCES = trackball.c +test_trackball_LDADD = $(TEST_LIBS) +test_trackball_LDFLAGS = -no-install + test_misc_SOURCES = misc.c test_misc_CFLAGS= $(AM_CFLAGS) -DLIBINPUT_LT_VERSION="\"$(LIBINPUT_LT_VERSION)\"" test_misc_LDADD = $(TEST_LIBS) diff --git a/test/trackball.c b/test/trackball.c new file mode 100644 index 00000000..d3238cfd --- /dev/null +++ b/test/trackball.c @@ -0,0 +1,273 @@ +/* + * Copyright © 2016 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include + +#include "libinput-util.h" +#include "litest.h" + +START_TEST(trackball_rotation_config_defaults) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + int angle; + + ck_assert(libinput_device_config_rotation_is_available(device)); + + angle = libinput_device_config_rotation_get_angle(device); + ck_assert_int_eq(angle, 0); + angle = libinput_device_config_rotation_get_default_angle(device); + ck_assert_int_eq(angle, 0); +} +END_TEST + +START_TEST(trackball_rotation_config_invalid_range) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + enum libinput_config_status status; + + status = libinput_device_config_rotation_set_angle(device, 360); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID); + status = libinput_device_config_rotation_set_angle(device, 361); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID); + status = libinput_device_config_rotation_set_angle(device, -1); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID); +} +END_TEST + +START_TEST(trackball_rotation_config_no_rotation) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + enum libinput_config_status status; + int angle; + + ck_assert(!libinput_device_config_rotation_is_available(device)); + + angle = libinput_device_config_rotation_get_angle(device); + ck_assert_int_eq(angle, 0); + angle = libinput_device_config_rotation_get_default_angle(device); + ck_assert_int_eq(angle, 0); + + /* 0 always succeeds */ + status = libinput_device_config_rotation_set_angle(device, 0); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + for (angle = 1; angle < 360; angle++) { + if (angle % 90 == 0) + continue; + status = libinput_device_config_rotation_set_angle(device, + angle); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED); + } +} +END_TEST + +START_TEST(trackball_rotation_config_right_angle) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + enum libinput_config_status status; + int angle; + + ck_assert(libinput_device_config_rotation_is_available(device)); + + for (angle = 0; angle < 360; angle += 90) { + status = libinput_device_config_rotation_set_angle(device, + angle); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + } +} +END_TEST + +START_TEST(trackball_rotation_config_odd_angle) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + enum libinput_config_status status; + int angle; + + ck_assert(libinput_device_config_rotation_is_available(device)); + + for (angle = 0; angle < 360; angle++) { + if (angle % 90 == 0) + continue; + status = libinput_device_config_rotation_set_angle(device, + angle); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID); + } +} +END_TEST + +START_TEST(trackball_rotation_x) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_device *device = dev->libinput_device; + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + int angle; + double dx, dy; + + litest_drain_events(li); + + for (angle = 0; angle < 360; angle++) { + libinput_device_config_rotation_set_angle(device, angle); + + litest_event(dev, EV_REL, REL_X, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + event = libinput_get_event(li); + ptrev = litest_is_motion_event(event); + + /* Test unaccelerated because pointer accel may mangle the + other coords */ + dx = libinput_event_pointer_get_dx_unaccelerated(ptrev); + dy = libinput_event_pointer_get_dy_unaccelerated(ptrev); + + switch (angle) { + case 0: + ck_assert_double_eq(dx, 1.0); + ck_assert_double_eq(dy, 0.0); + break; + case 90: + ck_assert_double_eq(dx, 0.0); + ck_assert_double_eq(dy, 1.0); + break; + case 180: + ck_assert_double_eq(dx, -1.0); + ck_assert_double_eq(dy, 0.0); + break; + case 270: + ck_assert_double_eq(dx, 0.0); + ck_assert_double_eq(dy, -1.0); + break; + } + libinput_event_destroy(event); + } +} +END_TEST + +START_TEST(trackball_rotation_y) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_device *device = dev->libinput_device; + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + int angle; + double dx, dy; + + litest_drain_events(li); + + for (angle = 0; angle < 360; angle++) { + libinput_device_config_rotation_set_angle(device, angle); + + litest_event(dev, EV_REL, REL_Y, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + event = libinput_get_event(li); + ptrev = litest_is_motion_event(event); + + /* Test unaccelerated because pointer accel may mangle the + other coords */ + dx = libinput_event_pointer_get_dx_unaccelerated(ptrev); + dy = libinput_event_pointer_get_dy_unaccelerated(ptrev); + + switch (angle) { + case 0: + ck_assert_double_eq(dx, 0.0); + ck_assert_double_eq(dy, 1.0); + break; + case 90: + ck_assert_double_eq(dx, -1.0); + ck_assert_double_eq(dy, 0.0); + break; + case 180: + ck_assert_double_eq(dx, 0.0); + ck_assert_double_eq(dy, -1.0); + break; + case 270: + ck_assert_double_eq(dx, 1.0); + ck_assert_double_eq(dy, 0.0); + break; + } + libinput_event_destroy(event); + } +} +END_TEST + +START_TEST(trackball_rotation_accel) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_device *device = dev->libinput_device; + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + double dx, dy; + + litest_drain_events(li); + + /* Pointer accel mangles the coordinates, so we only test one angle + * and rely on the unaccelerated tests above to warn us when + * something's off */ + libinput_device_config_rotation_set_angle(device, 90); + + litest_event(dev, EV_REL, REL_Y, 1); + litest_event(dev, EV_REL, REL_X, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + event = libinput_get_event(li); + ptrev = litest_is_motion_event(event); + + dx = libinput_event_pointer_get_dx(ptrev); + dy = libinput_event_pointer_get_dy(ptrev); + + ck_assert_double_lt(dx, 0.0); + ck_assert_double_gt(dy, 0.0); + libinput_event_destroy(event); +} +END_TEST + +void +litest_setup_tests(void) +{ + litest_add("trackball:rotation", trackball_rotation_config_defaults, LITEST_TRACKBALL, LITEST_ANY); + litest_add("trackball:rotation", trackball_rotation_config_invalid_range, LITEST_TRACKBALL, LITEST_ANY); + litest_add("trackball:rotation", trackball_rotation_config_no_rotation, LITEST_ANY, LITEST_TRACKBALL); + litest_add("trackball:rotation", trackball_rotation_config_right_angle, LITEST_TRACKBALL, LITEST_ANY); + litest_add("trackball:rotation", trackball_rotation_config_odd_angle, LITEST_TRACKBALL, LITEST_ANY); + litest_add("trackball:rotation", trackball_rotation_x, LITEST_TRACKBALL, LITEST_ANY); + litest_add("trackball:rotation", trackball_rotation_y, LITEST_TRACKBALL, LITEST_ANY); + litest_add("trackball:rotation", trackball_rotation_accel, LITEST_TRACKBALL, LITEST_ANY); +}