diff --git a/src/evdev.c b/src/evdev.c index bcd9c5bd..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. @@ -1715,26 +1783,30 @@ 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), + MODEL(TRACKBALL), { NULL, EVDEV_MODEL_DEFAULT }, +#undef MODEL }; const struct model_map *m = model_map; uint32_t model_flags = 0; diff --git a/src/evdev.h b/src/evdev.h index db6d83fa..8dafc2b4 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 { @@ -288,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/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/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); +} 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