diff --git a/COPYING b/COPYING
index 8bbb3c38..efc1a949 100644
--- a/COPYING
+++ b/COPYING
@@ -1,3 +1,4 @@
+Copyright © 2006-2009 Simon Thum
Copyright © 2008-2012 Kristian Høgsberg
Copyright © 2010-2012 Intel Corporation
Copyright © 2010-2011 Benjamin Franzke
diff --git a/configure.ac b/configure.ac
index 141f97ac..d321e87b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
AC_PREREQ([2.64])
m4_define([libinput_major_version], [0])
-m4_define([libinput_minor_version], [13])
+m4_define([libinput_minor_version], [15])
m4_define([libinput_micro_version], [0])
m4_define([libinput_version],
[libinput_major_version.libinput_minor_version.libinput_micro_version])
@@ -31,7 +31,7 @@ AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
# b) If interfaces have been changed or added, but binary compatibility has
# been preserved, change to C+1:0:A+1
# c) If the interface is the same as the previous version, change to C:R+1:A
-LIBINPUT_LT_VERSION=10:1:0
+LIBINPUT_LT_VERSION=12:0:2
AC_SUBST(LIBINPUT_LT_VERSION)
AM_SILENT_RULES([yes])
@@ -60,6 +60,20 @@ PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(MTDEV, [mtdev >= 1.1.0])
PKG_CHECK_MODULES(LIBUDEV, [libudev])
PKG_CHECK_MODULES(LIBEVDEV, [libevdev >= 0.4])
+PKG_CHECK_MODULES(LIBUNWIND,
+ [libunwind],
+ [HAVE_LIBUNWIND=yes],
+ [HAVE_LIBUNWIND=no])
+if test "x$HAVE_LIBUNWIND" = "xyes"; then
+ AC_DEFINE(HAVE_LIBUNWIND, 1, [Have libunwind support])
+fi
+AM_CONDITIONAL(HAVE_LIBUNWIND, [test "x$HAVE_LIBUNWIND" = xyes])
+AC_PATH_PROG(ADDR2LINE, [addr2line])
+if test "x$ADDR2LINE" != "x"; then
+ AC_DEFINE_UNQUOTED(HAVE_ADDR2LINE, 1, [addr2line found])
+ AC_DEFINE_UNQUOTED(ADDR2LINE, ["$ADDR2LINE"], [Path to addr2line])
+fi
+
AC_CHECK_LIB([m], [atan2])
AC_CHECK_LIB([rt], [clock_gettime])
@@ -201,5 +215,6 @@ AC_MSG_RESULT([
Build documentation ${build_documentation}
Build tests ${build_tests}
Tests use valgrind ${VALGRIND}
+ Tests use libunwind ${HAVE_LIBUNWIND}
Build GUI event tool ${build_eventgui}
])
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 271960e3..48e68b8a 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -1,4 +1,7 @@
-EXTRA_DIST = touchpad-tap-state-machine.svg touchpad-softbutton-state-machine.svg
+EXTRA_DIST = \
+ middle-button-emulation.svg \
+ touchpad-tap-state-machine.svg \
+ touchpad-softbutton-state-machine.svg
if BUILD_DOCS
@@ -16,7 +19,8 @@ header_files = \
$(srcdir)/seats.dox \
$(srcdir)/t440-support.dox \
$(srcdir)/tablet-support.dox \
- $(srcdir)/tapping.dox
+ $(srcdir)/tapping.dox \
+ $(srcdir)/test-suite.dox
diagram_files = \
$(srcdir)/dot/seats-sketch.gv \
diff --git a/doc/device-configuration-via-udev.dox b/doc/device-configuration-via-udev.dox
index fc1c0af8..e38b93f6 100644
--- a/doc/device-configuration-via-udev.dox
+++ b/doc/device-configuration-via-udev.dox
@@ -44,7 +44,10 @@ udev_device_type.
ID_INPUT_TOUCHSCREEN, ID_INPUT_TABLET, ID_INPUT_JOYSTICK,
ID_INPUT_ACCELEROMETER
If any of the above is set, libinput initializes the device as the given
-type, see @ref udev_device_type.
+type, see @ref udev_device_type. Note that for historical reasons more than
+one of these may be set at any time, libinput will select only one of these
+to determine the device type. To ensure libinput selects the correct device
+type, only set one of them.
WL_SEAT
Assigns the logical seat for this device. See
libinput_seat_get_logical_name()
@@ -57,6 +60,13 @@ See @ref motion_normalization for details.
The angle in degrees for each click on a mouse wheel. See
libinput_pointer_get_axis_source() for details.
+ POINTINGSTICK_CONST_ACCEL
+A constant (linear) acceleration factor to apply to pointingstick deltas
+to normalize them.
+ LIBINPUT_MODEL_*
+This prefix is reserved as private API, do not use. . See @ref
+model_specific_configuration for details.
+
Below is an example udev rule to assign "seat1" to a device from vendor
@@ -91,4 +101,16 @@ ACTION=="add|change", KERNEL=="event[0-9]*", ENV{ID_VENDOR_ID}=="012a", \
ENV{ID_MODEL_ID}=="034b", ENV{ID_INPUT_TOUCHPAD}="", ENV{ID_INPUT_TABLET}="1"
@endcode
+@section model_specific_configuration Model-specific configuration
+
+libinput reserves the property prefix LIBINPUT_MODEL_ for
+model-specific configuration. This prefix is reserved as private API, do
+not use.
+
+The effect of this property may be to enable or disable certain
+features on a specific device or set of devices, to change configuration
+defaults or any other reason. The effects of setting this property, the
+format of the property and the value of the property are subject to change
+at any time.
+
*/
diff --git a/doc/middle-button-emulation.svg b/doc/middle-button-emulation.svg
new file mode 100644
index 00000000..338af386
--- /dev/null
+++ b/doc/middle-button-emulation.svg
@@ -0,0 +1,1315 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ left button
+ press
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ right button
+ press
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ other button
+ press
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+ left button
+ release
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+ right button
+ press
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ BTN_LEFT
+ RELEASE
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ BTN_MIDDLE
+ PRESS
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+ left button
+ release
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ right button
+ release
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+ BTN_MIDDLE
+ RELEASE
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ BTN_MIDDLE
+ RELEASE
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+ left button
+ release
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ right button
+ release
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+ right button
+ press
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+ left button
+ press
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ other button
+ press
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+ left button
+ press
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ right button
+ release
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ other button
+ press
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+ BTN_RIGHT
+ PRESS
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ BTN_RIGHT
+ PRESS
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ BTN_RIGHT
+ RELEASE
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ other button
+ press
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+ BTN_MIDDLE
+ RELEASE
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ left button
+ release
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ right button
+ release
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ other button
+ event
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+ left button
+ press
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ left button
+ release
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+ BTN_LEFT
+ RELEASE
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+ right button
+ release
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+ right button
+ release
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ right button
+ press
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ BTN_RIGHT
+ PRESS
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ BTN_RIGHT
+ RELEASE
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ left button
+ release
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+ other button
+ press
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+ other button
+ press
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+ BTN_RIGHT
+ PRESS
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+ any button
+ event
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+ all buttons
+ up?
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+ other button
+ event
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ other button
+ event
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ any button
+ event
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+ all buttons
+ up?
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
+
+
+
+
+
+
+ [Not supported by viewer]
+
+
+
+
diff --git a/doc/palm-detection.dox b/doc/palm-detection.dox
index 4e839e62..a03f9c14 100644
--- a/doc/palm-detection.dox
+++ b/doc/palm-detection.dox
@@ -23,6 +23,10 @@ screen, it is common for a finger to start inside an exclusion zone and move
rapidly across the touchpad. libinput detects such movements and avoids palm
detection on such touch sequences.
+Each exclusion zone is divided into a top part and a bottom part. A touch
+starting in the top part of the exclusion zone does not trigger a
+tap (see @ref tapping).
+
In the diagram below, the exclusion zones are painted red.
Touch 'A' starts inside the exclusion zone and moves
almost vertically. It is considered a palm and ignored for cursor movement,
@@ -31,6 +35,11 @@ despite moving out of the exclusion zone.
Touch 'B' starts inside the exclusion zone but moves horizontally out of the
zone. It is considered a valid touch and controls the cursor.
+Touch 'C' occurs in the top part of the exclusion zone. Despite being a
+tapping motion, it does not generate an emulated button event. Touch 'D'
+likewise occurs within the exclusion zone but in the bottom half. libinput
+will generate a button event for this touch.
+
@image html palm-detection.svg
@section trackpoint-disabling Palm detection during trackpoint use
@@ -48,5 +57,26 @@ the palm on the touchpad while using the trackstick).
If the touchpad is disabled, the @ref t440_support "top software buttons"
remain enabled.
+@section disable-while-typing Disable-while-typing
+
+libinput automatically disables the touchpad for a timeout after a key
+press, a feature traditionally referred to as "disable while typing" and
+previously available through the
+[syndaemon(1)](http://linux.die.net/man/1/syndaemon) command. libinput does
+not require an external command and the feature is currently enabled for all
+touchpads but will be reduced in the future to only apply to touchpads where
+finger width or pressure data is unreliable.
+
+Notable behaviors of libinput's disable-while-typing feature:
+- Two different timeouts are used, after a single key press the timeout is
+ short to ensure responsiveness. After multiple key events, the timeout is
+ longer to avoid accidental pointer manipulation while typing.
+- Some keys do not trigger the timeout, specifically some modifier keys
+ (Ctrl, Alt, Shift, and Fn). Actions such as Ctrl + click thus stay
+ responsive.
+- Touches started while the touchpad is disabled do not control the cursor,
+ it is thus possible to rest the palm on the touchpad while typing.
+- Physical buttons work even while the touchpad is disabled. This includes
+ software-emulated buttons.
*/
diff --git a/doc/scrolling.dox b/doc/scrolling.dox
index 3ded909d..b5a01cf8 100644
--- a/doc/scrolling.dox
+++ b/doc/scrolling.dox
@@ -1,7 +1,20 @@
/**
@page scrolling Scrolling
-libinput supports three different types of scrolling behavior.
+libinput supports three different types of scrolling methods: @ref
+twofinger_scrolling, @ref edge_scrolling and @ref button_scrolling. Some devices
+support multiple methods, though only one can be enabled at a time. See
+libinput_device_config_scroll_set_method() for documentation on how to
+switch methods and libinput_device_config_scroll_get_methods() for
+documentation on how to query a device for available scroll methods.
+
+Scroll movements provide vertical and horizontal directions, each
+scroll event contains both directions where applicable, see
+libinput_event_pointer_get_axis_value(). libinput does not provide separate
+toggles to enable or disable horizontal scrolling. Instead, horizontal
+scrolling is always enabled. This is intentional, libinput does not have
+enough context to know when horizontal scrolling is appropriate for a given
+widget. The task of filtering horizontal movements is up to the caller.
@section twofinger_scrolling Two-finger scrolling
@@ -16,7 +29,6 @@ For scrolling to trigger, a built-in distance threshold has to be met but once
engaged any movement will scroll. In other words, to start scrolling a
sufficiently large movement is required, once scrolling tiny amounts of
movements will translate into tiny scroll movements.
-
Scrolling in both directions at once is possible by meeting the required
distance thresholds to enable each direction separately.
@@ -28,23 +40,27 @@ scroll).
@image html edge-scrolling.svg "Vertical and horizontal edge scrolling"
-Due to the layout of the edges, diagonal scrolling is not possible.
+Due to the layout of the edges, diagonal scrolling is not possible. The
+behavior of edge scrolling using both edges at the same time is undefined.
Edge scrolling conflicts with @ref clickpad_softbuttons and is
-not usually available on clickpads.
+not usually available on clickpads. See
+http://who-t.blogspot.com.au/2015/03/why-libinput-doesnt-support-edge.html
+for details.
@section button_scrolling On-Button scrolling
-Scrolling when a button is held down is available on selected devices. The
-motion of a device is converted into scrolling motion.
-
-For example, Lenovo devices provide a
+On-button scrolling converts the motion of a device into scroll events while
+a designated button is held down. For example, Lenovo devices provide a
pointing stick that emulates
scroll events when the trackstick's middle mouse button is held down.
@image html button-scrolling.svg "Button scrolling"
-Note that libinput's @ref t440_support enables the use of the middle
+The button may be changed with
+libinput_device_config_scroll_set_button() but must be on the same device as
+the motion events. Cross-device scrolling is not supported but
+for one exception: libinput's @ref t440_support enables the use of the middle
button for button scrolling (even when the touchpad is disabled).
*/
diff --git a/doc/svg/palm-detection.svg b/doc/svg/palm-detection.svg
index 9fb6077d..c3e45f44 100644
--- a/doc/svg/palm-detection.svg
+++ b/doc/svg/palm-detection.svg
@@ -2,14 +2,66 @@
+ id="svg2"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="palm-detection.svg">
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
-
-
-
-
-
- A
- B
-
-
+
+
+
+
+
+ A
+
+ B
+
+ C
+ D
+
+
diff --git a/doc/tapping.dox b/doc/tapping.dox
index f603b7d5..7eb81e62 100644
--- a/doc/tapping.dox
+++ b/doc/tapping.dox
@@ -33,6 +33,10 @@ continue the dragging process, so that multiple touchpad-widths of distance
can be covered easily. If two-fingers are supported by the hardware, a
second finger can be used to drag while the first is held in-place.
+An alternative method to end a drag process is to tap immediately after
+lifting the finger. The full sequence is thus: tap, finger down, drag,
+finger up, tap.
+
@section tap_constraints Constraints while tapping
A couple of constraints apply to the contact to be converted into a press, the most common ones are:
diff --git a/doc/test-suite.dox b/doc/test-suite.dox
new file mode 100644
index 00000000..30dd1579
--- /dev/null
+++ b/doc/test-suite.dox
@@ -0,0 +1,92 @@
+/**
+@page test-suite libinput test suite
+
+The libinput test suite is based on
+[Check](http://check.sourceforge.net/doc/check_html/) and runs automatically
+during `make check`. Check itself is wrapped into a libinput-specific test
+suite called *litest*. Tests are found in `$srcdir/test/`, the test binaries are
+prefixed with `test-` and can be run individually.
+
+@section test-config X.Org config to avoid interference
+
+uinput devices created by the test suite are usually recognised by X as
+input devices. All events sent through these devices will generate X events
+and interfere with your desktop.
+
+Copy the file `$srcdir/test/50-litest.conf` into your `/etc/X11/xorg.conf.d`
+and restart X. This will ignore any litest devices and thus not interfere
+with your desktop.
+
+@section test-root Permissions required to run tests
+
+Most tests require the creation of uinput devices and access to the
+resulting `/dev/input/eventX` nodes. Some tests require temporary udev rules.
+This usually requires the tests to be run as root .
+
+@section test-filtering Selective running of tests
+
+litest's tests are grouped by test groups and devices. A test group is e.g.
+"touchpad:tap" and incorporates all tapping-related tests for touchpads.
+Each test function is (usually) run with one or more specific devices.
+The `--list` commandline argument shows the list of suites and tests.
+@code
+$ ./test/test-device --list
+device:wheel:
+ wheel only
+ blackwidow
+device:invalid devices:
+ no device
+device:group:
+ no device
+ logitech trackball
+ MS surface cover
+ mouse_roccat
+ wheel only
+ blackwidow
+...
+@endcode
+
+In the above example, the "device:wheel" suite is run for the "wheel only" and
+the "blackwidow" device. Both devices are automatically instantiated through
+uinput by litest. The "no device" entry signals that litest does not
+instantiate a uinput device for a specific test (though the test itself may
+instantiate one).
+
+The `--filter-test` argument enables selective running of tests through
+basic shell-style function name matching. For example:
+
+@code
+$ ./test/test-touchpad --filter-test="*1fg_tap*"
+@endcode
+
+The `--filter-device` argument enables selective running of tests through
+basic shell-style device name matching. The device names matched are the
+litest-specific shortnames, see the output of `--list`. For example:
+
+@code
+$ ./test/test-touchpad --filter-device="synaptics*"
+@endcode
+
+The `--filter-group` argument enables selective running of test groups
+through basic shell-style test group matching. The test groups matched are
+litest-specific test groups, see the output of `--list`. For example:
+
+@code
+$ ./test/test-touchpad --filter-group="touchpad:*hover*"
+@endcode
+
+The `--filter-device` and `--filter-group` arguments can be combined with
+`--list` to show which groups and devices will be affected.
+
+@section test-verbosity Controlling test output
+
+Each test supports the `--verbose` commandline option to enable debugging
+output, see libinput_log_set_priority() for details. The `LITEST_VERBOSE`
+environment variable, if set, also enables verbose mode.
+
+@code
+$ ./test/test-device --verbose
+$ LITEST_VERBOSE=1 make check
+@endcode
+
+*/
diff --git a/doc/touchpad-tap-state-machine.svg b/doc/touchpad-tap-state-machine.svg
index 7aecefc1..39b0b86b 100644
--- a/doc/touchpad-tap-state-machine.svg
+++ b/doc/touchpad-tap-state-machine.svg
@@ -1,756 +1,1141 @@
-
-
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
- IDLE
+
+ IDLE
-
+
- TOUCH
+
+ TOUCH
-
+
- first
- finger down
+
+ first
+
+ finger down
-
-
-
+
+
+
- finger up
+
+ finger up
-
-
-
+
+
+
- button 1
- press
+
+ button 1
+
+ press
-
+
- timeout
+
+ timeout
-
-
-
+
+
+
- move >
- threshold
+
+ move >
+
+ threshold
-
-
-
+
+
+
- second
- finger down
+
+ second
+
+ finger down
-
-
-
+
+
+
- TOUCH_2
+
+ TOUCH_2
-
+
- second
- finger up
+
+ second
+
+ finger up
-
-
-
+
+
+
- button 2
- press
+
+ button 2
+
+ press
-
+
- move >
- threshold
+
+ move >
+
+ threshold
-
+
- timeout
+
+ timeout
-
-
-
-
-
+
+
+
+
+
- button 1
- release
+
+ button 1
+
+ release
-
+
- button 2
- release
+
+ button 2
+
+ release
-
-
-
-
-
+
+
+
+
+
- TAPPED
+
+ TAPPED
-
+
- timeout
+
+ timeout
-
-
-
+
+
+
- first
- finger down
+
+ first
+
+ finger down
-
-
-
+
+
+
- DRAGGING
+
+ DRAGGING
-
+
- first
- finger up
+
+ first
+
+ finger up
-
+
- btn1
- release
+
+ btn1
+
+ release
-
-
-
-
-
-
-
+
+
+
+
+
+
+
- IDLE
+
+ IDLE
-
+
- third
- finger down
+
+ third
+
+ finger down
-
-
-
+
+
+
- TOUCH_3
+
+ TOUCH_3
-
-
-
+
+
+
- button 3
- press
+
+ button 3
+
+ press
-
+
- button 3
- release
+
+ button 3
+
+ release
-
-
-
+
+
+
- move >
- threshold
+
+ move >
+
+ threshold
-
-
-
+
+
+
- IDLE
+
+ IDLE
-
+
- timeout
+
+ timeout
-
-
-
+
+
+
- first
- finger up
+
+ first
+
+ finger up
-
-
-
+
+
+
- IDLE
+
+ IDLE
-
+
- fourth
- finger down
+
+ fourth
+
+ finger down
-
-
-
-
-
+
+
+
+
+
- DRAGGING_OR_DOUBLETAP
+
+ DRAGGING_OR_DOUBLETAP
-
-
-
+
+
+
- timeout
+
+ timeout
-
-
-
+
+
+
- first
- finger up
+
+ first
+
+ finger up
-
-
-
+
+
+
- button 1
- release
+
+ button 1
+
+ release
-
+
- button 1
- press
+
+ button 1
+
+ press
-
+
- btn1
- release
+
+ btn1
+
+ release
-
-
-
-
-
-
-
-
-
+
+
+
- second
- finger down
+
+ second
+
+ finger down
-
-
-
+
+
+
- move >
- threshold
+
+ move >
+
+ threshold
-
-
-
-
-
+
+
+
+
+
- HOLD
+
+ HOLD
-
+
- first
- finger up
+
+ first
+
+ finger up
-
-
-
-
-
+
+
+
+
+
- second
- finger down
+
+ second
+
+ finger down
-
-
-
-
-
-
-
+
+
+
+
+
+
+
- TOUCH_2_HOLD
+
+ TOUCH_2_HOLD
-
+
- second
- finger up
+
+ second
+
+ finger up
-
-
-
+
+
+
- first
- finger up
+
+ first
+
+ finger up
-
-
-
-
-
-
-
+
+
+
+
+
+
+
- third
- finger down
+
+ third
+
+ finger down
-
-
-
-
-
-
-
+
+
+
+
+
+
+
- TOUCH_3_HOLD
+
+ TOUCH_3_HOLD
-
-
-
+
+
+
- fourth
- finger down
+
+ fourth
+
+ finger down
-
+
- DEAD
+
+ DEAD
-
-
-
-
-
-
-
+
+
+
+
+
+
+
- any finger up
+
+ any finger up
-
+
- fourth
- finger up
+
+ fourth
+
+ finger up
-
+
- any finger up
+
+ any finger up
-
-
-
-
+
+
+
+
-
- yes
+
+
+ yes
-
+
- any finger up
+
+ any finger up
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
- IDLE
+
+ IDLE
-
+
- if finger
- count == 0
+
+ if finger
+
+ count == 0
-
-
-
-
-
-
-
+
+
+
+
+
+
+
- second
- finger up
+
+ second
+
+ finger up
-
+
- DRAGGING_2
+
+ DRAGGING_2
-
-
-
-
-
+
+
+
+
+
- first
- finger up
+
+ first
+
+ finger up
-
-
-
-
-
-
-
+
+
+
+
+
+
+
- second
- finger down
+
+ second
+
+ finger down
-
-
-
-
-
-
-
+
+
+
+
+
+
+
- third
- finger down
+
+ third
+
+ finger down
-
-
-
+
+
+
- btn1
- release
+
+ btn1
+
+ release
-
-
-
+
+
+
- clickpad
- button
- press
+
+ phys
+
+ button
+
+ press
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
- clickpad
- button
- press
+
+ phys
+
+ button
+
+ press
-
-
-
+
+
+
- button 1
- release
+
+ button 1
+
+ release
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
- DRAGGING_WAIT
+
+ DRAGGING_WAIT
-
+
- timeout
+
+ timeout
-
-
-
-
-
-
-
+
+
+
+
+
+
+
- first
- finger down
+
+ first
+
+ finger down
-
-
-
+
+
+
- TOUCH_TOUCH
+
+ TOUCH_TOUCH
-
+
- TOUCH_IDLE
+
+ TOUCH_IDLE
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
- TOUCH_DEAD
+
+ TOUCH_DEAD
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
- yes
+
+
+ yes
-
+
- TOUCH_DEAD
+
+ TOUCH_DEAD
-
-
-
-
-
-
-
+
+
+
+
+
+
+
- TOUCH_IDLE
+
+ TOUCH_IDLE
-
-
-
+
+
+
- TOUCH_TOUCH
+
+ TOUCH_TOUCH
-
-
-
-
-
+
+
+
+
+
- TOUCH_IDLE
+
+ TOUCH_IDLE
-
-
-
+
+
+
- TOUCH_IDLE
+
+ TOUCH_IDLE
-
-
-
+
+
+
- TOUCH_IDLE
+
+ TOUCH_IDLE
-
-
-
+
+
+
- TOUCH_TOUCH
+
+ TOUCH_TOUCH
-
-
-
+
+
+
- that finger
- TOUCH_IDLE
+
+ that finger
+
+ TOUCH_IDLE
-
-
-
+
+
+
- TOUCH_DEAD
+
+ TOUCH_DEAD
-
-
-
-
-
-
-
+
+
+
+
+
+
+
- that finger
- TOUCH_IDLE
+
+ that finger
+
+ TOUCH_IDLE
-
-
-
-
+
+
+
+
-
- no
+
+
+ no
-
+
- TOUCH_TOUCH
+
+ TOUCH_TOUCH
-
-
-
+
+
+
- TOUCH_IDLE
+
+ TOUCH_IDLE
-
-
-
+
- TOUCH_TOUCH
+
+ TOUCH_TOUCH
-
-
-
+
+
+
- TOUCH_DEAD
+
+ TOUCH_DEAD
-
-
-
-
-
+
+
+
+
+
- TOUCH_IDLE
+
+ TOUCH_IDLE
-
-
-
+
+
+
- TOUCH_TOUCH
+
+ TOUCH_TOUCH
-
-
-
-
-
+
- TOUCH_TOUCH
+
+ TOUCH_TOUCH
-
-
-
+
+
+
- TOUCH_IDLE
+
+ TOUCH_IDLE
-
-
-
+
+
+
- TOUCH_IDLE
+
+ TOUCH_IDLE
-
-
-
+
+
+
- TOUCH_TOUCH
+
+ TOUCH_TOUCH
-
-
-
+
+
+
- TOUCH_IDLE
+
+ TOUCH_IDLE
-
-
-
+
+
+
- TOUCH_TOUCH
+
+ TOUCH_TOUCH
-
-
-
+
+
+
- that finger
- TOUCH_IDLE
+
+ that finger
+
+ TOUCH_IDLE
-
-
-
+
+
+
- TOUCH_DEAD
+
+ TOUCH_DEAD
-
-
-
+
+
+
- TOUCH_DEAD
+
+ TOUCH_DEAD
-
+
- TOUCH_DEAD
+
+ TOUCH_DEAD
-
+
- TOUCH_DEAD
+
+ TOUCH_DEAD
-
-
-
+
+
+
- TOUCH_DEAD
+
+ TOUCH_DEAD
-
-
-
+
+
+
- TOUCH_DEAD
+
+ TOUCH_DEAD
-
-
-
+
+
+
- state ==
- TOUCH_TOUCH
+
+ state ==
+
+ TOUCH_TOUCH
-
+
- that finger state ==
- TOUCH_TOUCH
+
+ that finger state ==
+
+ TOUCH_TOUCH
-
-
+
+
-
- no
+
+
+ no
-
+
- TOUCH_DEAD
+
+ TOUCH_DEAD
-
-
-
+
+
+
- TOUCH_DEAD
+
+ TOUCH_DEAD
-
+
- TOUCH_DEAD
+
+ TOUCH_DEAD
+
+
+
+ first
+
+ finger down
+
+
+
+
+ MULTITAP
+
+
+
+
+
+
+
+
+ timeout
+
+
+
+
+
+
+
+
+ IDLE
+
+
+
+
+
+
+
+
+
+
+ MULTITAP_DOWN
+
+
+
+
+ button 1
+
+ press
+
+
+
+
+
+
+
+
+ first
+
+ finger up
+
+
+
+
+
+
+ button 1
+
+ release
+
+
+
+
+
+
+ timeout
+
+
+
+
+ second
+
+ finger down
+
+
+
+
+ move >
+
+ threshold
+
+
+
+
+
+
+
+
+
+
+ button 1
+
+ release
+
+
+
+
+ button 1
+
+ press
+
+
+
+
+
+
+
+
+
+
+ button 1
+
+ release
+
+
+
+
+ button 1
+
+ press
+
+
+
+
+
+
+
+
+
+
+ button 1
+
+ release
+
+
+
+
+ button 1
+
+ press
+
+
+
+
+
+
+
+
+
+
+
+
+ TOUCH_TOUCH
+
+
+
+
+
+
+ TOUCH_IDLE
+
+
+
+
+
+
+ phys
+
+ button
+
+ press
+
+
+
+
+
+
+
+
+
+
+ DRAGGING_OR_TAP
+
+
+
+
+ first
+
+ finger up
+
+
+
+
+
+
+ timeout
+
+
+
+
+
+
+
+
+ move >
+
+ threshold
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TOUCH_IDLE
+
+
+
+
+
+
+
diff --git a/include/linux/input.h b/include/linux/input.h
index 2ac46630..4bf3d6d4 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -162,6 +162,7 @@ struct input_keymap_entry {
#define INPUT_PROP_SEMI_MT 0x03 /* touch rectangle only */
#define INPUT_PROP_TOPBUTTONPAD 0x04 /* softbuttons at top of pad */
#define INPUT_PROP_POINTING_STICK 0x05 /* is a pointing stick */
+#define INPUT_PROP_ACCELEROMETER 0x06 /* has accelerometer */
#define INPUT_PROP_MAX 0x1f
#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1)
@@ -459,7 +460,10 @@ struct input_keymap_entry {
#define KEY_VIDEO_NEXT 241 /* drive next video source */
#define KEY_VIDEO_PREV 242 /* drive previous video source */
#define KEY_BRIGHTNESS_CYCLE 243 /* brightness up, after max is min */
-#define KEY_BRIGHTNESS_ZERO 244 /* brightness off, use ambient */
+#define KEY_BRIGHTNESS_AUTO 244 /* Set Auto Brightness: manual
+ brightness control is off,
+ rely on ambient */
+#define KEY_BRIGHTNESS_ZERO KEY_BRIGHTNESS_AUTO
#define KEY_DISPLAY_OFF 245 /* display device to off state */
#define KEY_WWAN 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */
@@ -629,6 +633,7 @@ struct input_keymap_entry {
#define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */
#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */
#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */
+#define KEY_BRIGHTNESS_TOGGLE KEY_DISPLAYTOGGLE
#define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */
#define KEY_LOGOFF 0x1b1 /* AL Logoff */
@@ -720,6 +725,24 @@ struct input_keymap_entry {
#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */
+#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */
+#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */
+#define KEY_JOURNAL 0x242 /* AL Log/Journal/Timecard */
+#define KEY_CONTROLPANEL 0x243 /* AL Control Panel */
+#define KEY_APPSELECT 0x244 /* AL Select Task/Application */
+#define KEY_SCREENSAVER 0x245 /* AL Screen Saver */
+#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */
+
+#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */
+#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */
+
+#define KEY_KBDINPUTASSIST_PREV 0x260
+#define KEY_KBDINPUTASSIST_NEXT 0x261
+#define KEY_KBDINPUTASSIST_PREVGROUP 0x262
+#define KEY_KBDINPUTASSIST_NEXTGROUP 0x263
+#define KEY_KBDINPUTASSIST_ACCEPT 0x264
+#define KEY_KBDINPUTASSIST_CANCEL 0x265
+
#define BTN_TRIGGER_HAPPY 0x2c0
#define BTN_TRIGGER_HAPPY1 0x2c0
#define BTN_TRIGGER_HAPPY2 0x2c1
@@ -945,7 +968,8 @@ struct input_keymap_entry {
*/
#define MT_TOOL_FINGER 0
#define MT_TOOL_PEN 1
-#define MT_TOOL_MAX 1
+#define MT_TOOL_PALM 2
+#define MT_TOOL_MAX 2
/*
* Values describing the status of a force-feedback effect
diff --git a/src/Makefile.am b/src/Makefile.am
index d5cd4f41..343e75c7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,6 @@
lib_LTLIBRARIES = libinput.la
-noinst_LTLIBRARIES = libinput-util.la
+noinst_LTLIBRARIES = libinput-util.la \
+ libfilter.la
include_HEADERS = \
libinput.h
@@ -10,14 +11,15 @@ libinput_la_SOURCES = \
libinput-private.h \
evdev.c \
evdev.h \
- evdev-tablet.c \
- evdev-tablet.h \
+ evdev-middle-button.c \
evdev-mt-touchpad.c \
evdev-mt-touchpad.h \
evdev-mt-touchpad-tap.c \
evdev-mt-touchpad-buttons.c \
evdev-mt-touchpad-edge-scroll.c \
evdev-mt-touchpad-gestures.c \
+ evdev-tablet.c \
+ evdev-tablet.h \
filter.c \
filter.h \
filter-private.h \
@@ -52,6 +54,13 @@ libinput_util_la_CFLAGS = -I$(top_srcdir)/include \
$(LIBUDEV_CFLAGS) \
$(GCC_CFLAGS)
+libfilter_la_SOURCES = \
+ filter.c \
+ filter.h \
+ filter-private.h
+libfilter_la_LIBADD =
+libfilter_la_CFLAGS =
+
libinput_la_LDFLAGS = -version-info $(LIBINPUT_LT_VERSION) -shared \
-Wl,--version-script=$(srcdir)/libinput.sym
diff --git a/src/evdev-middle-button.c b/src/evdev-middle-button.c
new file mode 100644
index 00000000..328cf6c3
--- /dev/null
+++ b/src/evdev-middle-button.c
@@ -0,0 +1,716 @@
+/*
+ * Copyright © 2014 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include
+
+#include "evdev.h"
+
+#define MIDDLEBUTTON_TIMEOUT 50
+
+/*****************************************
+ * BEFORE YOU EDIT THIS FILE, look at the state diagram in
+ * doc/middle-button-emulation-state-machine.svg, or online at
+ * https://drive.google.com/file/d/0B1NwWmji69nodUJncXRMc1FvY1k/view?usp=sharing
+ * (it's a http://draw.io diagram)
+ *
+ * Any changes in this file must be represented in the diagram.
+ *
+ * Note in regards to the state machine: it only handles left, right and
+ * emulated middle button clicks, all other button events are passed
+ * through. When in the PASSTHROUGH state, all events are passed through
+ * as-is.
+ */
+
+#define CASE_RETURN_STRING(a) case a: return #a;
+
+static inline const char*
+middlebutton_state_to_str(enum evdev_middlebutton_state state)
+{
+ switch (state) {
+ CASE_RETURN_STRING(MIDDLEBUTTON_IDLE);
+ CASE_RETURN_STRING(MIDDLEBUTTON_LEFT_DOWN);
+ CASE_RETURN_STRING(MIDDLEBUTTON_RIGHT_DOWN);
+ CASE_RETURN_STRING(MIDDLEBUTTON_MIDDLE);
+ CASE_RETURN_STRING(MIDDLEBUTTON_LEFT_UP_PENDING);
+ CASE_RETURN_STRING(MIDDLEBUTTON_RIGHT_UP_PENDING);
+ CASE_RETURN_STRING(MIDDLEBUTTON_PASSTHROUGH);
+ CASE_RETURN_STRING(MIDDLEBUTTON_IGNORE_LR);
+ CASE_RETURN_STRING(MIDDLEBUTTON_IGNORE_L);
+ CASE_RETURN_STRING(MIDDLEBUTTON_IGNORE_R);
+ }
+
+ return NULL;
+}
+
+static inline const char*
+middlebutton_event_to_str(enum evdev_middlebutton_event event)
+{
+ switch (event) {
+ CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_L_DOWN);
+ CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_R_DOWN);
+ CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_OTHER);
+ CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_L_UP);
+ CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_R_UP);
+ CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_TIMEOUT);
+ CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_ALL_UP);
+ }
+
+ return NULL;
+}
+
+static void
+middlebutton_state_error(struct evdev_device *device,
+ enum evdev_middlebutton_event event)
+{
+ log_bug_libinput(device->base.seat->libinput,
+ "Invalid event %s in middle btn state %s\n",
+ middlebutton_event_to_str(event),
+ middlebutton_state_to_str(device->middlebutton.state));
+}
+
+static void
+middlebutton_timer_set(struct evdev_device *device, uint64_t now)
+{
+ libinput_timer_set(&device->middlebutton.timer,
+ now + MIDDLEBUTTON_TIMEOUT);
+}
+
+static void
+middlebutton_timer_cancel(struct evdev_device *device)
+{
+ libinput_timer_cancel(&device->middlebutton.timer);
+}
+
+static inline void
+middlebutton_set_state(struct evdev_device *device,
+ enum evdev_middlebutton_state state,
+ uint64_t now)
+{
+ switch (state) {
+ case MIDDLEBUTTON_LEFT_DOWN:
+ case MIDDLEBUTTON_RIGHT_DOWN:
+ middlebutton_timer_set(device, now);
+ device->middlebutton.first_event_time = now;
+ break;
+ case MIDDLEBUTTON_IDLE:
+ case MIDDLEBUTTON_MIDDLE:
+ case MIDDLEBUTTON_LEFT_UP_PENDING:
+ case MIDDLEBUTTON_RIGHT_UP_PENDING:
+ case MIDDLEBUTTON_PASSTHROUGH:
+ case MIDDLEBUTTON_IGNORE_LR:
+ case MIDDLEBUTTON_IGNORE_L:
+ case MIDDLEBUTTON_IGNORE_R:
+ middlebutton_timer_cancel(device);
+ break;
+ }
+
+ device->middlebutton.state = state;
+}
+
+static void
+middlebutton_post_event(struct evdev_device *device,
+ uint64_t now,
+ int button,
+ enum libinput_button_state state)
+{
+ evdev_pointer_notify_button(device,
+ now,
+ button,
+ state);
+}
+
+static int
+evdev_middlebutton_idle_handle_event(struct evdev_device *device,
+ uint64_t time,
+ enum evdev_middlebutton_event event)
+{
+ switch (event) {
+ case MIDDLEBUTTON_EVENT_L_DOWN:
+ middlebutton_set_state(device, MIDDLEBUTTON_LEFT_DOWN, time);
+ break;
+ case MIDDLEBUTTON_EVENT_R_DOWN:
+ middlebutton_set_state(device, MIDDLEBUTTON_RIGHT_DOWN, time);
+ break;
+ case MIDDLEBUTTON_EVENT_OTHER:
+ return 0;
+ case MIDDLEBUTTON_EVENT_R_UP:
+ case MIDDLEBUTTON_EVENT_L_UP:
+ case MIDDLEBUTTON_EVENT_TIMEOUT:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_ALL_UP:
+ break;
+ }
+
+ return 1;
+}
+
+static int
+evdev_middlebutton_ldown_handle_event(struct evdev_device *device,
+ uint64_t time,
+ enum evdev_middlebutton_event event)
+{
+ switch (event) {
+ case MIDDLEBUTTON_EVENT_L_DOWN:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_R_DOWN:
+ middlebutton_post_event(device, time,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
+ break;
+ case MIDDLEBUTTON_EVENT_OTHER:
+ middlebutton_post_event(device, time,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ middlebutton_set_state(device,
+ MIDDLEBUTTON_PASSTHROUGH,
+ time);
+ return 0;
+ case MIDDLEBUTTON_EVENT_R_UP:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_L_UP:
+ middlebutton_post_event(device,
+ device->middlebutton.first_event_time,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ middlebutton_post_event(device, time,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
+ break;
+ case MIDDLEBUTTON_EVENT_TIMEOUT:
+ middlebutton_post_event(device,
+ device->middlebutton.first_event_time,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ middlebutton_set_state(device,
+ MIDDLEBUTTON_PASSTHROUGH,
+ time);
+ break;
+ case MIDDLEBUTTON_EVENT_ALL_UP:
+ middlebutton_state_error(device, event);
+ break;
+ }
+
+ return 1;
+}
+
+static int
+evdev_middlebutton_rdown_handle_event(struct evdev_device *device,
+ uint64_t time,
+ enum evdev_middlebutton_event event)
+{
+ switch (event) {
+ case MIDDLEBUTTON_EVENT_L_DOWN:
+ middlebutton_post_event(device, time,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
+ break;
+ case MIDDLEBUTTON_EVENT_R_DOWN:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_OTHER:
+ middlebutton_post_event(device,
+ device->middlebutton.first_event_time,
+ BTN_RIGHT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ middlebutton_set_state(device,
+ MIDDLEBUTTON_PASSTHROUGH,
+ time);
+ return 0;
+ case MIDDLEBUTTON_EVENT_R_UP:
+ middlebutton_post_event(device,
+ device->middlebutton.first_event_time,
+ BTN_RIGHT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ middlebutton_post_event(device, time,
+ BTN_RIGHT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
+ break;
+ case MIDDLEBUTTON_EVENT_L_UP:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_TIMEOUT:
+ middlebutton_post_event(device,
+ device->middlebutton.first_event_time,
+ BTN_RIGHT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ middlebutton_set_state(device,
+ MIDDLEBUTTON_PASSTHROUGH,
+ time);
+ break;
+ case MIDDLEBUTTON_EVENT_ALL_UP:
+ middlebutton_state_error(device, event);
+ break;
+ }
+
+ return 1;
+}
+
+static int
+evdev_middlebutton_middle_handle_event(struct evdev_device *device,
+ uint64_t time,
+ enum evdev_middlebutton_event event)
+{
+ switch (event) {
+ case MIDDLEBUTTON_EVENT_L_DOWN:
+ case MIDDLEBUTTON_EVENT_R_DOWN:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_OTHER:
+ middlebutton_post_event(device, time,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_LR, time);
+ return 0;
+ case MIDDLEBUTTON_EVENT_R_UP:
+ middlebutton_post_event(device, time,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ middlebutton_set_state(device,
+ MIDDLEBUTTON_LEFT_UP_PENDING,
+ time);
+ break;
+ case MIDDLEBUTTON_EVENT_L_UP:
+ middlebutton_post_event(device, time,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ middlebutton_set_state(device,
+ MIDDLEBUTTON_RIGHT_UP_PENDING,
+ time);
+ break;
+ case MIDDLEBUTTON_EVENT_TIMEOUT:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_ALL_UP:
+ middlebutton_state_error(device, event);
+ break;
+ }
+
+ return 1;
+}
+
+static int
+evdev_middlebutton_lup_pending_handle_event(struct evdev_device *device,
+ uint64_t time,
+ enum evdev_middlebutton_event event)
+{
+ switch (event) {
+ case MIDDLEBUTTON_EVENT_L_DOWN:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_R_DOWN:
+ middlebutton_post_event(device, time,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
+ break;
+ case MIDDLEBUTTON_EVENT_OTHER:
+ middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_L, time);
+ return 0;
+ case MIDDLEBUTTON_EVENT_R_UP:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_L_UP:
+ middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
+ break;
+ case MIDDLEBUTTON_EVENT_TIMEOUT:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_ALL_UP:
+ middlebutton_state_error(device, event);
+ break;
+ }
+
+ return 1;
+}
+
+static int
+evdev_middlebutton_rup_pending_handle_event(struct evdev_device *device,
+ uint64_t time,
+ enum evdev_middlebutton_event event)
+{
+ switch (event) {
+ case MIDDLEBUTTON_EVENT_L_DOWN:
+ middlebutton_post_event(device, time,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
+ break;
+ case MIDDLEBUTTON_EVENT_R_DOWN:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_OTHER:
+ middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_R, time);
+ return 0;
+ case MIDDLEBUTTON_EVENT_R_UP:
+ middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
+ break;
+ case MIDDLEBUTTON_EVENT_L_UP:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_TIMEOUT:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_ALL_UP:
+ middlebutton_state_error(device, event);
+ break;
+ }
+
+ return 1;
+}
+
+static int
+evdev_middlebutton_passthrough_handle_event(struct evdev_device *device,
+ uint64_t time,
+ enum evdev_middlebutton_event event)
+{
+ switch (event) {
+ case MIDDLEBUTTON_EVENT_L_DOWN:
+ case MIDDLEBUTTON_EVENT_R_DOWN:
+ case MIDDLEBUTTON_EVENT_OTHER:
+ case MIDDLEBUTTON_EVENT_R_UP:
+ case MIDDLEBUTTON_EVENT_L_UP:
+ return 0;
+ case MIDDLEBUTTON_EVENT_TIMEOUT:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_ALL_UP:
+ middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
+ break;
+ }
+
+ return 1;
+}
+
+static int
+evdev_middlebutton_ignore_lr_handle_event(struct evdev_device *device,
+ uint64_t time,
+ enum evdev_middlebutton_event event)
+{
+ switch (event) {
+ case MIDDLEBUTTON_EVENT_L_DOWN:
+ case MIDDLEBUTTON_EVENT_R_DOWN:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_OTHER:
+ return 0;
+ case MIDDLEBUTTON_EVENT_R_UP:
+ middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_L, time);
+ break;
+ case MIDDLEBUTTON_EVENT_L_UP:
+ middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_R, time);
+ break;
+ case MIDDLEBUTTON_EVENT_TIMEOUT:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_ALL_UP:
+ middlebutton_state_error(device, event);
+ break;
+ }
+
+ return 1;
+}
+
+static int
+evdev_middlebutton_ignore_l_handle_event(struct evdev_device *device,
+ uint64_t time,
+ enum evdev_middlebutton_event event)
+{
+ switch (event) {
+ case MIDDLEBUTTON_EVENT_L_DOWN:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_R_DOWN:
+ return 0;
+ case MIDDLEBUTTON_EVENT_OTHER:
+ case MIDDLEBUTTON_EVENT_R_UP:
+ return 0;
+ case MIDDLEBUTTON_EVENT_L_UP:
+ middlebutton_set_state(device,
+ MIDDLEBUTTON_PASSTHROUGH,
+ time);
+ break;
+ case MIDDLEBUTTON_EVENT_TIMEOUT:
+ case MIDDLEBUTTON_EVENT_ALL_UP:
+ middlebutton_state_error(device, event);
+ break;
+ }
+
+ return 1;
+}
+static int
+evdev_middlebutton_ignore_r_handle_event(struct evdev_device *device,
+ uint64_t time,
+ enum evdev_middlebutton_event event)
+{
+ switch (event) {
+ case MIDDLEBUTTON_EVENT_L_DOWN:
+ return 0;
+ case MIDDLEBUTTON_EVENT_R_DOWN:
+ middlebutton_state_error(device, event);
+ break;
+ case MIDDLEBUTTON_EVENT_OTHER:
+ return 0;
+ case MIDDLEBUTTON_EVENT_R_UP:
+ middlebutton_set_state(device,
+ MIDDLEBUTTON_PASSTHROUGH,
+ time);
+ break;
+ case MIDDLEBUTTON_EVENT_L_UP:
+ return 0;
+ case MIDDLEBUTTON_EVENT_TIMEOUT:
+ case MIDDLEBUTTON_EVENT_ALL_UP:
+ break;
+ }
+
+ return 1;
+}
+
+static int
+evdev_middlebutton_handle_event(struct evdev_device *device,
+ uint64_t time,
+ enum evdev_middlebutton_event event)
+{
+ int rc;
+ enum evdev_middlebutton_state current;
+
+ current = device->middlebutton.state;
+
+ switch (current) {
+ case MIDDLEBUTTON_IDLE:
+ rc = evdev_middlebutton_idle_handle_event(device, time, event);
+ break;
+ case MIDDLEBUTTON_LEFT_DOWN:
+ rc = evdev_middlebutton_ldown_handle_event(device, time, event);
+ break;
+ case MIDDLEBUTTON_RIGHT_DOWN:
+ rc = evdev_middlebutton_rdown_handle_event(device, time, event);
+ break;
+ case MIDDLEBUTTON_MIDDLE:
+ rc = evdev_middlebutton_middle_handle_event(device, time, event);
+ break;
+ case MIDDLEBUTTON_LEFT_UP_PENDING:
+ rc = evdev_middlebutton_lup_pending_handle_event(device,
+ time,
+ event);
+ break;
+ case MIDDLEBUTTON_RIGHT_UP_PENDING:
+ rc = evdev_middlebutton_rup_pending_handle_event(device,
+ time,
+ event);
+ break;
+ case MIDDLEBUTTON_PASSTHROUGH:
+ rc = evdev_middlebutton_passthrough_handle_event(device,
+ time,
+ event);
+ break;
+ case MIDDLEBUTTON_IGNORE_LR:
+ rc = evdev_middlebutton_ignore_lr_handle_event(device,
+ time,
+ event);
+ break;
+ case MIDDLEBUTTON_IGNORE_L:
+ rc = evdev_middlebutton_ignore_l_handle_event(device,
+ time,
+ event);
+ break;
+ case MIDDLEBUTTON_IGNORE_R:
+ rc = evdev_middlebutton_ignore_r_handle_event(device,
+ time,
+ event);
+ break;
+ }
+
+ log_debug(device->base.seat->libinput,
+ "middlebuttonstate: %s → %s → %s, rc %d\n",
+ middlebutton_state_to_str(current),
+ middlebutton_event_to_str(event),
+ middlebutton_state_to_str(device->middlebutton.state),
+ rc);
+
+ return rc;
+}
+
+static inline void
+evdev_middlebutton_apply_config(struct evdev_device *device)
+{
+ if (device->middlebutton.want_enabled ==
+ device->middlebutton.enabled)
+ return;
+
+ if (device->middlebutton.button_mask != 0)
+ return;
+
+ device->middlebutton.enabled = device->middlebutton.want_enabled;
+}
+
+bool
+evdev_middlebutton_filter_button(struct evdev_device *device,
+ uint64_t time,
+ int button,
+ enum libinput_button_state state)
+{
+ enum evdev_middlebutton_event event;
+ bool is_press = state == LIBINPUT_BUTTON_STATE_PRESSED;
+ int rc;
+ unsigned int bit = (button - BTN_LEFT);
+ uint32_t old_mask = 0;
+
+ if (!device->middlebutton.enabled)
+ return false;
+
+ switch (button) {
+ case BTN_LEFT:
+ if (is_press)
+ event = MIDDLEBUTTON_EVENT_L_DOWN;
+ else
+ event = MIDDLEBUTTON_EVENT_L_UP;
+ break;
+ case BTN_RIGHT:
+ if (is_press)
+ event = MIDDLEBUTTON_EVENT_R_DOWN;
+ else
+ event = MIDDLEBUTTON_EVENT_R_UP;
+ break;
+
+ /* BTN_MIDDLE counts as "other" and resets middle button
+ * emulation */
+ case BTN_MIDDLE:
+ default:
+ event = MIDDLEBUTTON_EVENT_OTHER;
+ break;
+ }
+
+ if (button < BTN_LEFT ||
+ bit >= sizeof(device->middlebutton.button_mask) * 8) {
+ log_bug_libinput(device->base.seat->libinput,
+ "Button mask too small for %d\n",
+ libevdev_event_code_get_name(EV_KEY,
+ button));
+ return true;
+ }
+
+ rc = evdev_middlebutton_handle_event(device, time, event);
+
+ old_mask = device->middlebutton.button_mask;
+ if (is_press)
+ device->middlebutton.button_mask |= 1 << bit;
+ else
+ device->middlebutton.button_mask &= ~(1 << bit);
+
+ if (old_mask != device->middlebutton.button_mask &&
+ device->middlebutton.button_mask == 0) {
+ evdev_middlebutton_handle_event(device,
+ time,
+ MIDDLEBUTTON_EVENT_ALL_UP);
+ evdev_middlebutton_apply_config(device);
+ }
+
+ return rc;
+}
+
+static void
+evdev_middlebutton_handle_timeout(uint64_t now, void *data)
+{
+ struct evdev_device *device = (struct evdev_device*)data;
+
+ evdev_middlebutton_handle_event(device,
+ libinput_now(device->base.seat->libinput),
+ MIDDLEBUTTON_EVENT_TIMEOUT);
+}
+
+static int
+evdev_middlebutton_is_available(struct libinput_device *device)
+{
+ return 1;
+}
+
+static enum libinput_config_status
+evdev_middlebutton_set(struct libinput_device *device,
+ enum libinput_config_middle_emulation_state enable)
+{
+ struct evdev_device *evdev = (struct evdev_device*)device;
+
+ switch (enable) {
+ case LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED:
+ evdev->middlebutton.want_enabled = true;
+ break;
+ case LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED:
+ evdev->middlebutton.want_enabled = false;
+ break;
+ default:
+ return LIBINPUT_CONFIG_STATUS_INVALID;
+ }
+
+ evdev_middlebutton_apply_config(evdev);
+
+ return LIBINPUT_CONFIG_STATUS_SUCCESS;
+}
+
+static enum libinput_config_middle_emulation_state
+evdev_middlebutton_get(struct libinput_device *device)
+{
+ struct evdev_device *evdev = (struct evdev_device*)device;
+
+ return evdev->middlebutton.enabled ?
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED :
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
+}
+
+static enum libinput_config_middle_emulation_state
+evdev_middlebutton_get_default(struct libinput_device *device)
+{
+ struct evdev_device *evdev = (struct evdev_device*)device;
+
+ return evdev->middlebutton.enabled_default ?
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED :
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
+}
+
+void
+evdev_init_middlebutton(struct evdev_device *device,
+ bool enable,
+ bool want_config)
+{
+ libinput_timer_init(&device->middlebutton.timer,
+ device->base.seat->libinput,
+ evdev_middlebutton_handle_timeout,
+ device);
+ device->middlebutton.enabled_default = enable;
+ device->middlebutton.want_enabled = enable;
+ device->middlebutton.enabled = enable;
+
+ if (!want_config)
+ return;
+
+ device->middlebutton.config.available = evdev_middlebutton_is_available;
+ device->middlebutton.config.set = evdev_middlebutton_set;
+ device->middlebutton.config.get = evdev_middlebutton_get;
+ device->middlebutton.config.get_default = evdev_middlebutton_get_default;
+ device->base.config.middle_emulation = &device->middlebutton.config;
+}
diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c
index 18c32fda..43e983b2 100644
--- a/src/evdev-mt-touchpad-buttons.c
+++ b/src/evdev-mt-touchpad-buttons.c
@@ -144,12 +144,15 @@ tp_button_set_leave_timer(struct tp_dispatch *tp, struct tp_touch *t)
* as described in the state machine diagram.
*/
static void
-tp_button_set_state(struct tp_dispatch *tp, struct tp_touch *t,
- enum button_state new_state, enum button_event event)
+tp_button_set_state(struct tp_dispatch *tp,
+ struct tp_touch *t,
+ enum button_state new_state,
+ enum button_event event)
{
libinput_timer_cancel(&t->button.timer);
t->button.state = new_state;
+
switch (t->button.state) {
case BUTTON_STATE_NONE:
t->button.curr = 0;
@@ -235,7 +238,9 @@ tp_button_bottom_handle_event(struct tp_dispatch *tp,
case BUTTON_EVENT_IN_BOTTOM_R:
case BUTTON_EVENT_IN_BOTTOM_L:
if (event != t->button.curr)
- tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM,
+ tp_button_set_state(tp,
+ t,
+ BUTTON_STATE_BOTTOM,
event);
break;
case BUTTON_EVENT_IN_TOP_R:
@@ -256,8 +261,8 @@ tp_button_bottom_handle_event(struct tp_dispatch *tp,
static void
tp_button_top_handle_event(struct tp_dispatch *tp,
- struct tp_touch *t,
- enum button_event event)
+ struct tp_touch *t,
+ enum button_event event)
{
switch (event) {
case BUTTON_EVENT_IN_BOTTOM_R:
@@ -268,7 +273,9 @@ tp_button_top_handle_event(struct tp_dispatch *tp,
case BUTTON_EVENT_IN_TOP_M:
case BUTTON_EVENT_IN_TOP_L:
if (event != t->button.curr)
- tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW,
+ tp_button_set_state(tp,
+ t,
+ BUTTON_STATE_TOP_NEW,
event);
break;
case BUTTON_EVENT_IN_AREA:
@@ -286,8 +293,8 @@ tp_button_top_handle_event(struct tp_dispatch *tp,
static void
tp_button_top_new_handle_event(struct tp_dispatch *tp,
- struct tp_touch *t,
- enum button_event event)
+ struct tp_touch *t,
+ enum button_event event)
{
switch(event) {
case BUTTON_EVENT_IN_BOTTOM_R:
@@ -298,7 +305,9 @@ tp_button_top_new_handle_event(struct tp_dispatch *tp,
case BUTTON_EVENT_IN_TOP_M:
case BUTTON_EVENT_IN_TOP_L:
if (event != t->button.curr)
- tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW,
+ tp_button_set_state(tp,
+ t,
+ BUTTON_STATE_TOP_NEW,
event);
break;
case BUTTON_EVENT_IN_AREA:
@@ -320,18 +329,22 @@ tp_button_top_new_handle_event(struct tp_dispatch *tp,
static void
tp_button_top_to_ignore_handle_event(struct tp_dispatch *tp,
- struct tp_touch *t,
- enum button_event event)
+ struct tp_touch *t,
+ enum button_event event)
{
switch(event) {
case BUTTON_EVENT_IN_TOP_R:
case BUTTON_EVENT_IN_TOP_M:
case BUTTON_EVENT_IN_TOP_L:
if (event == t->button.curr)
- tp_button_set_state(tp, t, BUTTON_STATE_TOP,
+ tp_button_set_state(tp,
+ t,
+ BUTTON_STATE_TOP,
event);
else
- tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW,
+ tp_button_set_state(tp,
+ t,
+ BUTTON_STATE_TOP_NEW,
event);
break;
case BUTTON_EVENT_IN_BOTTOM_R:
@@ -352,8 +365,8 @@ tp_button_top_to_ignore_handle_event(struct tp_dispatch *tp,
static void
tp_button_ignore_handle_event(struct tp_dispatch *tp,
- struct tp_touch *t,
- enum button_event event)
+ struct tp_touch *t,
+ enum button_event event)
{
switch (event) {
case BUTTON_EVENT_IN_BOTTOM_R:
@@ -426,18 +439,22 @@ tp_button_handle_state(struct tp_dispatch *tp, uint64_t time)
if (t->state == TOUCH_END) {
tp_button_handle_event(tp, t, BUTTON_EVENT_UP, time);
} else if (t->dirty) {
+ enum button_event event;
+
if (is_inside_bottom_right_area(tp, t))
- tp_button_handle_event(tp, t, BUTTON_EVENT_IN_BOTTOM_R, time);
+ event = BUTTON_EVENT_IN_BOTTOM_R;
else if (is_inside_bottom_left_area(tp, t))
- tp_button_handle_event(tp, t, BUTTON_EVENT_IN_BOTTOM_L, time);
+ event = BUTTON_EVENT_IN_BOTTOM_L;
else if (is_inside_top_right_area(tp, t))
- tp_button_handle_event(tp, t, BUTTON_EVENT_IN_TOP_R, time);
+ event = BUTTON_EVENT_IN_TOP_R;
else if (is_inside_top_middle_area(tp, t))
- tp_button_handle_event(tp, t, BUTTON_EVENT_IN_TOP_M, time);
+ event = BUTTON_EVENT_IN_TOP_M;
else if (is_inside_top_left_area(tp, t))
- tp_button_handle_event(tp, t, BUTTON_EVENT_IN_TOP_L, time);
+ event = BUTTON_EVENT_IN_TOP_L;
else
- tp_button_handle_event(tp, t, BUTTON_EVENT_IN_AREA, time);
+ event = BUTTON_EVENT_IN_AREA;
+
+ tp_button_handle_event(tp, t, event, time);
}
if (tp->queued & TOUCHPAD_EVENT_BUTTON_RELEASE)
tp_button_handle_event(tp, t, BUTTON_EVENT_RELEASE, time);
@@ -629,12 +646,25 @@ tp_button_config_click_get_method(struct libinput_device *device)
static enum libinput_config_click_method
tp_click_get_default_method(struct tp_dispatch *tp)
{
+ struct evdev_device *device = tp->device;
+
if (!tp->buttons.is_clickpad)
return LIBINPUT_CONFIG_CLICK_METHOD_NONE;
else if (libevdev_get_id_vendor(tp->device->evdev) == VENDOR_ID_APPLE)
return LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
- else
- return LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
+
+ switch (device->model) {
+ case EVDEV_MODEL_CHROMEBOOK:
+ case EVDEV_MODEL_SYSTEM76_BONOBO:
+ case EVDEV_MODEL_SYSTEM76_GALAGO:
+ case EVDEV_MODEL_SYSTEM76_KUDU:
+ case EVDEV_MODEL_CLEVO_W740SU:
+ return LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
+ default:
+ break;
+ }
+
+ return LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
}
static enum libinput_config_click_method
@@ -694,6 +724,10 @@ tp_init_buttons(struct tp_dispatch *tp,
tp_init_top_softbuttons(tp, device, 1.0);
+ if (!tp->buttons.is_clickpad &&
+ !libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE))
+ evdev_init_middlebutton(tp->device, true, false);
+
tp_for_each_touch(tp, t) {
t->button.state = BUTTON_STATE_NONE;
libinput_timer_init(&t->button.timer,
@@ -734,10 +768,10 @@ tp_post_physical_buttons(struct tp_dispatch *tp, uint64_t time)
state = LIBINPUT_BUTTON_STATE_RELEASED;
b = evdev_to_left_handed(tp->device, button);
- evdev_pointer_notify_button(tp->device,
- time,
- b,
- state);
+ evdev_pointer_notify_physical_button(tp->device,
+ time,
+ b,
+ state);
}
button++;
@@ -765,8 +799,10 @@ tp_notify_clickpadbutton(struct tp_dispatch *tp,
event.type = EV_KEY;
event.code = button;
event.value = (state == LIBINPUT_BUTTON_STATE_PRESSED) ? 1 : 0;
- dispatch->interface->process(dispatch, tp->buttons.trackpoint,
- &event, time);
+ dispatch->interface->process(dispatch,
+ tp->buttons.trackpoint,
+ &event,
+ time);
return 1;
}
@@ -779,8 +815,9 @@ tp_notify_clickpadbutton(struct tp_dispatch *tp,
* by the softbutton code with one based on the number of fingers.
*/
if (tp->buttons.click_method == LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER &&
- state == LIBINPUT_BUTTON_STATE_PRESSED) {
+ state == LIBINPUT_BUTTON_STATE_PRESSED) {
switch (tp->nfingers_down) {
+ case 0:
case 1: button = BTN_LEFT; break;
case 2: button = BTN_RIGHT; break;
case 3: button = BTN_MIDDLE; break;
@@ -814,46 +851,48 @@ tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time)
if (current) {
struct tp_touch *t;
+ uint32_t area = 0;
tp_for_each_touch(tp, t) {
switch (t->button.curr) {
case BUTTON_EVENT_IN_AREA:
- button |= AREA;
+ area |= AREA;
break;
case BUTTON_EVENT_IN_TOP_L:
is_top = 1;
/* fallthrough */
case BUTTON_EVENT_IN_BOTTOM_L:
- button |= LEFT;
+ area |= LEFT;
break;
case BUTTON_EVENT_IN_TOP_M:
is_top = 1;
- button |= MIDDLE;
+ area |= MIDDLE;
break;
case BUTTON_EVENT_IN_TOP_R:
is_top = 1;
/* fallthrough */
case BUTTON_EVENT_IN_BOTTOM_R:
- button |= RIGHT;
+ area |= RIGHT;
break;
default:
break;
}
}
- if (button == 0) {
+ if (area == 0 &&
+ tp->buttons.click_method != LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) {
/* No touches, wait for a touch before processing */
tp->buttons.click_pending = true;
return 0;
}
- if ((button & MIDDLE) || ((button & LEFT) && (button & RIGHT)))
+ if ((area & MIDDLE) || ((area & LEFT) && (area & RIGHT)))
button = evdev_to_left_handed(tp->device, BTN_MIDDLE);
- else if (button & RIGHT)
+ else if (area & RIGHT)
button = evdev_to_left_handed(tp->device, BTN_RIGHT);
- else if (button & LEFT)
+ else if (area & LEFT)
button = evdev_to_left_handed(tp->device, BTN_LEFT);
- else /* main area is always BTN_LEFT */
+ else /* main or no area (for clickfinger) is always BTN_LEFT */
button = BTN_LEFT;
tp->buttons.active = button;
@@ -870,8 +909,11 @@ tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time)
tp->buttons.click_pending = false;
if (button)
- return tp_notify_clickpadbutton(tp, time, button, is_top, state);
-
+ return tp_notify_clickpadbutton(tp,
+ time,
+ button,
+ is_top,
+ state);
return 0;
}
@@ -893,5 +935,6 @@ tp_button_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
bool
tp_button_is_inside_softbutton_area(struct tp_dispatch *tp, struct tp_touch *t)
{
- return is_inside_top_button_area(tp, t) || is_inside_bottom_button_area(tp, t);
+ return is_inside_top_button_area(tp, t) ||
+ is_inside_bottom_button_area(tp, t);
}
diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c
index 26d6bbcf..369fdedc 100644
--- a/src/evdev-mt-touchpad-edge-scroll.c
+++ b/src/evdev-mt-touchpad-edge-scroll.c
@@ -29,6 +29,8 @@
#include "evdev-mt-touchpad.h"
+#define CASE_RETURN_STRING(a) case a: return #a
+
#define DEFAULT_SCROLL_LOCK_TIMEOUT 300 /* ms */
/* Use a reasonably large threshold until locked into scrolling mode, to
avoid accidentally locking in scrolling mode when trying to use the entire
@@ -44,6 +46,32 @@ enum scroll_event {
SCROLL_EVENT_POSTED,
};
+static inline const char*
+edge_state_to_str(enum tp_edge_scroll_touch_state state)
+{
+
+ switch (state) {
+ CASE_RETURN_STRING(EDGE_SCROLL_TOUCH_STATE_NONE);
+ CASE_RETURN_STRING(EDGE_SCROLL_TOUCH_STATE_EDGE_NEW);
+ CASE_RETURN_STRING(EDGE_SCROLL_TOUCH_STATE_EDGE);
+ CASE_RETURN_STRING(EDGE_SCROLL_TOUCH_STATE_AREA);
+ }
+ return NULL;
+}
+
+static inline const char*
+edge_event_to_str(enum scroll_event event)
+{
+ switch (event) {
+ CASE_RETURN_STRING(SCROLL_EVENT_TOUCH);
+ CASE_RETURN_STRING(SCROLL_EVENT_MOTION);
+ CASE_RETURN_STRING(SCROLL_EVENT_RELEASE);
+ CASE_RETURN_STRING(SCROLL_EVENT_TIMEOUT);
+ CASE_RETURN_STRING(SCROLL_EVENT_POSTED);
+ }
+ return NULL;
+}
+
static uint32_t
tp_touch_get_edge(struct tp_dispatch *tp, struct tp_touch *t)
{
@@ -204,7 +232,10 @@ tp_edge_scroll_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum scroll_event event)
{
- switch (t->scroll.edge_state) {
+ struct libinput *libinput = tp->device->base.seat->libinput;
+ enum tp_edge_scroll_touch_state current = t->scroll.edge_state;
+
+ switch (current) {
case EDGE_SCROLL_TOUCH_STATE_NONE:
tp_edge_scroll_handle_none(tp, t, event);
break;
@@ -218,6 +249,12 @@ tp_edge_scroll_handle_event(struct tp_dispatch *tp,
tp_edge_scroll_handle_area(tp, t, event);
break;
}
+
+ log_debug(libinput,
+ "edge state: %s → %s → %s\n",
+ edge_state_to_str(current),
+ edge_event_to_str(event),
+ edge_state_to_str(t->scroll.edge_state));
}
static void
@@ -313,7 +350,6 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
struct tp_touch *t;
enum libinput_pointer_axis axis;
double *delta;
- double initial_dx, initial_dy, *initial_delta;
struct normalized_coords normalized;
const struct normalized_coords zero = { 0.0, 0.0 };
const struct discrete_coords zero_discrete = { 0.0, 0.0 };
@@ -340,20 +376,17 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
case EDGE_RIGHT:
axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
delta = &normalized.y;
- initial_delta = &initial_dy;
break;
case EDGE_BOTTOM:
axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
delta = &normalized.x;
- initial_delta = &initial_dx;
break;
default: /* EDGE_RIGHT | EDGE_BOTTOM */
continue; /* Don't know direction yet, skip */
}
normalized = tp_get_delta(t);
- tp_filter_motion(tp, &normalized.x, &normalized.y,
- NULL, NULL, time);
+ normalized = tp_filter_motion(tp, &normalized, time);
switch (t->scroll.edge_state) {
case EDGE_SCROLL_TOUCH_STATE_NONE:
@@ -363,16 +396,11 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
t->scroll.edge_state);
break;
case EDGE_SCROLL_TOUCH_STATE_EDGE_NEW:
- initial_dx = t->point.x - t->scroll.initial.x;
- initial_dy = t->point.y - t->scroll.initial.y;
- tp_normalize_delta(tp,
- initial_dx,
- initial_dy,
- &normalized);
- if (fabs(*initial_delta) < DEFAULT_SCROLL_THRESHOLD) {
- normalized.x = 0.0;
- normalized.y = 0.0;
- }
+ normalized = tp_normalize_delta(tp,
+ device_delta(t->point,
+ t->scroll.initial));
+ if (fabs(*delta) < DEFAULT_SCROLL_THRESHOLD)
+ normalized = zero;
break;
case EDGE_SCROLL_TOUCH_STATE_EDGE:
break;
diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c
index f852ff5a..57c07feb 100644
--- a/src/evdev-mt-touchpad-gestures.c
+++ b/src/evdev-mt-touchpad-gestures.c
@@ -39,7 +39,7 @@ tp_get_touches_delta(struct tp_dispatch *tp, bool average)
struct normalized_coords normalized;
struct normalized_coords delta = {0.0, 0.0};
- for (i = 0; i < tp->real_touches; i++) {
+ for (i = 0; i < tp->num_slots; i++) {
t = &tp->touches[i];
if (tp_touch_active(tp, t) && t->dirty) {
@@ -93,14 +93,13 @@ tp_gesture_post_pointer_motion(struct tp_dispatch *tp, uint64_t time)
/* When a clickpad is clicked, combine motion of all active touches */
if (tp->buttons.is_clickpad && tp->buttons.state)
- delta = tp_get_combined_touches_delta(tp);
+ unaccel = tp_get_combined_touches_delta(tp);
else
- delta = tp_get_average_touches_delta(tp);
+ unaccel = tp_get_average_touches_delta(tp);
- tp_filter_motion(tp, &delta.x, &delta.y, &unaccel.x, &unaccel.y, time);
+ delta = tp_filter_motion(tp, &unaccel, time);
- if (delta.x != 0.0 || delta.y != 0.0 ||
- unaccel.x != 0.0 || unaccel.y != 0.0) {
+ if (!normalized_is_zero(delta) || !normalized_is_zero(unaccel)) {
pointer_notify_motion(&tp->device->base, time,
&delta, &unaccel);
}
@@ -111,10 +110,23 @@ tp_gesture_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
{
struct normalized_coords delta;
- delta = tp_get_average_touches_delta(tp);
- tp_filter_motion(tp, &delta.x, &delta.y, NULL, NULL, time);
+ if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG)
+ return;
- if (delta.x == 0.0 && delta.y == 0.0)
+ /* On some semi-mt models slot 0 is more accurate, so for semi-mt
+ * we only use slot 0. */
+ if (tp->semi_mt) {
+ if (!tp->touches[0].dirty)
+ return;
+
+ delta = tp_get_delta(&tp->touches[0]);
+ } else {
+ delta = tp_get_average_touches_delta(tp);
+ }
+
+ delta = tp_filter_motion(tp, &delta, time);
+
+ if (normalized_is_zero(delta))
return;
tp_gesture_start(tp, time);
@@ -154,6 +166,9 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time)
void
tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
{
+ if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG)
+ return;
+
evdev_stop_scroll(tp->device,
time,
LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c
index 6bd7c582..fb8c9e43 100644
--- a/src/evdev-mt-touchpad-tap.c
+++ b/src/evdev-mt-touchpad-tap.c
@@ -34,9 +34,10 @@
#include "evdev-mt-touchpad.h"
-#define CASE_RETURN_STRING(a) case a: return #a;
+#define CASE_RETURN_STRING(a) case a: return #a
#define DEFAULT_TAP_TIMEOUT_PERIOD 180
+#define DEFAULT_DRAG_TIMEOUT_PERIOD 500
#define DEFAULT_TAP_MOVE_THRESHOLD TP_MM_TO_DPI_NORMALIZED(3)
enum tap_event {
@@ -59,8 +60,8 @@ enum tap_event {
*/
static inline const char*
-tap_state_to_str(enum tp_tap_state state) {
-
+tap_state_to_str(enum tp_tap_state state)
+{
switch(state) {
CASE_RETURN_STRING(TAP_STATE_IDLE);
CASE_RETURN_STRING(TAP_STATE_HOLD);
@@ -73,15 +74,18 @@ tap_state_to_str(enum tp_tap_state state) {
CASE_RETURN_STRING(TAP_STATE_DRAGGING);
CASE_RETURN_STRING(TAP_STATE_DRAGGING_WAIT);
CASE_RETURN_STRING(TAP_STATE_DRAGGING_OR_DOUBLETAP);
+ CASE_RETURN_STRING(TAP_STATE_DRAGGING_OR_TAP);
CASE_RETURN_STRING(TAP_STATE_DRAGGING_2);
+ CASE_RETURN_STRING(TAP_STATE_MULTITAP);
+ CASE_RETURN_STRING(TAP_STATE_MULTITAP_DOWN);
CASE_RETURN_STRING(TAP_STATE_DEAD);
}
return NULL;
}
static inline const char*
-tap_event_to_str(enum tap_event event) {
-
+tap_event_to_str(enum tap_event event)
+{
switch(event) {
CASE_RETURN_STRING(TAP_EVENT_TOUCH);
CASE_RETURN_STRING(TAP_EVENT_MOTION);
@@ -126,6 +130,12 @@ tp_tap_set_timer(struct tp_dispatch *tp, uint64_t time)
libinput_timer_set(&tp->tap.timer, time + DEFAULT_TAP_TIMEOUT_PERIOD);
}
+static void
+tp_tap_set_drag_timer(struct tp_dispatch *tp, uint64_t time)
+{
+ libinput_timer_set(&tp->tap.timer, time + DEFAULT_DRAG_TIMEOUT_PERIOD);
+}
+
static void
tp_tap_clear_timer(struct tp_dispatch *tp)
{
@@ -145,9 +155,10 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp,
tp_tap_set_timer(tp, time);
break;
case TAP_EVENT_RELEASE:
+ break;
case TAP_EVENT_MOTION:
log_bug_libinput(libinput,
- "invalid event, no fingers are down\n");
+ "invalid tap event, no fingers are down\n");
break;
case TAP_EVENT_TIMEOUT:
break;
@@ -218,10 +229,11 @@ tp_tap_tapped_handle_event(struct tp_dispatch *tp,
case TAP_EVENT_MOTION:
case TAP_EVENT_RELEASE:
log_bug_libinput(libinput,
- "invalid event when fingers are up\n");
+ "invalid tap event when fingers are up\n");
break;
case TAP_EVENT_TOUCH:
tp->tap.state = TAP_STATE_DRAGGING_OR_DOUBLETAP;
+ tp_tap_set_timer(tp, time);
break;
case TAP_EVENT_TIMEOUT:
tp->tap.state = TAP_STATE_IDLE;
@@ -351,11 +363,8 @@ tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp,
tp->tap.state = TAP_STATE_DRAGGING_2;
break;
case TAP_EVENT_RELEASE:
- tp->tap.state = TAP_STATE_IDLE;
+ tp->tap.state = TAP_STATE_MULTITAP;
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
- tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
- tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
- tp_tap_clear_timer(tp);
break;
case TAP_EVENT_MOTION:
case TAP_EVENT_TIMEOUT:
@@ -380,7 +389,7 @@ tp_tap_dragging_handle_event(struct tp_dispatch *tp,
break;
case TAP_EVENT_RELEASE:
tp->tap.state = TAP_STATE_DRAGGING_WAIT;
- tp_tap_set_timer(tp, time);
+ tp_tap_set_drag_timer(tp, time);
break;
case TAP_EVENT_MOTION:
case TAP_EVENT_TIMEOUT:
@@ -401,8 +410,8 @@ tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp,
switch (event) {
case TAP_EVENT_TOUCH:
- tp->tap.state = TAP_STATE_DRAGGING;
- tp_tap_clear_timer(tp);
+ tp->tap.state = TAP_STATE_DRAGGING_OR_TAP;
+ tp_tap_set_timer(tp, time);
break;
case TAP_EVENT_RELEASE:
case TAP_EVENT_MOTION:
@@ -418,6 +427,32 @@ tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp,
}
}
+static void
+tp_tap_dragging_tap_handle_event(struct tp_dispatch *tp,
+ struct tp_touch *t,
+ enum tap_event event, uint64_t time)
+{
+
+ switch (event) {
+ case TAP_EVENT_TOUCH:
+ tp->tap.state = TAP_STATE_DRAGGING_2;
+ tp_tap_clear_timer(tp);
+ break;
+ case TAP_EVENT_RELEASE:
+ tp->tap.state = TAP_STATE_IDLE;
+ tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
+ break;
+ case TAP_EVENT_MOTION:
+ case TAP_EVENT_TIMEOUT:
+ tp->tap.state = TAP_STATE_DRAGGING;
+ break;
+ case TAP_EVENT_BUTTON:
+ tp->tap.state = TAP_STATE_DEAD;
+ tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
+ break;
+ }
+}
+
static void
tp_tap_dragging2_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
@@ -443,6 +478,78 @@ tp_tap_dragging2_handle_event(struct tp_dispatch *tp,
}
}
+static void
+tp_tap_multitap_handle_event(struct tp_dispatch *tp,
+ struct tp_touch *t,
+ enum tap_event event, uint64_t time)
+{
+ struct libinput *libinput = tp->device->base.seat->libinput;
+
+ switch (event) {
+ case TAP_EVENT_RELEASE:
+ log_bug_libinput(libinput,
+ "invalid tap event, no fingers are down\n");
+ break;
+ case TAP_EVENT_TOUCH:
+ tp->tap.state = TAP_STATE_MULTITAP_DOWN;
+ tp->tap.multitap_last_time = time;
+ tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
+ tp_tap_set_timer(tp, time);
+ break;
+ case TAP_EVENT_MOTION:
+ log_bug_libinput(libinput,
+ "invalid tap event, no fingers are down\n");
+ break;
+ case TAP_EVENT_TIMEOUT:
+ tp->tap.state = TAP_STATE_IDLE;
+ tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
+ tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
+ break;
+ case TAP_EVENT_BUTTON:
+ tp->tap.state = TAP_STATE_IDLE;
+ tp_tap_clear_timer(tp);
+ break;
+ }
+}
+
+static void
+tp_tap_multitap_down_handle_event(struct tp_dispatch *tp,
+ struct tp_touch *t,
+ enum tap_event event,
+ uint64_t time)
+{
+ switch (event) {
+ case TAP_EVENT_RELEASE:
+ tp->tap.state = TAP_STATE_MULTITAP;
+ tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
+ break;
+ case TAP_EVENT_TOUCH:
+ tp->tap.state = TAP_STATE_DRAGGING_2;
+ tp_tap_notify(tp,
+ tp->tap.multitap_last_time,
+ 1,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
+ tp_tap_clear_timer(tp);
+ break;
+ case TAP_EVENT_MOTION:
+ case TAP_EVENT_TIMEOUT:
+ tp->tap.state = TAP_STATE_DRAGGING;
+ tp_tap_notify(tp,
+ tp->tap.multitap_last_time,
+ 1,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
+ tp_tap_clear_timer(tp);
+ break;
+ case TAP_EVENT_BUTTON:
+ tp->tap.state = TAP_STATE_DEAD;
+ tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
+ tp_tap_clear_timer(tp);
+ break;
+ }
+}
+
static void
tp_tap_dead_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
@@ -508,9 +615,18 @@ tp_tap_handle_event(struct tp_dispatch *tp,
case TAP_STATE_DRAGGING_WAIT:
tp_tap_dragging_wait_handle_event(tp, t, event, time);
break;
+ case TAP_STATE_DRAGGING_OR_TAP:
+ tp_tap_dragging_tap_handle_event(tp, t, event, time);
+ break;
case TAP_STATE_DRAGGING_2:
tp_tap_dragging2_handle_event(tp, t, event, time);
break;
+ case TAP_STATE_MULTITAP:
+ tp_tap_multitap_handle_event(tp, t, event, time);
+ break;
+ case TAP_STATE_MULTITAP_DOWN:
+ tp_tap_multitap_down_handle_event(tp, t, event, time);
+ break;
case TAP_STATE_DEAD:
tp_tap_dead_handle_event(tp, t, event, time);
break;
@@ -530,16 +646,11 @@ static bool
tp_tap_exceeds_motion_threshold(struct tp_dispatch *tp,
struct tp_touch *t)
{
- int threshold = DEFAULT_TAP_MOVE_THRESHOLD;
- double dx, dy;
- struct normalized_coords normalized;
+ struct normalized_coords norm =
+ tp_normalize_delta(tp, device_delta(t->point,
+ t->tap.initial));
- dx = abs(t->tap.initial.x - t->point.x);
- dy = abs(t->tap.initial.y - t->point.y);
- tp_normalize_delta(tp, dx, dy, &normalized);
-
- return normalized.x * normalized.x + normalized.y * normalized.y
- > threshold * threshold;
+ return normalized_length(norm) > DEFAULT_TAP_MOVE_THRESHOLD;
}
static bool
@@ -575,6 +686,14 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
t->tap.state = TAP_TOUCH_STATE_TOUCH;
t->tap.initial = t->point;
tp_tap_handle_event(tp, t, TAP_EVENT_TOUCH, time);
+
+ /* If we think this is a palm, pretend there's a
+ * motion event which will prevent tap clicks
+ * without requiring extra states in the FSM.
+ */
+ if (tp_palm_tap_is_palm(tp, t))
+ tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time);
+
} else if (t->state == TOUCH_END) {
tp_tap_handle_event(tp, t, TAP_EVENT_RELEASE, time);
t->tap.state = TAP_TOUCH_STATE_IDLE;
@@ -603,8 +722,10 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
case TAP_STATE_TOUCH:
case TAP_STATE_TAPPED:
case TAP_STATE_DRAGGING_OR_DOUBLETAP:
+ case TAP_STATE_DRAGGING_OR_TAP:
case TAP_STATE_TOUCH_2:
case TAP_STATE_TOUCH_3:
+ case TAP_STATE_MULTITAP_DOWN:
filter_motion = 1;
break;
@@ -780,6 +901,7 @@ tp_tap_dragging(struct tp_dispatch *tp)
case TAP_STATE_DRAGGING:
case TAP_STATE_DRAGGING_2:
case TAP_STATE_DRAGGING_WAIT:
+ case TAP_STATE_DRAGGING_OR_TAP:
return true;
default:
return false;
diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index 6282b71e..79cc0519 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -34,6 +34,9 @@
#define DEFAULT_ACCEL_NUMERATOR 3000.0
#define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0
#define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT 500 /* ms */
+#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1 200 /* ms */
+#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2 500 /* ms */
+#define FAKE_FINGER_OVERFLOW (1 << 7)
static inline int
tp_hysteresis(int in, int center, int margin)
@@ -58,33 +61,16 @@ tp_motion_history_offset(struct tp_touch *t, int offset)
return &t->history.samples[offset_index];
}
-void
+struct normalized_coords
tp_filter_motion(struct tp_dispatch *tp,
- double *dx, double *dy,
- double *dx_unaccel, double *dy_unaccel,
+ const struct normalized_coords *unaccelerated,
uint64_t time)
{
- struct normalized_coords unaccelerated;
- struct normalized_coords accelerated;
+ if (normalized_is_zero(*unaccelerated))
+ return *unaccelerated;
- unaccelerated.x = *dx;
- unaccelerated.y = *dy;
-
- if (unaccelerated.x != 0.0 || unaccelerated.y != 0.0)
- accelerated = filter_dispatch(tp->device->pointer.filter,
- &unaccelerated,
- tp,
- time);
- else
- accelerated = unaccelerated;
-
- if (dx_unaccel)
- *dx_unaccel = unaccelerated.x;
- if (dy_unaccel)
- *dy_unaccel = unaccelerated.y;
-
- *dx = accelerated.x;
- *dy = accelerated.y;
+ return filter_dispatch(tp->device->pointer.filter,
+ unaccelerated, tp, time);
}
static inline void
@@ -144,8 +130,10 @@ tp_get_touch(struct tp_dispatch *tp, unsigned int slot)
static inline unsigned int
tp_fake_finger_count(struct tp_dispatch *tp)
{
- /* don't count BTN_TOUCH */
- return ffs(tp->fake_touches >> 1);
+ if (tp->fake_touches & FAKE_FINGER_OVERFLOW)
+ return FAKE_FINGER_OVERFLOW;
+ else /* don't count BTN_TOUCH */
+ return ffs(tp->fake_touches >> 1);
}
static inline bool
@@ -163,6 +151,8 @@ tp_fake_finger_set(struct tp_dispatch *tp,
switch (code) {
case BTN_TOUCH:
+ if (!is_press)
+ tp->fake_touches &= ~FAKE_FINGER_OVERFLOW;
shift = 0;
break;
case BTN_TOOL_FINGER:
@@ -173,14 +163,24 @@ tp_fake_finger_set(struct tp_dispatch *tp,
case BTN_TOOL_QUADTAP:
shift = code - BTN_TOOL_DOUBLETAP + 2;
break;
+ /* when QUINTTAP is released we're either switching to 6 fingers
+ (flag stays in place until BTN_TOUCH is released) or
+ one of DOUBLE/TRIPLE/QUADTAP (will clear the flag on press) */
+ case BTN_TOOL_QUINTTAP:
+ if (is_press)
+ tp->fake_touches |= FAKE_FINGER_OVERFLOW;
+ return;
default:
return;
}
- if (is_press)
+ if (is_press) {
+ tp->fake_touches &= ~FAKE_FINGER_OVERFLOW;
tp->fake_touches |= 1 << shift;
- else
+
+ } else {
tp->fake_touches &= ~(0x1 << shift);
+ }
}
static inline void
@@ -233,7 +233,7 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
}
t->dirty = true;
- t->palm.is_palm = false;
+ t->palm.state = PALM_NONE;
t->state = TOUCH_END;
t->pinned.is_pinned = false;
t->millis = time;
@@ -261,23 +261,22 @@ tp_estimate_delta(int x0, int x1, int x2, int x3)
struct normalized_coords
tp_get_delta(struct tp_touch *t)
{
- double dx, dy; /* in device coords */
- struct normalized_coords normalized = { 0.0, 0.0 };
+ struct device_float_coords delta;
+ const struct normalized_coords zero = { 0.0, 0.0 };
if (t->history.count < TOUCHPAD_MIN_SAMPLES)
- return normalized;
+ return zero;
- dx = tp_estimate_delta(tp_motion_history_offset(t, 0)->x,
- tp_motion_history_offset(t, 1)->x,
- tp_motion_history_offset(t, 2)->x,
- tp_motion_history_offset(t, 3)->x);
- dy = tp_estimate_delta(tp_motion_history_offset(t, 0)->y,
- tp_motion_history_offset(t, 1)->y,
- tp_motion_history_offset(t, 2)->y,
- tp_motion_history_offset(t, 3)->y);
- tp_normalize_delta(t->tp, dx, dy, &normalized);
+ delta.x = tp_estimate_delta(tp_motion_history_offset(t, 0)->x,
+ tp_motion_history_offset(t, 1)->x,
+ tp_motion_history_offset(t, 2)->x,
+ tp_motion_history_offset(t, 3)->x);
+ delta.y = tp_estimate_delta(tp_motion_history_offset(t, 0)->y,
+ tp_motion_history_offset(t, 1)->y,
+ tp_motion_history_offset(t, 2)->y,
+ tp_motion_history_offset(t, 3)->y);
- return normalized;
+ return tp_normalize_delta(t->tp, delta);
}
static void
@@ -303,6 +302,9 @@ tp_process_absolute(struct tp_dispatch *tp,
case ABS_MT_SLOT:
tp->slot = e->value;
break;
+ case ABS_MT_DISTANCE:
+ t->distance = e->value;
+ break;
case ABS_MT_TRACKING_ID:
if (e->value != -1)
tp_new_touch(tp, t, time);
@@ -335,19 +337,18 @@ tp_process_absolute_st(struct tp_dispatch *tp,
}
static void
-tp_process_fake_touch(struct tp_dispatch *tp,
- const struct input_event *e,
- uint64_t time)
+tp_process_fake_touches(struct tp_dispatch *tp,
+ uint64_t time)
{
struct tp_touch *t;
unsigned int nfake_touches;
unsigned int i, start;
- tp_fake_finger_set(tp, e->code, e->value != 0);
-
nfake_touches = tp_fake_finger_count(tp);
+ if (nfake_touches == FAKE_FINGER_OVERFLOW)
+ return;
- start = tp->has_mt ? tp->real_touches : 0;
+ start = tp->has_mt ? tp->num_slots : 0;
for (i = start; i < tp->ntouches; i++) {
t = tp_get_touch(tp, i);
if (i < nfake_touches)
@@ -408,7 +409,8 @@ tp_process_key(struct tp_dispatch *tp,
case BTN_TOOL_DOUBLETAP:
case BTN_TOOL_TRIPLETAP:
case BTN_TOOL_QUADTAP:
- tp_process_fake_touch(tp, e, time);
+ case BTN_TOOL_QUINTTAP:
+ tp_fake_finger_set(tp, e->code, !!e->value);
break;
case BTN_0:
case BTN_1:
@@ -455,29 +457,58 @@ int
tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
{
return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
- !t->palm.is_palm &&
+ t->palm.state == PALM_NONE &&
!t->pinned.is_pinned &&
tp_button_touch_active(tp, t) &&
tp_edge_scroll_touch_active(tp, t);
}
+bool
+tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ if (t->state != TOUCH_BEGIN)
+ return false;
+
+ if (t->point.x > tp->palm.left_edge &&
+ t->point.x < tp->palm.right_edge)
+ return false;
+
+ /* We're inside the left/right palm edge and in the northern half of
+ * the touchpad - this tap is a palm */
+ if (t->point.y < tp->palm.vert_center)
+ return true;
+
+ return false;
+}
+
static void
tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
{
const int PALM_TIMEOUT = 200; /* ms */
const int DIRECTIONS = NE|E|SE|SW|W|NW;
+ struct device_float_coords delta;
+ int dirs;
+
+ if (tp->sendevents.keyboard_active &&
+ t->state == TOUCH_BEGIN) {
+ t->palm.state = PALM_TYPING;
+ t->palm.time = time;
+ t->palm.first = t->point;
+ return;
+ }
/* If labelled a touch as palm, we unlabel as palm when
we move out of the palm edge zone within the timeout, provided
the direction is within 45 degrees of the horizontal.
*/
- if (t->palm.is_palm) {
+ if (t->palm.state == PALM_EDGE) {
if (time < t->palm.time + PALM_TIMEOUT &&
(t->point.x > tp->palm.left_edge && t->point.x < tp->palm.right_edge)) {
- int dirs = vector_get_direction(t->point.x - t->palm.first.x,
- t->point.y - t->palm.first.y);
+ delta = device_delta(t->point, t->palm.first);
+ dirs = normalized_get_direction(
+ tp_normalize_delta(tp, delta));
if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS)) {
- t->palm.is_palm = false;
+ t->palm.state = PALM_NONE;
}
}
return;
@@ -496,13 +527,35 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_button_is_inside_softbutton_area(tp, t))
return;
- t->palm.is_palm = true;
+ t->palm.state = PALM_EDGE;
t->palm.time = time;
t->palm.first = t->point;
}
static void
-tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
+tp_unhover_abs_distance(struct tp_dispatch *tp, uint64_t time)
+{
+ struct tp_touch *t;
+ unsigned int i;
+
+ for (i = 0; i < tp->ntouches; i++) {
+ t = tp_get_touch(tp, i);
+
+ if (t->state == TOUCH_HOVERING) {
+ if (t->distance == 0) {
+ /* avoid jumps when landing a finger */
+ tp_motion_history_reset(t);
+ tp_begin_touch(tp, t, time);
+ }
+ } else {
+ if (t->distance > 0)
+ tp_end_touch(tp, t, time);
+ }
+ }
+}
+
+static void
+tp_unhover_fake_touches(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *t;
unsigned int nfake_touches;
@@ -512,6 +565,9 @@ tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
return;
nfake_touches = tp_fake_finger_count(tp);
+ if (nfake_touches == FAKE_FINGER_OVERFLOW)
+ return;
+
if (tp->nfingers_down == nfake_touches &&
((tp->nfingers_down == 0 && !tp_fake_finger_is_touching(tp)) ||
(tp->nfingers_down > 0 && tp_fake_finger_is_touching(tp))))
@@ -544,7 +600,8 @@ tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
for (i = tp->ntouches - 1; i >= 0; i--) {
t = tp_get_touch(tp, i);
- if (t->state == TOUCH_HOVERING)
+ if (t->state == TOUCH_HOVERING ||
+ t->state == TOUCH_NONE)
continue;
tp_end_touch(tp, t, time);
@@ -556,6 +613,16 @@ tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
}
}
+static void
+tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
+{
+ if (tp->reports_distance)
+ tp_unhover_abs_distance(tp, time);
+ else
+ tp_unhover_fake_touches(tp, time);
+
+}
+
static void
tp_process_state(struct tp_dispatch *tp, uint64_t time)
{
@@ -563,6 +630,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time)
struct tp_touch *first = tp_get_touch(tp, 0);
unsigned int i;
+ tp_process_fake_touches(tp, time);
tp_unhover_touches(tp, time);
for (i = 0; i < tp->ntouches; i++) {
@@ -572,7 +640,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time)
if (tp->semi_mt && tp->nfingers_down != tp->old_nfingers_down)
tp_motion_history_reset(t);
- if (i >= tp->real_touches && t->state != TOUCH_NONE) {
+ if (i >= tp->num_slots && t->state != TOUCH_NONE) {
t->point = first->point;
if (!t->dirty)
t->dirty = first->dirty;
@@ -647,7 +715,9 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time)
filter_motion |= tp_tap_handle_state(tp, time);
filter_motion |= tp_post_button_events(tp, time);
- if (filter_motion || tp->sendevents.trackpoint_active) {
+ if (filter_motion ||
+ tp->sendevents.trackpoint_active ||
+ tp->sendevents.keyboard_active) {
tp_edge_scroll_stop_events(tp, time);
tp_gesture_stop(tp, time);
return;
@@ -669,10 +739,10 @@ tp_handle_state(struct tp_dispatch *tp,
}
static void
-tp_process(struct evdev_dispatch *dispatch,
- struct evdev_device *device,
- struct input_event *e,
- uint64_t time)
+tp_interface_process(struct evdev_dispatch *dispatch,
+ struct evdev_device *device,
+ struct input_event *e,
+ uint64_t time)
{
struct tp_dispatch *tp =
(struct tp_dispatch *)dispatch;
@@ -697,14 +767,19 @@ static void
tp_remove_sendevents(struct tp_dispatch *tp)
{
libinput_timer_cancel(&tp->sendevents.trackpoint_timer);
+ libinput_timer_cancel(&tp->sendevents.keyboard_timer);
if (tp->buttons.trackpoint)
libinput_device_remove_event_listener(
&tp->sendevents.trackpoint_listener);
+
+ if (tp->sendevents.keyboard)
+ libinput_device_remove_event_listener(
+ &tp->sendevents.keyboard_listener);
}
static void
-tp_remove(struct evdev_dispatch *dispatch)
+tp_interface_remove(struct evdev_dispatch *dispatch)
{
struct tp_dispatch *tp =
(struct tp_dispatch*)dispatch;
@@ -717,7 +792,7 @@ tp_remove(struct evdev_dispatch *dispatch)
}
static void
-tp_destroy(struct evdev_dispatch *dispatch)
+tp_interface_destroy(struct evdev_dispatch *dispatch)
{
struct tp_dispatch *tp =
(struct tp_dispatch*)dispatch;
@@ -726,6 +801,12 @@ tp_destroy(struct evdev_dispatch *dispatch)
free(tp);
}
+static void
+tp_release_fake_touches(struct tp_dispatch *tp)
+{
+ tp->fake_touches = 0;
+}
+
static void
tp_clear_state(struct tp_dispatch *tp)
{
@@ -749,6 +830,7 @@ tp_clear_state(struct tp_dispatch *tp)
tp_for_each_touch(tp, t) {
tp_end_sequence(tp, t, now);
}
+ tp_release_fake_touches(tp);
tp_handle_state(tp, now);
}
@@ -771,6 +853,15 @@ tp_suspend(struct tp_dispatch *tp, struct evdev_device *device)
}
}
+static void
+tp_interface_suspend(struct evdev_dispatch *dispatch,
+ struct evdev_device *device)
+{
+ struct tp_dispatch *tp = (struct tp_dispatch *)dispatch;
+
+ tp_clear_state(tp);
+}
+
static void
tp_resume(struct tp_dispatch *tp, struct evdev_device *device)
{
@@ -816,13 +907,76 @@ tp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data)
}
static void
-tp_device_added(struct evdev_device *device,
- struct evdev_device *added_device)
+tp_keyboard_timeout(uint64_t now, void *data)
+{
+ struct tp_dispatch *tp = data;
+
+ tp_tap_resume(tp, now);
+ tp->sendevents.keyboard_active = false;
+}
+
+static void
+tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data)
+{
+ struct tp_dispatch *tp = data;
+ struct libinput_event_keyboard *kbdev;
+ unsigned int timeout;
+
+ if (event->type != LIBINPUT_EVENT_KEYBOARD_KEY)
+ return;
+
+ kbdev = libinput_event_get_keyboard_event(event);
+
+ /* Only trigger the timer on key down. */
+ if (libinput_event_keyboard_get_key_state(kbdev) !=
+ LIBINPUT_KEY_STATE_PRESSED)
+ return;
+
+ /* modifier keys don't trigger disable-while-typing so things like
+ * ctrl+zoom or ctrl+click are possible */
+ switch (libinput_event_keyboard_get_key(kbdev)) {
+ case KEY_LEFTCTRL:
+ case KEY_RIGHTCTRL:
+ case KEY_LEFTALT:
+ case KEY_RIGHTALT:
+ case KEY_LEFTSHIFT:
+ case KEY_RIGHTSHIFT:
+ case KEY_FN:
+ return;
+ default:
+ break;
+ }
+
+ if (!tp->sendevents.keyboard_active) {
+ tp_edge_scroll_stop_events(tp, time);
+ tp_gesture_stop(tp, time);
+ tp_tap_suspend(tp, time);
+ tp->sendevents.keyboard_active = true;
+ timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1;
+ } else {
+ timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2;
+ }
+
+ libinput_timer_set(&tp->sendevents.keyboard_timer,
+ time + timeout);
+}
+
+static void
+tp_interface_device_added(struct evdev_device *device,
+ struct evdev_device *added_device)
{
struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
+ unsigned int bus_tp = libevdev_get_id_bustype(device->evdev),
+ bus_trp = libevdev_get_id_bustype(added_device->evdev),
+ bus_kbd = libevdev_get_id_bustype(added_device->evdev);
+ bool tp_is_internal, trp_is_internal, kbd_is_internal;
+
+ tp_is_internal = bus_tp != BUS_USB && bus_tp != BUS_BLUETOOTH;
+ trp_is_internal = bus_trp != BUS_USB && bus_trp != BUS_BLUETOOTH;
if (tp->buttons.trackpoint == NULL &&
- (added_device->tags & EVDEV_TAG_TRACKPOINT)) {
+ (added_device->tags & EVDEV_TAG_TRACKPOINT) &&
+ tp_is_internal && trp_is_internal) {
/* Don't send any pending releases to the new trackpoint */
tp->buttons.active_is_topbutton = false;
tp->buttons.trackpoint = added_device;
@@ -831,6 +985,18 @@ tp_device_added(struct evdev_device *device,
tp_trackpoint_event, tp);
}
+ /* FIXME: detect external keyboard better */
+ kbd_is_internal = bus_tp != BUS_BLUETOOTH &&
+ bus_kbd == bus_tp;
+ if (tp_is_internal && kbd_is_internal &&
+ tp->sendevents.keyboard == NULL) {
+ libinput_device_add_event_listener(&added_device->base,
+ &tp->sendevents.keyboard_listener,
+ tp_keyboard_event, tp);
+ tp->sendevents.keyboard = added_device;
+ tp->sendevents.keyboard_active = false;
+ }
+
if (tp->sendevents.current_mode !=
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
return;
@@ -840,8 +1006,8 @@ tp_device_added(struct evdev_device *device,
}
static void
-tp_device_removed(struct evdev_device *device,
- struct evdev_device *removed_device)
+tp_interface_device_removed(struct evdev_device *device,
+ struct evdev_device *removed_device)
{
struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
struct libinput_device *dev;
@@ -857,6 +1023,12 @@ tp_device_removed(struct evdev_device *device,
tp->buttons.trackpoint = NULL;
}
+ if (removed_device == tp->sendevents.keyboard) {
+ libinput_device_remove_event_listener(
+ &tp->sendevents.keyboard_listener);
+ tp->sendevents.keyboard = NULL;
+ }
+
if (tp->sendevents.current_mode !=
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
return;
@@ -873,8 +1045,8 @@ tp_device_removed(struct evdev_device *device,
}
static void
-tp_tag_device(struct evdev_device *device,
- struct udev_device *udev_device)
+tp_interface_tag_device(struct evdev_device *device,
+ struct udev_device *udev_device)
{
int bustype;
@@ -896,15 +1068,16 @@ tp_tag_device(struct evdev_device *device,
}
static struct evdev_dispatch_interface tp_interface = {
- tp_process,
- tp_remove,
- tp_destroy,
- tp_device_added,
- tp_device_removed,
- tp_device_removed, /* device_suspended, treat as remove */
- tp_device_added, /* device_resumed, treat as add */
- tp_tag_device,
- NULL, /* post_added */
+ tp_interface_process,
+ tp_interface_suspend,
+ tp_interface_remove,
+ tp_interface_destroy,
+ tp_interface_device_added,
+ tp_interface_device_removed,
+ tp_interface_device_removed, /* device_suspended, treat as remove */
+ tp_interface_device_added, /* device_resumed, treat as add */
+ tp_interface_tag_device,
+ NULL, /* post_added */
};
static void
@@ -915,6 +1088,28 @@ tp_init_touch(struct tp_dispatch *tp,
t->has_ended = true;
}
+static void
+tp_sync_touch(struct tp_dispatch *tp,
+ struct evdev_device *device,
+ struct tp_touch *t,
+ int slot)
+{
+ struct libevdev *evdev = device->evdev;
+
+ if (!libevdev_fetch_slot_value(evdev,
+ slot,
+ ABS_MT_POSITION_X,
+ &t->point.x))
+ t->point.x = libevdev_get_event_value(evdev, EV_ABS, ABS_X);
+ if (!libevdev_fetch_slot_value(evdev,
+ slot,
+ ABS_MT_POSITION_Y,
+ &t->point.y))
+ t->point.y = libevdev_get_event_value(evdev, EV_ABS, ABS_Y);
+
+ libevdev_fetch_slot_value(evdev, slot, ABS_MT_DISTANCE, &t->distance);
+}
+
static int
tp_init_slots(struct tp_dispatch *tp,
struct evdev_device *device)
@@ -934,11 +1129,11 @@ tp_init_slots(struct tp_dispatch *tp,
absinfo = libevdev_get_abs_info(device->evdev, ABS_MT_SLOT);
if (absinfo) {
- tp->real_touches = absinfo->maximum + 1;
+ tp->num_slots = absinfo->maximum + 1;
tp->slot = absinfo->value;
tp->has_mt = true;
} else {
- tp->real_touches = 1;
+ tp->num_slots = 1;
tp->slot = 0;
tp->has_mt = false;
}
@@ -954,7 +1149,7 @@ tp_init_slots(struct tp_dispatch *tp,
}
}
- tp->ntouches = max(tp->real_touches, n_btn_tool_touches);
+ tp->ntouches = max(tp->num_slots, n_btn_tool_touches);
tp->touches = calloc(tp->ntouches, sizeof(struct tp_touch));
if (!tp->touches)
return -1;
@@ -962,6 +1157,12 @@ tp_init_slots(struct tp_dispatch *tp,
for (i = 0; i < tp->ntouches; i++)
tp_init_touch(tp, &tp->touches[i]);
+ /* Always sync the first touch so we get ABS_X/Y synced on
+ * single-touch touchpads */
+ tp_sync_touch(tp, device, &tp->touches[0], 0);
+ for (i = 1; i < tp->num_slots; i++)
+ tp_sync_touch(tp, device, &tp->touches[i], i);
+
return 0;
}
@@ -969,6 +1170,7 @@ static int
tp_init_accel(struct tp_dispatch *tp, double diagonal)
{
int res_x, res_y;
+ accel_profile_func_t profile;
res_x = tp->device->abs.absinfo_x->resolution;
res_y = tp->device->abs.absinfo_y->resolution;
@@ -992,9 +1194,16 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal)
tp->accel.y_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal;
}
- if (evdev_device_init_pointer_acceleration(
- tp->device,
- touchpad_accel_profile_linear) == -1)
+ switch (tp->device->model) {
+ case EVDEV_MODEL_LENOVO_X230:
+ profile = touchpad_lenovo_x230_accel_profile;
+ break;
+ default:
+ profile = touchpad_accel_profile_linear;
+ break;
+ }
+
+ if (evdev_device_init_pointer_acceleration(tp->device, profile) == -1)
return -1;
return 0;
@@ -1087,13 +1296,16 @@ static int
tp_init_palmdetect(struct tp_dispatch *tp,
struct evdev_device *device)
{
- int width;
+ int width, height;
tp->palm.right_edge = INT_MAX;
tp->palm.left_edge = INT_MIN;
+ tp->palm.vert_center = INT_MIN;
width = abs(device->abs.absinfo_x->maximum -
device->abs.absinfo_x->minimum);
+ height = abs(device->abs.absinfo_y->maximum -
+ device->abs.absinfo_y->minimum);
/* Apple touchpads are always big enough to warrant palm detection */
if (evdev_device_get_id_vendor(device) != VENDOR_ID_APPLE) {
@@ -1101,15 +1313,16 @@ tp_init_palmdetect(struct tp_dispatch *tp,
if (device->abs.absinfo_x->resolution == 1)
return 0;
- /* Enable palm detection on touchpads >= 80 mm. Anything smaller
+ /* Enable palm detection on touchpads >= 70 mm. Anything smaller
probably won't need it, until we find out it does */
- if (width/device->abs.absinfo_x->resolution < 80)
+ if (width/device->abs.absinfo_x->resolution < 70)
return 0;
}
/* palm edges are 5% of the width on each side */
tp->palm.right_edge = device->abs.absinfo_x->maximum - width * 0.05;
tp->palm.left_edge = device->abs.absinfo_x->minimum + width * 0.05;
+ tp->palm.vert_center = device->abs.absinfo_y->minimum + height/2;
return 0;
}
@@ -1121,6 +1334,10 @@ tp_init_sendevents(struct tp_dispatch *tp,
libinput_timer_init(&tp->sendevents.trackpoint_timer,
tp->device->base.seat->libinput,
tp_trackpoint_timeout, tp);
+
+ libinput_timer_init(&tp->sendevents.keyboard_timer,
+ tp->device->base.seat->libinput,
+ tp_keyboard_timeout, tp);
return 0;
}
@@ -1143,6 +1360,10 @@ tp_init(struct tp_dispatch *tp,
device->abs.absinfo_y->minimum);
diagonal = sqrt(width*width + height*height);
+ tp->reports_distance = libevdev_has_event_code(device->evdev,
+ EV_ABS,
+ ABS_MT_DISTANCE);
+
tp->hysteresis_margin.x =
diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
tp->hysteresis_margin.y =
@@ -1313,7 +1534,7 @@ evdev_mt_touchpad_create(struct evdev_device *device)
tp->model = tp_get_model(device);
if (tp_init(tp, device) != 0) {
- tp_destroy(&tp->base);
+ tp_interface_destroy(&tp->base);
return NULL;
}
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index 9980f900..3d51a398 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -61,6 +61,12 @@ enum touch_state {
TOUCH_END
};
+enum touch_palm_state {
+ PALM_NONE = 0,
+ PALM_EDGE,
+ PALM_TYPING,
+};
+
enum button_event {
BUTTON_EVENT_IN_BOTTOM_R = 30,
BUTTON_EVENT_IN_BOTTOM_L,
@@ -94,9 +100,12 @@ enum tp_tap_state {
TAP_STATE_TOUCH_3,
TAP_STATE_TOUCH_3_HOLD,
TAP_STATE_DRAGGING_OR_DOUBLETAP,
+ TAP_STATE_DRAGGING_OR_TAP,
TAP_STATE_DRAGGING,
TAP_STATE_DRAGGING_WAIT,
TAP_STATE_DRAGGING_2,
+ TAP_STATE_MULTITAP,
+ TAP_STATE_MULTITAP_DOWN,
TAP_STATE_DEAD, /**< finger count exceeded */
};
@@ -127,6 +136,7 @@ struct tp_touch {
bool dirty;
struct device_coords point;
uint64_t millis;
+ int distance; /* distance == 0 means touch */
struct {
struct device_coords samples[TOUCHPAD_HISTORY_LENGTH];
@@ -167,7 +177,7 @@ struct tp_touch {
} scroll;
struct {
- bool is_palm;
+ enum touch_palm_state state;
struct device_coords first; /* first coordinates if is_palm == true */
uint32_t time; /* first timestamp if is_palm == true */
} palm;
@@ -181,9 +191,10 @@ struct tp_dispatch {
unsigned int slot; /* current slot */
bool has_mt;
bool semi_mt;
+ bool reports_distance; /* does the device support true hovering */
enum touchpad_model model;
- unsigned int real_touches; /* number of slots */
+ unsigned int num_slots; /* number of slots */
unsigned int ntouches; /* no slots inc. fakes */
struct tp_touch *touches; /* len == ntouches */
/* bit 0: BTN_TOUCH
@@ -255,41 +266,50 @@ struct tp_dispatch {
struct libinput_timer timer;
enum tp_tap_state state;
uint32_t buttons_pressed;
+ uint64_t multitap_last_time;
} tap;
struct {
int32_t right_edge; /* in device coordinates */
int32_t left_edge; /* in device coordinates */
+ int32_t vert_center; /* in device coordinates */
} palm;
struct {
struct libinput_device_config_send_events config;
enum libinput_config_send_events_mode current_mode;
+
bool trackpoint_active;
struct libinput_event_listener trackpoint_listener;
struct libinput_timer trackpoint_timer;
+
+ bool keyboard_active;
+ struct libinput_event_listener keyboard_listener;
+ struct libinput_timer keyboard_timer;
+ struct evdev_device *keyboard;
} sendevents;
};
#define tp_for_each_touch(_tp, _t) \
for (unsigned int _i = 0; _i < (_tp)->ntouches && (_t = &(_tp)->touches[_i]); _i++)
-static inline void
-tp_normalize_delta(struct tp_dispatch *tp,
- double dx, double dy,
- struct normalized_coords *normalized)
+static inline struct normalized_coords
+tp_normalize_delta(struct tp_dispatch *tp, struct device_float_coords delta)
{
- normalized->x = dx * tp->accel.x_scale_coeff;
- normalized->y = dy * tp->accel.y_scale_coeff;
+ struct normalized_coords normalized;
+
+ normalized.x = delta.x * tp->accel.x_scale_coeff;
+ normalized.y = delta.y * tp->accel.y_scale_coeff;
+
+ return normalized;
}
struct normalized_coords
tp_get_delta(struct tp_touch *t);
-void
+struct normalized_coords
tp_filter_motion(struct tp_dispatch *tp,
- double *dx, double *dy,
- double *dx_unaccel, double *dy_unaccel,
+ const struct normalized_coords *unaccelerated,
uint64_t time);
int
@@ -385,4 +405,7 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time);
void
tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time);
+bool
+tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t);
+
#endif
diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c
index 3e3924ca..301fe246 100644
--- a/src/evdev-tablet.c
+++ b/src/evdev-tablet.c
@@ -1022,6 +1022,7 @@ tablet_check_initial_proximity(struct evdev_device *device,
static struct evdev_dispatch_interface tablet_interface = {
tablet_process,
+ NULL, /* suspend */
NULL, /* remove */
tablet_destroy,
NULL, /* device_added */
diff --git a/src/evdev.c b/src/evdev.c
index 6a3526a8..68d83a9e 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -137,6 +137,21 @@ evdev_keyboard_notify_key(struct evdev_device *device,
keyboard_notify_key(&device->base, time, key, state);
}
+void
+evdev_pointer_notify_physical_button(struct evdev_device *device,
+ uint32_t time,
+ int button,
+ enum libinput_button_state state)
+{
+ if (evdev_middlebutton_filter_button(device,
+ time,
+ button,
+ state))
+ return;
+
+ evdev_pointer_notify_button(device, time, button, state);
+}
+
void
evdev_pointer_notify_button(struct evdev_device *device,
uint32_t time,
@@ -151,13 +166,13 @@ evdev_pointer_notify_button(struct evdev_device *device,
(state == LIBINPUT_BUTTON_STATE_RELEASED && down_count == 0)) {
pointer_notify_button(&device->base, time, button, state);
- if (state == LIBINPUT_BUTTON_STATE_RELEASED &&
- device->left_handed.change_to_enabled)
- device->left_handed.change_to_enabled(device);
+ if (state == LIBINPUT_BUTTON_STATE_RELEASED) {
+ if (device->left_handed.change_to_enabled)
+ device->left_handed.change_to_enabled(device);
- if (state == LIBINPUT_BUTTON_STATE_RELEASED &&
- device->scroll.change_scroll_method)
- device->scroll.change_scroll_method(device);
+ if (device->scroll.change_scroll_method)
+ device->scroll.change_scroll_method(device);
+ }
}
}
@@ -230,8 +245,8 @@ normalize_delta(struct evdev_device *device,
const struct device_coords *delta,
struct normalized_coords *normalized)
{
- normalized->x = delta->x * (double)device->dpi / DEFAULT_MOUSE_DPI;
- normalized->y = delta->y * (double)device->dpi / DEFAULT_MOUSE_DPI;
+ normalized->x = delta->x * DEFAULT_MOUSE_DPI / (double)device->dpi;
+ normalized->y = delta->y * DEFAULT_MOUSE_DPI / (double)device->dpi;
}
static void
@@ -266,12 +281,13 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time)
}
/* Apply pointer acceleration. */
- accel = filter_dispatch(device->pointer.filter, &unaccel, device, time);
+ accel = filter_dispatch(device->pointer.filter,
+ &unaccel,
+ device,
+ time);
- if (accel.x == 0.0 && accel.y == 0.0 &&
- unaccel.x == 0.0 && unaccel.y == 0.0) {
+ if (normalized_is_zero(accel) && normalized_is_zero(unaccel))
break;
- }
pointer_notify_motion(base, time, &accel, &unaccel);
break;
@@ -432,10 +448,10 @@ evdev_button_scroll_button(struct evdev_device *device,
} else {
/* If the button is released quickly enough emit the
* button press/release events. */
- evdev_pointer_notify_button(device, time,
+ evdev_pointer_notify_physical_button(device, time,
device->scroll.button,
LIBINPUT_BUTTON_STATE_PRESSED);
- evdev_pointer_notify_button(device, time,
+ evdev_pointer_notify_physical_button(device, time,
device->scroll.button,
LIBINPUT_BUTTON_STATE_RELEASED);
}
@@ -507,7 +523,7 @@ evdev_process_key(struct evdev_device *device,
evdev_button_scroll_button(device, time, e->value);
break;
}
- evdev_pointer_notify_button(
+ evdev_pointer_notify_physical_button(
device,
time,
evdev_to_left_handed(device, e->code),
@@ -744,6 +760,65 @@ fallback_process(struct evdev_dispatch *dispatch,
}
}
+static void
+release_pressed_keys(struct evdev_device *device)
+{
+ struct libinput *libinput = device->base.seat->libinput;
+ uint64_t time;
+ int code;
+
+ if ((time = libinput_now(libinput)) == 0)
+ return;
+
+ for (code = 0; code < KEY_CNT; code++) {
+ int count = get_key_down_count(device, code);
+
+ if (count == 0)
+ continue;
+
+ if (count > 1) {
+ log_bug_libinput(libinput,
+ "Key %d is down %d times.\n",
+ code,
+ count);
+ }
+
+ switch (get_key_type(code)) {
+ case EVDEV_KEY_TYPE_NONE:
+ break;
+ case EVDEV_KEY_TYPE_KEY:
+ evdev_keyboard_notify_key(
+ device,
+ time,
+ code,
+ LIBINPUT_KEY_STATE_RELEASED);
+ break;
+ case EVDEV_KEY_TYPE_BUTTON:
+ evdev_pointer_notify_physical_button(
+ device,
+ time,
+ evdev_to_left_handed(device, code),
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ break;
+ }
+
+ count = get_key_down_count(device, code);
+ if (count != 0) {
+ log_bug_libinput(libinput,
+ "Releasing key %d failed.\n",
+ code);
+ break;
+ }
+ }
+}
+
+static void
+fallback_suspend(struct evdev_dispatch *dispatch,
+ struct evdev_device *device)
+{
+ release_pressed_keys(device);
+}
+
static void
fallback_destroy(struct evdev_dispatch *dispatch)
{
@@ -801,6 +876,7 @@ evdev_calibration_get_default_matrix(struct libinput_device *libinput_device,
struct evdev_dispatch_interface fallback_interface = {
fallback_process,
+ fallback_suspend,
NULL, /* remove */
fallback_destroy,
NULL, /* device_added */
@@ -971,8 +1047,15 @@ evdev_scroll_get_default_method(struct libinput_device *device)
if (libevdev_has_property(evdev->evdev, INPUT_PROP_POINTING_STICK))
return LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
- else
- return LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
+
+ /* Mice without a scroll wheel but with middle button have on-button
+ * scrolling by default */
+ if (!libevdev_has_event_code(evdev->evdev, EV_REL, REL_WHEEL) &&
+ !libevdev_has_event_code(evdev->evdev, EV_REL, REL_HWHEEL) &&
+ libevdev_has_event_code(evdev->evdev, EV_KEY, BTN_MIDDLE))
+ return LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
+
+ return LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
}
static enum libinput_config_status
@@ -1004,8 +1087,15 @@ evdev_scroll_get_default_button(struct libinput_device *device)
if (libevdev_has_property(evdev->evdev, INPUT_PROP_POINTING_STICK))
return BTN_MIDDLE;
- else
- return 0;
+
+ /* A device that defaults to button scrolling defaults
+ to BTN_MIDDLE */
+ if (evdev_scroll_get_default_method(device) ==
+ LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN &&
+ libevdev_has_event_code(evdev->evdev, EV_KEY, BTN_MIDDLE))
+ return BTN_MIDDLE;
+
+ return 0;
}
static int
@@ -1131,6 +1221,24 @@ fallback_dispatch_create(struct libinput_device *device)
evdev_init_calibration(evdev_device, dispatch);
evdev_init_sendevents(evdev_device, dispatch);
+ /* BTN_MIDDLE is set on mice even when it's not present. So
+ * we can only use the absense of BTN_MIDDLE to mean something, i.e.
+ * we enable it by default on anything that only has L&R.
+ * If we have L&R and no middle, we don't expose it as config
+ * option */
+ if (libevdev_has_event_code(evdev_device->evdev, EV_KEY, BTN_LEFT) &&
+ libevdev_has_event_code(evdev_device->evdev, EV_KEY, BTN_RIGHT)) {
+ bool has_middle = libevdev_has_event_code(evdev_device->evdev,
+ EV_KEY,
+ BTN_MIDDLE);
+ bool want_config = has_middle;
+ bool enable_by_default = !has_middle;
+
+ evdev_init_middlebutton(evdev_device,
+ enable_by_default,
+ want_config);
+ }
+
return dispatch;
}
@@ -1140,6 +1248,19 @@ evdev_process_event(struct evdev_device *device, struct input_event *e)
struct evdev_dispatch *dispatch = device->dispatch;
uint64_t time = e->time.tv_sec * 1000ULL + e->time.tv_usec / 1000;
+#if 0
+ if (libevdev_event_is_code(e, EV_SYN, SYN_REPORT))
+ log_debug(device->base.seat->libinput,
+ "-------------- EV_SYN ------------\n");
+ else
+ log_debug(device->base.seat->libinput,
+ "%-7s %-16s %-20s %4d\n",
+ evdev_device_get_sysname(device),
+ libevdev_event_type_get_name(e->type),
+ libevdev_event_code_get_name(e->type, e->code),
+ e->value);
+#endif
+
dispatch->interface->process(dispatch, device, e, time);
}
@@ -1322,6 +1443,31 @@ evdev_read_wheel_click_prop(struct evdev_device *device)
return angle;
}
+
+static inline int
+evdev_get_trackpoint_dpi(struct evdev_device *device)
+{
+ struct libinput *libinput = device->base.seat->libinput;
+ const char *trackpoint_accel;
+ double accel = DEFAULT_TRACKPOINT_ACCEL;
+
+ trackpoint_accel = udev_device_get_property_value(
+ device->udev_device, "POINTINGSTICK_CONST_ACCEL");
+ if (trackpoint_accel) {
+ accel = parse_trackpoint_accel_property(trackpoint_accel);
+ if (accel == 0.0) {
+ log_error(libinput, "Trackpoint accel property for "
+ "'%s' is present but invalid, "
+ "using %.2f instead\n",
+ device->devname,
+ DEFAULT_TRACKPOINT_ACCEL);
+ accel = DEFAULT_TRACKPOINT_ACCEL;
+ }
+ }
+
+ return DEFAULT_MOUSE_DPI / accel;
+}
+
static inline int
evdev_read_dpi_prop(struct evdev_device *device)
{
@@ -1329,6 +1475,14 @@ evdev_read_dpi_prop(struct evdev_device *device)
const char *mouse_dpi;
int dpi = DEFAULT_MOUSE_DPI;
+ /*
+ * Trackpoints do not have dpi, instead hwdb may contain a
+ * POINTINGSTICK_CONST_ACCEL value to compensate for sensitivity
+ * differences between models, we translate this to a fake dpi.
+ */
+ if (libevdev_has_property(device->evdev, INPUT_PROP_POINTING_STICK))
+ return evdev_get_trackpoint_dpi(device);
+
mouse_dpi = udev_device_get_property_value(device->udev_device,
"MOUSE_DPI");
if (mouse_dpi) {
@@ -1346,6 +1500,33 @@ evdev_read_dpi_prop(struct evdev_device *device)
return dpi;
}
+static inline enum evdev_device_model
+evdev_read_model(struct evdev_device *device)
+{
+ const struct model_map {
+ const char *property;
+ enum evdev_device_model model;
+ } model_map[] = {
+ { "LIBINPUT_MODEL_LENOVO_X230", EVDEV_MODEL_LENOVO_X230 },
+ { "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 },
+ { NULL, EVDEV_MODEL_DEFAULT },
+ };
+ const struct model_map *m = model_map;
+
+ while (m->property) {
+ if (!!udev_device_get_property_value(device->udev_device,
+ m->property))
+ break;
+ m++;
+ }
+
+ return m->model;
+}
+
/* Return 1 if the given resolutions have been set, or 0 otherwise */
inline int
evdev_fix_abs_resolution(struct evdev_device *device,
@@ -1380,13 +1561,6 @@ evdev_fix_abs_resolution(struct evdev_device *device,
absx = libevdev_get_abs_info(evdev, xcode);
absy = libevdev_get_abs_info(evdev, ycode);
- if ((absx->resolution == 0 && absy->resolution != 0) ||
- (absx->resolution != 0 && absy->resolution == 0)) {
- log_bug_kernel(libinput,
- "Kernel has only x or y resolution, not both.\n");
- return 0;
- }
-
if (absx->resolution == 0 || absx->resolution == EVDEV_FAKE_RESOLUTION) {
fixed = *absx;
fixed.resolution = xresolution;
@@ -1434,6 +1608,18 @@ evdev_device_get_udev_tags(struct evdev_device *device,
return tags;
}
+/* Fake MT devices have the ABS_MT_SLOT bit set because of
+ the limited ABS_* range - they aren't MT devices, they
+ just have too many ABS_ axes */
+static inline bool
+evdev_is_fake_mt_device(struct evdev_device *device)
+{
+ struct libevdev *evdev = device->evdev;
+
+ return libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT) &&
+ libevdev_get_num_slots(evdev) == -1;
+}
+
static inline void
evdev_fix_android_mt(struct evdev_device *device)
{
@@ -1444,12 +1630,13 @@ evdev_fix_android_mt(struct evdev_device *device)
return;
if (!libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) ||
- !libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y))
+ !libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y) ||
+ evdev_is_fake_mt_device(device))
return;
- libevdev_set_abs_info(evdev, ABS_X,
+ libevdev_enable_event_code(evdev, EV_ABS, ABS_X,
libevdev_get_abs_info(evdev, ABS_MT_POSITION_X));
- libevdev_set_abs_info(evdev, ABS_Y,
+ libevdev_enable_event_code(evdev, EV_ABS, ABS_Y,
libevdev_get_abs_info(evdev, ABS_MT_POSITION_Y));
}
@@ -1464,11 +1651,27 @@ evdev_check_min_max(struct evdev_device *device, unsigned int code)
absinfo = libevdev_get_abs_info(evdev, code);
if (absinfo->minimum == absinfo->maximum) {
- log_bug_kernel(device->base.seat->libinput,
- "Device '%s' has min == max on %s\n",
- device->devname,
- libevdev_event_code_get_name(EV_ABS, code));
- return -1;
+ /* Some devices have a sort-of legitimate min/max of 0 for
+ * ABS_MISC and above (e.g. Roccat Kone XTD). Don't ignore
+ * them, simply disable the axes so we won't get events,
+ * we don't know what to do with them anyway.
+ */
+ if (absinfo->minimum == 0 &&
+ code >= ABS_MISC && code < ABS_MT_SLOT) {
+ log_info(device->base.seat->libinput,
+ "Disabling EV_ABS %#x on device '%s' (min == max == 0)\n",
+ code,
+ device->devname);
+ libevdev_disable_event_code(device->evdev,
+ EV_ABS,
+ code);
+ } else {
+ log_bug_kernel(device->base.seat->libinput,
+ "Device '%s' has min == max on %s\n",
+ device->devname,
+ libevdev_event_code_get_name(EV_ABS, code));
+ return -1;
+ }
}
return 0;
@@ -1477,17 +1680,46 @@ evdev_check_min_max(struct evdev_device *device, unsigned int code)
static int
evdev_reject_device(struct evdev_device *device)
{
+ struct libinput *libinput = device->base.seat->libinput;
struct libevdev *evdev = device->evdev;
unsigned int code;
+ const struct input_absinfo *absx, *absy;
if (libevdev_has_event_code(evdev, EV_ABS, ABS_X) ^
libevdev_has_event_code(evdev, EV_ABS, ABS_Y))
return -1;
+ if (libevdev_has_event_code(evdev, EV_REL, REL_X) ^
+ libevdev_has_event_code(evdev, EV_REL, REL_Y))
+ return -1;
+
if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) ^
libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y))
return -1;
+ if (libevdev_has_event_code(evdev, EV_ABS, ABS_X)) {
+ absx = libevdev_get_abs_info(evdev, ABS_X);
+ absy = libevdev_get_abs_info(evdev, ABS_Y);
+ if ((absx->resolution == 0 && absy->resolution != 0) ||
+ (absx->resolution != 0 && absy->resolution == 0)) {
+ log_bug_kernel(libinput,
+ "Kernel has only x or y resolution, not both.\n");
+ return -1;
+ }
+ }
+
+ if (!evdev_is_fake_mt_device(device) &&
+ libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X)) {
+ absx = libevdev_get_abs_info(evdev, ABS_MT_POSITION_X);
+ absy = libevdev_get_abs_info(evdev, ABS_MT_POSITION_Y);
+ if ((absx->resolution == 0 && absy->resolution != 0) ||
+ (absx->resolution != 0 && absy->resolution == 0)) {
+ log_bug_kernel(libinput,
+ "Kernel has only x or y MT resolution, not both.\n");
+ return -1;
+ }
+ }
+
for (code = 0; code < ABS_CNT; code++) {
switch (code) {
case ABS_MISC:
@@ -1550,8 +1782,16 @@ evdev_configure_mt_device(struct evdev_device *device)
for (slot = 0; slot < num_slots; ++slot) {
slots[slot].seat_slot = -1;
- slots[slot].point.x = 0;
- slots[slot].point.y = 0;
+
+ if (evdev_need_mtdev(device))
+ continue;
+
+ slots[slot].point.x = libevdev_get_slot_value(evdev,
+ slot,
+ ABS_MT_POSITION_X);
+ slots[slot].point.y = libevdev_get_slot_value(evdev,
+ slot,
+ ABS_MT_POSITION_Y);
}
device->mt.slots = slots;
device->mt.slots_len = num_slots;
@@ -1615,10 +1855,10 @@ evdev_configure_device(struct evdev_device *device)
return -1;
}
- if (libevdev_has_event_code(evdev, EV_ABS, ABS_X) ||
- libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X)) {
+ if (!evdev_is_fake_mt_device(device))
evdev_fix_android_mt(device);
+ if (libevdev_has_event_code(evdev, EV_ABS, ABS_X)) {
if (evdev_fix_abs_resolution(device,
ABS_X,
ABS_Y,
@@ -1627,12 +1867,10 @@ evdev_configure_device(struct evdev_device *device)
device->abs.fake_resolution = 1;
device->abs.absinfo_x = libevdev_get_abs_info(evdev, ABS_X);
device->abs.absinfo_y = libevdev_get_abs_info(evdev, ABS_Y);
+ device->abs.point.x = device->abs.absinfo_x->value;
+ device->abs.point.y = device->abs.absinfo_y->value;
- /* Fake MT devices have the ABS_MT_SLOT bit set because of
- the limited ABS_* range - they aren't MT devices, they
- just have too many ABS_ axes */
- if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT) &&
- libevdev_get_num_slots(evdev) == -1) {
+ if (evdev_is_fake_mt_device(device)) {
udev_tags &= ~EVDEV_UDEV_TAG_TOUCHSCREEN;
} else if (evdev_configure_mt_device(device) == -1) {
return -1;
@@ -1663,8 +1901,8 @@ evdev_configure_device(struct evdev_device *device)
}
if (udev_tags & EVDEV_UDEV_TAG_MOUSE) {
- if (!libevdev_has_event_code(evdev, EV_ABS, ABS_X) &&
- !libevdev_has_event_code(evdev, EV_ABS, ABS_Y) &&
+ if (libevdev_has_event_code(evdev, EV_REL, REL_X) &&
+ libevdev_has_event_code(evdev, EV_REL, REL_Y) &&
evdev_device_init_pointer_acceleration(
device,
pointer_accel_profile_linear) == -1)
@@ -1689,6 +1927,13 @@ evdev_configure_device(struct evdev_device *device)
log_info(libinput,
"input device '%s', %s is a keyboard\n",
device->devname, devnode);
+
+ /* want natural-scroll config option */
+ if (libevdev_has_event_code(evdev, EV_REL, REL_WHEEL) ||
+ libevdev_has_event_code(evdev, EV_REL, REL_HWHEEL)) {
+ device->scroll.natural_scrolling_enabled = true;
+ device->seat_caps |= EVDEV_DEVICE_POINTER;
+ }
}
if (udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN) {
@@ -1804,7 +2049,8 @@ evdev_device_create(struct libinput_seat *seat,
/* Use non-blocking mode so that we can loop on read on
* evdev_device_data() until all events on the fd are
* read. mtdev_get() also expects this. */
- fd = open_restricted(libinput, devnode, O_RDWR | O_NONBLOCK);
+ fd = open_restricted(libinput, devnode,
+ O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (fd < 0) {
log_info(libinput,
"opening input device '%s' failed (%s).\n",
@@ -1844,6 +2090,7 @@ evdev_device_create(struct libinput_seat *seat,
device->scroll.wheel_click_angle =
evdev_read_wheel_click_prop(device);
device->dpi = evdev_read_dpi_prop(device);
+ device->model = evdev_read_model(device);
/* at most 5 SYN_DROPPED log-messages per 30s */
ratelimit_init(&device->syn_drop_limit, 30ULL * 1000, 5);
@@ -2044,6 +2291,15 @@ evdev_device_has_button(struct evdev_device *device, uint32_t code)
return libevdev_has_event_code(device->evdev, EV_KEY, code);
}
+int
+evdev_device_has_key(struct evdev_device *device, uint32_t code)
+{
+ if (!(device->seat_caps & EVDEV_DEVICE_KEYBOARD))
+ return -1;
+
+ return libevdev_has_event_code(device->evdev, EV_KEY, code);
+}
+
static inline bool
evdev_is_scrolling(const struct evdev_device *device,
enum libinput_pointer_axis axis)
@@ -2121,7 +2377,7 @@ evdev_post_scroll(struct evdev_device *device,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
event.x = 0.0;
- if (event.x != 0.0 || event.y != 0.0) {
+ if (!normalized_is_zero(event)) {
const struct discrete_coords zero_discrete = { 0.0, 0.0 };
evdev_notify_axis(device,
time,
@@ -2154,49 +2410,6 @@ evdev_stop_scroll(struct evdev_device *device,
device->scroll.direction = 0;
}
-static void
-release_pressed_keys(struct evdev_device *device)
-{
- struct libinput *libinput = device->base.seat->libinput;
- uint64_t time;
- int code;
-
- if ((time = libinput_now(libinput)) == 0)
- return;
-
- for (code = 0; code < KEY_CNT; code++) {
- int count = get_key_down_count(device, code);
-
- if (count > 1) {
- log_bug_libinput(libinput,
- "Key %d is down %d times.\n",
- code,
- count);
- }
-
- while (get_key_down_count(device, code) > 0) {
- switch (get_key_type(code)) {
- case EVDEV_KEY_TYPE_NONE:
- break;
- case EVDEV_KEY_TYPE_KEY:
- evdev_keyboard_notify_key(
- device,
- time,
- code,
- LIBINPUT_KEY_STATE_RELEASED);
- break;
- case EVDEV_KEY_TYPE_BUTTON:
- evdev_pointer_notify_button(
- device,
- time,
- evdev_to_left_handed(device, code),
- LIBINPUT_BUTTON_STATE_RELEASED);
- break;
- }
- }
- }
-}
-
void
evdev_notify_suspended_device(struct evdev_device *device)
{
@@ -2242,14 +2455,16 @@ evdev_device_suspend(struct evdev_device *device)
{
evdev_notify_suspended_device(device);
+ if (device->dispatch->interface->suspend)
+ device->dispatch->interface->suspend(device->dispatch,
+ device);
+
if (device->source) {
libinput_remove_source(device->base.seat->libinput,
device->source);
device->source = NULL;
}
- release_pressed_keys(device);
-
if (device->mtdev) {
mtdev_close_delete(device->mtdev);
device->mtdev = NULL;
@@ -2279,7 +2494,8 @@ evdev_device_resume(struct evdev_device *device)
return -ENODEV;
devnode = udev_device_get_devnode(device->udev_device);
- fd = open_restricted(libinput, devnode, O_RDWR | O_NONBLOCK);
+ fd = open_restricted(libinput, devnode,
+ O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (fd < 0)
return -errno;
diff --git a/src/evdev.h b/src/evdev.h
index c49186bc..19454ec8 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -36,6 +36,13 @@
/* The HW DPI rate we normalize to before calculating pointer acceleration */
#define DEFAULT_MOUSE_DPI 1000
+
+/*
+ * The constant (linear) acceleration factor we use to normalize trackpoint
+ * deltas before calculating pointer acceleration.
+ */
+#define DEFAULT_TRACKPOINT_ACCEL 1.0
+
/* The fake resolution value for abs devices without resolution */
#define EVDEV_FAKE_RESOLUTION 1
@@ -64,6 +71,39 @@ enum evdev_device_tags {
EVDEV_TAG_TOUCHPAD_TRACKPOINT = (1 << 3),
};
+enum evdev_middlebutton_state {
+ MIDDLEBUTTON_IDLE,
+ MIDDLEBUTTON_LEFT_DOWN,
+ MIDDLEBUTTON_RIGHT_DOWN,
+ MIDDLEBUTTON_MIDDLE,
+ MIDDLEBUTTON_LEFT_UP_PENDING,
+ MIDDLEBUTTON_RIGHT_UP_PENDING,
+ MIDDLEBUTTON_IGNORE_LR,
+ MIDDLEBUTTON_IGNORE_L,
+ MIDDLEBUTTON_IGNORE_R,
+ MIDDLEBUTTON_PASSTHROUGH,
+};
+
+enum evdev_middlebutton_event {
+ MIDDLEBUTTON_EVENT_L_DOWN,
+ MIDDLEBUTTON_EVENT_R_DOWN,
+ MIDDLEBUTTON_EVENT_OTHER,
+ MIDDLEBUTTON_EVENT_L_UP,
+ MIDDLEBUTTON_EVENT_R_UP,
+ MIDDLEBUTTON_EVENT_TIMEOUT,
+ MIDDLEBUTTON_EVENT_ALL_UP,
+};
+
+enum evdev_device_model {
+ EVDEV_MODEL_DEFAULT,
+ EVDEV_MODEL_LENOVO_X230,
+ EVDEV_MODEL_CHROMEBOOK,
+ EVDEV_MODEL_SYSTEM76_BONOBO,
+ EVDEV_MODEL_SYSTEM76_GALAGO,
+ EVDEV_MODEL_SYSTEM76_KUDU,
+ EVDEV_MODEL_CLEVO_W740SU,
+};
+
struct mt_slot {
int32_t seat_slot;
struct device_coords point;
@@ -159,8 +199,22 @@ struct evdev_device {
void (*change_to_enabled)(struct evdev_device *device);
} left_handed;
+ struct {
+ struct libinput_device_config_middle_emulation config;
+ /* middle-button emulation enabled */
+ bool enabled;
+ bool enabled_default;
+ bool want_enabled;
+ enum evdev_middlebutton_state state;
+ struct libinput_timer timer;
+ uint32_t button_mask;
+ uint64_t first_event_time;
+ } middlebutton;
+
int dpi; /* HW resolution */
struct ratelimit syn_drop_limit; /* ratelimit for SYN_DROPPED logging */
+
+ enum evdev_device_model model;
};
#define EVDEV_UNHANDLED_DEVICE ((struct evdev_device *) 1)
@@ -174,6 +228,10 @@ struct evdev_dispatch_interface {
struct input_event *event,
uint64_t time);
+ /* Device is being suspended */
+ void (*suspend)(struct evdev_dispatch *dispatch,
+ struct evdev_device *device);
+
/* Device is being removed (may be NULL) */
void (*remove)(struct evdev_dispatch *dispatch);
@@ -283,6 +341,9 @@ evdev_device_get_size(struct evdev_device *device,
int
evdev_device_has_button(struct evdev_device *device, uint32_t code);
+int
+evdev_device_has_key(struct evdev_device *device, uint32_t code);
+
double
evdev_device_transform_x(struct evdev_device *device,
double x,
@@ -315,6 +376,11 @@ evdev_pointer_notify_button(struct evdev_device *device,
uint32_t time,
int button,
enum libinput_button_state state);
+void
+evdev_pointer_notify_physical_button(struct evdev_device *device,
+ uint32_t time,
+ int button,
+ enum libinput_button_state state);
void
evdev_init_natural_scroll(struct evdev_device *device);
@@ -336,6 +402,17 @@ evdev_device_remove(struct evdev_device *device);
void
evdev_device_destroy(struct evdev_device *device);
+bool
+evdev_middlebutton_filter_button(struct evdev_device *device,
+ uint64_t time,
+ int button,
+ enum libinput_button_state state);
+
+void
+evdev_init_middlebutton(struct evdev_device *device,
+ bool enabled,
+ bool want_config);
+
static inline double
evdev_convert_to_mm(const struct input_absinfo *absinfo, double v)
{
diff --git a/src/filter.c b/src/filter.c
index dc299286..626cb0aa 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -1,4 +1,5 @@
/*
+ * Copyright © 2006-2009 Simon Thum
* Copyright © 2012 Jonas Ådahl
*
* Permission to use, copy, modify, distribute, and sell this software and
@@ -122,7 +123,7 @@ feed_trackers(struct pointer_accelerator *accel,
trackers[current].delta.x = 0.0;
trackers[current].delta.y = 0.0;
trackers[current].time = time;
- trackers[current].dir = vector_get_direction(delta->x, delta->y);
+ trackers[current].dir = normalized_get_direction(*delta);
}
static struct pointer_tracker *
@@ -137,11 +138,9 @@ tracker_by_offset(struct pointer_accelerator *accel, unsigned int offset)
static double
calculate_tracker_velocity(struct pointer_tracker *tracker, uint64_t time)
{
- double distance;
double tdelta = time - tracker->time + 1;
- distance = hypot(tracker->delta.x, tracker->delta.y);
- return distance / tdelta; /* units/ms */
+ return normalized_length(tracker->delta) / tdelta; /* units/ms */
}
static double
@@ -260,13 +259,15 @@ accelerator_set_speed(struct motion_filter *filter,
assert(speed >= -1.0 && speed <= 1.0);
/* delay when accel kicks in */
- accel_filter->threshold = DEFAULT_THRESHOLD - speed/6.0;
+ accel_filter->threshold = DEFAULT_THRESHOLD - speed / 4.0;
+ if (accel_filter->threshold < 0.2)
+ accel_filter->threshold = 0.2;
/* adjust max accel factor */
- accel_filter->accel = DEFAULT_ACCELERATION + speed;
+ accel_filter->accel = DEFAULT_ACCELERATION + speed * 1.5;
/* higher speed -> faster to reach max */
- accel_filter->incline = DEFAULT_INCLINE + speed/2.0;
+ accel_filter->incline = DEFAULT_INCLINE + speed * 0.75;
filter->speed = speed;
return true;
@@ -345,3 +346,40 @@ touchpad_accel_profile_linear(struct motion_filter *filter,
return speed_out * TP_MAGIC_SLOWDOWN;
}
+
+double
+touchpad_lenovo_x230_accel_profile(struct motion_filter *filter,
+ void *data,
+ double speed_in,
+ uint64_t time)
+{
+ /* Keep the magic factor from touchpad_accel_profile_linear. */
+ const double TP_MAGIC_SLOWDOWN = 0.4;
+
+ /* Those touchpads presents an actual lower resolution that what is
+ * advertised. We see some jumps from the cursor due to the big steps
+ * in X and Y when we are receiving data.
+ * Apply a factor to minimize those jumps at low speed, and try
+ * keeping the same feeling as regular touchpads at high speed.
+ * It still feels slower but it is usable at least */
+ const double TP_MAGIC_LOW_RES_FACTOR = 4.0;
+ double speed_out;
+ struct pointer_accelerator *accel_filter =
+ (struct pointer_accelerator *)filter;
+
+ double s1, s2;
+ const double max_accel = accel_filter->accel *
+ TP_MAGIC_LOW_RES_FACTOR; /* unitless factor */
+ const double threshold = accel_filter->threshold /
+ TP_MAGIC_LOW_RES_FACTOR; /* units/ms */
+ const double incline = accel_filter->incline * TP_MAGIC_LOW_RES_FACTOR;
+
+ speed_in *= TP_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR;
+
+ s1 = min(1, speed_in * 5);
+ s2 = 1 + (speed_in - threshold) * incline;
+
+ speed_out = min(max_accel, s2 > 1 ? s2 : s1);
+
+ return speed_out * TP_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR;
+}
diff --git a/src/filter.h b/src/filter.h
index 70363a62..a0538601 100644
--- a/src/filter.h
+++ b/src/filter.h
@@ -67,4 +67,9 @@ touchpad_accel_profile_linear(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time);
+double
+touchpad_lenovo_x230_accel_profile(struct motion_filter *filter,
+ void *data,
+ double speed_in,
+ uint64_t time);
#endif /* FILTER_H */
diff --git a/src/libinput-private.h b/src/libinput-private.h
index d1da276d..ae20f807 100644
--- a/src/libinput-private.h
+++ b/src/libinput-private.h
@@ -24,6 +24,7 @@
#define LIBINPUT_PRIVATE_H
#include
+#include
#include "linux/input.h"
@@ -39,6 +40,14 @@ struct device_coords {
int x, y;
};
+/*
+ * A coordinate pair in device coordinates, capable of holding non discrete
+ * values, this is necessary e.g. when device coordinates get averaged.
+ */
+struct device_float_coords {
+ double x, y;
+};
+
/* A dpi-normalized coordinate pair */
struct normalized_coords {
double x, y;
@@ -173,6 +182,17 @@ struct libinput_device_config_click_method {
enum libinput_config_click_method (*get_default_method)(struct libinput_device *device);
};
+struct libinput_device_config_middle_emulation {
+ int (*available)(struct libinput_device *device);
+ enum libinput_config_status (*set)(
+ struct libinput_device *device,
+ enum libinput_config_middle_emulation_state);
+ enum libinput_config_middle_emulation_state (*get)(
+ struct libinput_device *device);
+ enum libinput_config_middle_emulation_state (*get_default)(
+ struct libinput_device *device);
+};
+
struct libinput_device_config {
struct libinput_device_config_tap *tap;
struct libinput_device_config_calibration *calibration;
@@ -182,6 +202,7 @@ struct libinput_device_config {
struct libinput_device_config_left_handed *left_handed;
struct libinput_device_config_scroll_method *scroll_method;
struct libinput_device_config_click_method *click_method;
+ struct libinput_device_config_middle_emulation *middle_emulation;
};
struct libinput_device_group {
@@ -393,4 +414,85 @@ libinput_now(struct libinput *libinput)
return ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000;
}
+
+static inline struct device_float_coords
+device_delta(struct device_coords a, struct device_coords b)
+{
+ struct device_float_coords delta;
+
+ delta.x = a.x - b.x;
+ delta.y = a.y - b.y;
+
+ return delta;
+}
+
+static inline double
+normalized_length(struct normalized_coords norm)
+{
+ return hypot(norm.x, norm.y);
+}
+
+static inline int
+normalized_is_zero(struct normalized_coords norm)
+{
+ return norm.x == 0.0 && norm.y == 0.0;
+}
+
+enum directions {
+ N = 1 << 0,
+ NE = 1 << 1,
+ E = 1 << 2,
+ SE = 1 << 3,
+ S = 1 << 4,
+ SW = 1 << 5,
+ W = 1 << 6,
+ NW = 1 << 7,
+ UNDEFINED_DIRECTION = 0xff
+};
+
+static inline int
+normalized_get_direction(struct normalized_coords norm)
+{
+ int dir = UNDEFINED_DIRECTION;
+ int d1, d2;
+ double r;
+
+ if (fabs(norm.x) < 2.0 && fabs(norm.y) < 2.0) {
+ if (norm.x > 0.0 && norm.y > 0.0)
+ dir = S | SE | E;
+ else if (norm.x > 0.0 && norm.y < 0.0)
+ dir = N | NE | E;
+ else if (norm.x < 0.0 && norm.y > 0.0)
+ dir = S | SW | W;
+ else if (norm.x < 0.0 && norm.y < 0.0)
+ dir = N | NW | W;
+ else if (norm.x > 0.0)
+ dir = NE | E | SE;
+ else if (norm.x < 0.0)
+ dir = NW | W | SW;
+ else if (norm.y > 0.0)
+ dir = SE | S | SW;
+ else if (norm.y < 0.0)
+ dir = NE | N | NW;
+ } else {
+ /* Calculate r within the interval [0 to 8)
+ *
+ * r = [0 .. 2π] where 0 is North
+ * d_f = r / 2π ([0 .. 1))
+ * d_8 = 8 * d_f
+ */
+ r = atan2(norm.y, norm.x);
+ r = fmod(r + 2.5*M_PI, 2*M_PI);
+ r *= 4*M_1_PI;
+
+ /* Mark one or two close enough octants */
+ d1 = (int)(r + 0.9) % 8;
+ d2 = (int)(r + 0.1) % 8;
+
+ dir = (1 << d1) | (1 << d2);
+ }
+
+ return dir;
+}
+
#endif /* LIBINPUT_PRIVATE_H */
diff --git a/src/libinput-util.c b/src/libinput-util.c
index 49e297af..4857435b 100644
--- a/src/libinput-util.c
+++ b/src/libinput-util.c
@@ -29,6 +29,7 @@
#include "config.h"
#include
+#include
#include
#include
#include
@@ -201,3 +202,33 @@ parse_mouse_wheel_click_angle_property(const char *prop)
return angle;
}
+
+/**
+ * Helper function to parse the TRACKPOINT_CONST_ACCEL property from udev.
+ * Property is of the form:
+ * TRACKPOINT_CONST_ACCEL=
+ *
+ * @param prop The value of the udev property (without the TRACKPOINT_CONST_ACCEL=)
+ * @return The acceleration, or 0.0 on error.
+ */
+double
+parse_trackpoint_accel_property(const char *prop)
+{
+ locale_t c_locale;
+ double accel;
+ char *endp;
+
+ /* Create a "C" locale to force strtod to use '.' as separator */
+ c_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
+ if (c_locale == (locale_t)0)
+ return 0.0;
+
+ accel = strtod_l(prop, &endp, c_locale);
+
+ freelocale(c_locale);
+
+ if (*endp != '\0')
+ return 0.0;
+
+ return accel;
+}
diff --git a/src/libinput-util.h b/src/libinput-util.h
index 7e19bf0e..28deae72 100644
--- a/src/libinput-util.h
+++ b/src/libinput-util.h
@@ -122,63 +122,6 @@ msleep(unsigned int ms)
usleep(ms * 1000);
}
-enum directions {
- N = 1 << 0,
- NE = 1 << 1,
- E = 1 << 2,
- SE = 1 << 3,
- S = 1 << 4,
- SW = 1 << 5,
- W = 1 << 6,
- NW = 1 << 7,
- UNDEFINED_DIRECTION = 0xff
-};
-
-static inline int
-vector_get_direction(double dx, double dy)
-{
- int dir = UNDEFINED_DIRECTION;
- int d1, d2;
- double r;
-
- if (fabs(dx) < 2.0 && fabs(dy) < 2.0) {
- if (dx > 0.0 && dy > 0.0)
- dir = S | SE | E;
- else if (dx > 0.0 && dy < 0.0)
- dir = N | NE | E;
- else if (dx < 0.0 && dy > 0.0)
- dir = S | SW | W;
- else if (dx < 0.0 && dy < 0.0)
- dir = N | NW | W;
- else if (dx > 0.0)
- dir = NE | E | SE;
- else if (dx < 0.0)
- dir = NW | W | SW;
- else if (dy > 0.0)
- dir = SE | S | SW;
- else if (dy < 0.0)
- dir = NE | N | NW;
- } else {
- /* Calculate r within the interval [0 to 8)
- *
- * r = [0 .. 2π] where 0 is North
- * d_f = r / 2π ([0 .. 1))
- * d_8 = 8 * d_f
- */
- r = atan2(dy, dx);
- r = fmod(r + 2.5*M_PI, 2*M_PI);
- r *= 4*M_1_PI;
-
- /* Mark one or two close enough octants */
- d1 = (int)(r + 0.9) % 8;
- d2 = (int)(r + 0.1) % 8;
-
- dir = (1 << d1) | (1 << d2);
- }
-
- return dir;
-}
-
static inline int
long_bit_is_set(const unsigned long *array, int bit)
{
@@ -323,5 +266,6 @@ enum ratelimit_state ratelimit_test(struct ratelimit *r);
int parse_mouse_dpi_property(const char *prop);
int parse_mouse_wheel_click_angle_property(const char *prop);
+double parse_trackpoint_accel_property(const char *prop);
#endif /* LIBINPUT_UTIL_H */
diff --git a/src/libinput.c b/src/libinput.c
index 144e9506..61e2b6b2 100644
--- a/src/libinput.c
+++ b/src/libinput.c
@@ -1326,6 +1326,35 @@ notify_removed_device(struct libinput_device *device)
&removed_device_event->base);
}
+static inline bool
+device_has_cap(struct libinput_device *device,
+ enum libinput_device_capability cap)
+{
+ const char *capability;
+
+ if (libinput_device_has_capability(device, cap))
+ return true;
+
+ switch (cap) {
+ case LIBINPUT_DEVICE_CAP_POINTER:
+ capability = "CAP_POINTER";
+ break;
+ case LIBINPUT_DEVICE_CAP_KEYBOARD:
+ capability = "CAP_KEYBOARD";
+ break;
+ case LIBINPUT_DEVICE_CAP_TOUCH:
+ capability = "CAP_TOUCH";
+ break;
+ }
+
+ log_bug_libinput(device->seat->libinput,
+ "Event for missing capability %s on device \"%s\"\n",
+ capability,
+ libinput_device_get_name(device));
+
+ return false;
+}
+
void
keyboard_notify_key(struct libinput_device *device,
uint64_t time,
@@ -1335,6 +1364,9 @@ keyboard_notify_key(struct libinput_device *device,
struct libinput_event_keyboard *key_event;
uint32_t seat_key_count;
+ if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_KEYBOARD))
+ return;
+
key_event = zalloc(sizeof *key_event);
if (!key_event)
return;
@@ -1361,6 +1393,9 @@ pointer_notify_motion(struct libinput_device *device,
{
struct libinput_event_pointer *motion_event;
+ if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_POINTER))
+ return;
+
motion_event = zalloc(sizeof *motion_event);
if (!motion_event)
return;
@@ -1383,6 +1418,9 @@ pointer_notify_motion_absolute(struct libinput_device *device,
{
struct libinput_event_pointer *motion_absolute_event;
+ if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_POINTER))
+ return;
+
motion_absolute_event = zalloc(sizeof *motion_absolute_event);
if (!motion_absolute_event)
return;
@@ -1406,6 +1444,9 @@ pointer_notify_button(struct libinput_device *device,
struct libinput_event_pointer *button_event;
int32_t seat_button_count;
+ if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_POINTER))
+ return;
+
button_event = zalloc(sizeof *button_event);
if (!button_event)
return;
@@ -1436,6 +1477,9 @@ pointer_notify_axis(struct libinput_device *device,
{
struct libinput_event_pointer *axis_event;
+ if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_POINTER))
+ return;
+
axis_event = zalloc(sizeof *axis_event);
if (!axis_event)
return;
@@ -1462,6 +1506,9 @@ touch_notify_touch_down(struct libinput_device *device,
{
struct libinput_event_touch *touch_event;
+ if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_TOUCH))
+ return;
+
touch_event = zalloc(sizeof *touch_event);
if (!touch_event)
return;
@@ -1487,6 +1534,9 @@ touch_notify_touch_motion(struct libinput_device *device,
{
struct libinput_event_touch *touch_event;
+ if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_TOUCH))
+ return;
+
touch_event = zalloc(sizeof *touch_event);
if (!touch_event)
return;
@@ -1511,6 +1561,9 @@ touch_notify_touch_up(struct libinput_device *device,
{
struct libinput_event_touch *touch_event;
+ if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_TOUCH))
+ return;
+
touch_event = zalloc(sizeof *touch_event);
if (!touch_event)
return;
@@ -1532,6 +1585,9 @@ touch_notify_frame(struct libinput_device *device,
{
struct libinput_event_touch *touch_event;
+ if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_TOUCH))
+ return;
+
touch_event = zalloc(sizeof *touch_event);
if (!touch_event)
return;
@@ -1855,6 +1911,12 @@ libinput_device_pointer_has_button(struct libinput_device *device, uint32_t code
return evdev_device_has_button((struct evdev_device *)device, code);
}
+LIBINPUT_EXPORT int
+libinput_device_keyboard_has_key(struct libinput_device *device, uint32_t code)
+{
+ return evdev_device_has_key((struct evdev_device *)device, code);
+}
+
LIBINPUT_EXPORT struct libinput_event *
libinput_event_device_notify_get_base_event(struct libinput_event_device_notify *event)
{
@@ -2010,11 +2072,12 @@ libinput_device_config_tap_set_enabled(struct libinput_device *device,
enable != LIBINPUT_CONFIG_TAP_DISABLED)
return LIBINPUT_CONFIG_STATUS_INVALID;
- if (enable &&
- libinput_device_config_tap_get_finger_count(device) == 0)
- return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
+ if (libinput_device_config_tap_get_finger_count(device) == 0)
+ return enable ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED :
+ LIBINPUT_CONFIG_STATUS_SUCCESS;
return device->config.tap->set_enabled(device, enable);
+
}
LIBINPUT_EXPORT enum libinput_config_tap_state
@@ -2237,9 +2300,6 @@ LIBINPUT_EXPORT enum libinput_config_status
libinput_device_config_click_set_method(struct libinput_device *device,
enum libinput_config_click_method method)
{
- if ((libinput_device_config_click_get_methods(device) & method) != method)
- return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
-
/* Check method is a single valid method */
switch (method) {
case LIBINPUT_CONFIG_CLICK_METHOD_NONE:
@@ -2250,6 +2310,9 @@ libinput_device_config_click_set_method(struct libinput_device *device,
return LIBINPUT_CONFIG_STATUS_INVALID;
}
+ if ((libinput_device_config_click_get_methods(device) & method) != method)
+ return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
+
if (device->config.click_method)
return device->config.click_method->set_method(device, method);
else /* method must be _NONE to get here */
@@ -2274,6 +2337,60 @@ libinput_device_config_click_get_default_method(struct libinput_device *device)
return LIBINPUT_CONFIG_CLICK_METHOD_NONE;
}
+LIBINPUT_EXPORT int
+libinput_device_config_middle_emulation_is_available(
+ struct libinput_device *device)
+{
+ if (device->config.middle_emulation)
+ return device->config.middle_emulation->available(device);
+ else
+ return LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
+}
+
+LIBINPUT_EXPORT enum libinput_config_status
+libinput_device_config_middle_emulation_set_enabled(
+ struct libinput_device *device,
+ enum libinput_config_middle_emulation_state enable)
+{
+ int available =
+ libinput_device_config_middle_emulation_is_available(device);
+
+ switch (enable) {
+ case LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED:
+ if (!available)
+ return LIBINPUT_CONFIG_STATUS_SUCCESS;
+ break;
+ case LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED:
+ if (!available)
+ return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
+ break;
+ default:
+ return LIBINPUT_CONFIG_STATUS_INVALID;
+ }
+
+ return device->config.middle_emulation->set(device, enable);
+}
+
+LIBINPUT_EXPORT enum libinput_config_middle_emulation_state
+libinput_device_config_middle_emulation_get_enabled(
+ struct libinput_device *device)
+{
+ if (!libinput_device_config_middle_emulation_is_available(device))
+ return LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
+
+ return device->config.middle_emulation->get(device);
+}
+
+LIBINPUT_EXPORT enum libinput_config_middle_emulation_state
+libinput_device_config_middle_emulation_get_default_enabled(
+ struct libinput_device *device)
+{
+ if (!libinput_device_config_middle_emulation_is_available(device))
+ return LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
+
+ return device->config.middle_emulation->get_default(device);
+}
+
LIBINPUT_EXPORT uint32_t
libinput_device_config_scroll_get_methods(struct libinput_device *device)
{
diff --git a/src/libinput.h b/src/libinput.h
index a795e3f9..5bda5fe7 100644
--- a/src/libinput.h
+++ b/src/libinput.h
@@ -2183,6 +2183,22 @@ libinput_device_get_size(struct libinput_device *device,
int
libinput_device_pointer_has_button(struct libinput_device *device, uint32_t code);
+/**
+ * @ingroup device
+ *
+ * Check if a @ref LIBINPUT_DEVICE_CAP_KEYBOARD device has a key with the
+ * given code (see linux/input.h).
+ *
+ * @param device A current input device
+ * @param code Key code to check for, e.g. KEY_ESC
+ *
+ * @return 1 if the device supports this key code, 0 if it does not, -1
+ * on error.
+ */
+int
+libinput_device_keyboard_has_key(struct libinput_device *device,
+ uint32_t code);
+
/**
* @ingroup device
*
@@ -2914,6 +2930,130 @@ libinput_device_config_click_get_method(struct libinput_device *device);
enum libinput_config_click_method
libinput_device_config_click_get_default_method(struct libinput_device *device);
+/**
+ * @ingroup config
+ */
+enum libinput_config_middle_emulation_state {
+ /**
+ * Middle mouse button emulation is to be disabled, or
+ * is currently disabled.
+ */
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED,
+ /**
+ * Middle mouse button emulation is to be enabled, or
+ * is currently enabled.
+ */
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED,
+};
+
+/**
+ * @ingroup config
+ *
+ * Check if middle mouse button emulation configuration is available on this
+ * device. See libinput_device_config_middle_emulation_set_enabled() for
+ * details.
+ *
+ * @note Some devices provide middle mouse button emulation but do not allow
+ * enabling/disabling that emulation. These devices return zero in
+ * libinput_device_config_middle_emulation_is_available().
+ *
+ * @param device The device to query
+ *
+ * @return Non-zero if middle mouse button emulation is available and can be
+ * configured, zero otherwise.
+ *
+ * @see libinput_device_config_middle_emulation_set_enabled
+ * @see libinput_device_config_middle_emulation_get_enabled
+ * @see libinput_device_config_middle_emulation_get_default_enabled
+ */
+int
+libinput_device_config_middle_emulation_is_available(
+ struct libinput_device *device);
+
+/**
+ * @ingroup config
+ *
+ * Enable or disable middle button emulation on this device. When enabled, a
+ * simultaneous press of the left and right button generates a middle mouse
+ * button event. Releasing the buttons generates a middle mouse button
+ * release, the left and right button events are discarded otherwise.
+ *
+ * The middle button release event may be generated when either button is
+ * released, or when both buttons have been released. The exact behavior is
+ * device-dependent.
+ *
+ * The middle button emulation behavior when combined with other device
+ * buttons, including a physical middle button is device-dependent.
+ *
+ * @note Some devices provide middle mouse button emulation but do not allow
+ * enabling/disabling that emulation.
+ *
+ * @param device The device to configure
+ * @param enable @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED to
+ * disable, @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED To enable
+ * middle button emulation.
+ *
+ * @return A config status code. Disabling middle button emulation on a
+ * device that does not support middle button emulation always succeeds.
+ *
+ * @see libinput_device_config_middle_emulation_is_available
+ * @see libinput_device_config_middle_emulation_get_enabled
+ * @see libinput_device_config_middle_emulation_get_default_enabled
+ */
+enum libinput_config_status
+libinput_device_config_middle_emulation_set_enabled(
+ struct libinput_device *device,
+ enum libinput_config_middle_emulation_state enable);
+
+/**
+ * @ingroup config
+ *
+ * Check if configurable middle button emulation is enabled on this device.
+ * If the device does not have configurable middle button emulation, this
+ * function returns @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED.
+ *
+ * @note Some devices provide middle mouse button emulation but do not allow
+ * enabling/disabling that emulation. These devices always return @ref
+ * LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED.
+ *
+ * @param device The device to configure
+ * @return @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED if disabled
+ * or not available/configurable, @ref
+ * LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED If enabled.
+ *
+ * @see libinput_device_config_middle_emulation_is_available
+ * @see libinput_device_config_middle_emulation_set_enabled
+ * @see libinput_device_config_middle_emulation_get_default_enabled
+ */
+enum libinput_config_middle_emulation_state
+libinput_device_config_middle_emulation_get_enabled(
+ struct libinput_device *device);
+
+/**
+ * @ingroup config
+ *
+ * Check if configurable middle button emulation is enabled by default on
+ * this device. If the device does not have configurable middle button
+ * emulation, this function returns @ref
+ * LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED.
+ *
+ * @note Some devices provide middle mouse button emulation but do not allow
+ * enabling/disabling that emulation. These devices always return @ref
+ * LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED.
+ *
+ * @param device The device to configure
+ * @return @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED If disabled
+ * or not available, @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED if
+ * enabled.
+ *
+ * @see libinput_device_config_middle_emulation_is_available
+ * @see libinput_device_config_middle_emulation_set_enabled
+ * @see libinput_device_config_middle_emulation_get_enabled
+ */
+enum libinput_config_middle_emulation_state
+libinput_device_config_middle_emulation_get_default_enabled(
+ struct libinput_device *device);
+
/**
* @ingroup config
*
diff --git a/src/libinput.sym b/src/libinput.sym
index deab3c20..1ad6eef4 100644
--- a/src/libinput.sym
+++ b/src/libinput.sym
@@ -129,6 +129,19 @@ local:
*;
};
+LIBINPUT_0.14.0 {
+global:
+ libinput_device_config_middle_emulation_get_default_enabled;
+ libinput_device_config_middle_emulation_get_enabled;
+ libinput_device_config_middle_emulation_is_available;
+ libinput_device_config_middle_emulation_set_enabled;
+} LIBINPUT_0.12.0;
+
+LIBINPUT_0.15.0 {
+global:
+ libinput_device_keyboard_has_key;
+} LIBINPUT_0.14.0;
+
/* tablet APIs, they are not part of any stable API promise yet.
* keep them separate */
LIBINPUT_TABLET_SUPPORT {
@@ -154,4 +167,4 @@ LIBINPUT_TABLET_SUPPORT {
libinput_tool_ref;
libinput_tool_set_user_data;
libinput_tool_unref;
-} LIBINPUT_0.12.0;
+} LIBINPUT_0.15.0;
diff --git a/src/path.c b/src/path.c
index 832a1fd8..f9b1a6c2 100644
--- a/src/path.c
+++ b/src/path.c
@@ -310,7 +310,7 @@ udev_device_from_devnode(struct libinput *libinput,
dev = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
count++;
- if (count > 10) {
+ if (count > 50) {
log_bug_libinput(libinput,
"udev device never initialized (%s)\n",
devnode);
diff --git a/src/timer.c b/src/timer.c
index f6c8e427..d1d3c108 100644
--- a/src/timer.c
+++ b/src/timer.c
@@ -101,6 +101,15 @@ libinput_timer_handler(void *data)
struct libinput *libinput = data;
struct libinput_timer *timer, *tmp;
uint64_t now;
+ uint64_t discard;
+ int r;
+
+ r = read(libinput->timer.fd, &discard, sizeof(discard));
+ if (r == -1 && errno != EAGAIN)
+ log_bug_libinput(libinput,
+ "Error %d reading from timerfd (%s)",
+ errno,
+ strerror(errno));
now = libinput_now(libinput);
if (now == 0)
@@ -119,7 +128,8 @@ libinput_timer_handler(void *data)
int
libinput_timer_subsys_init(struct libinput *libinput)
{
- libinput->timer.fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+ libinput->timer.fd = timerfd_create(CLOCK_MONOTONIC,
+ TFD_CLOEXEC | TFD_NONBLOCK);
if (libinput->timer.fd < 0)
return -1;
diff --git a/test/Makefile.am b/test/Makefile.am
index 2875d295..3f28a0a3 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -14,10 +14,14 @@ liblitest_la_SOURCES = \
litest.h \
litest-int.h \
litest-alps-semi-mt.c \
+ litest-atmel-hover.c \
litest-bcm5974.c \
litest-generic-singletouch.c \
litest-keyboard.c \
+ litest-keyboard-razer-blackwidow.c \
+ litest-logitech-trackball.c \
litest-mouse.c \
+ litest-mouse-roccat.c \
litest-ms-surface-cover.c \
litest-protocol-a-touch-screen.c \
litest-qemu-usb-tablet.c \
@@ -33,23 +37,31 @@ liblitest_la_SOURCES = \
litest-wacom-isdv4-tablet.c \
litest-wacom-touch.c \
litest-wacom-intuos-finger.c \
+ litest-wheel-only.c \
litest-xen-virtual-pointer.c \
litest-vmware-virtual-usb-mouse.c \
litest.c
liblitest_la_LIBADD = $(top_builddir)/src/libinput-util.la
+liblitest_la_CFLAGS = $(AM_CFLAGS)
+if HAVE_LIBUNWIND
+liblitest_la_LIBADD += $(LIBUNWIND_LIBS) -ldl
+liblitest_la_CFLAGS += $(LIBUNWIND_CFLAGS)
+endif
run_tests = \
- test-udev \
- test-path \
+ test-touchpad \
+ test-tablet \
+ test-device \
test-pointer \
test-touch \
- test-log \
- test-tablet \
- test-touchpad \
test-trackpoint \
+ test-udev \
+ test-path \
+ test-log \
test-misc \
test-keyboard \
- test-device
+ test-litest-selftest
+
build_tests = \
test-build-cxx \
test-build-linker \
@@ -83,7 +95,6 @@ test_log_LDADD = $(TEST_LIBS)
test_log_LDFLAGS = -no-install
test_tablet_SOURCES = tablet.c
-test_tablet_CFLAGS = $(AM_CPPFLAGS)
test_tablet_LDADD = $(TEST_LIBS)
test_tablet_LDFLAGS = -static
@@ -107,6 +118,11 @@ test_device_SOURCES = device.c
test_device_LDADD = $(TEST_LIBS)
test_device_LDFLAGS = -no-install
+test_litest_selftest_SOURCES = litest-selftest.c litest.c litest-int.h litest.h
+test_litest_selftest_CFLAGS = -DLITEST_DISABLE_BACKTRACE_LOGGING -DLITEST_NO_MAIN
+test_litest_selftest_LDADD = $(TEST_LIBS)
+test_litest_selftest_LDFLAGS = -no-install
+
# build-test only
test_build_pedantic_c99_SOURCES = build-pedantic.c
test_build_pedantic_c99_CFLAGS = -std=c99 -pedantic -Werror
@@ -123,6 +139,8 @@ test_build_linker_LDADD = $(top_builddir)/src/libinput.la $(top_builddir)/src/li
test_build_cxx_SOURCES = build-cxx.cc
test_build_cxx_CXXFLAGS = -Wall -Wextra -Wno-unused-parameter $(AM_CXXFLAGS)
+AM_TESTS_ENVIRONMENT= LITEST_VERBOSE=1; export LITEST_VERBOSE;
+
if HAVE_VALGRIND
VALGRIND_FLAGS=--leak-check=full \
--quiet \
@@ -130,7 +148,7 @@ VALGRIND_FLAGS=--leak-check=full \
--suppressions=$(srcdir)/valgrind.suppressions
valgrind:
- $(MAKE) check-TESTS LOG_COMPILER="$(VALGRIND)" LOG_FLAGS="$(VALGRIND_FLAGS)" CK_FORK=no
+ $(MAKE) check-TESTS LOG_COMPILER="$(VALGRIND)" LOG_FLAGS="$(VALGRIND_FLAGS)" CK_FORK=no USING_VALGRIND=yes
check: valgrind
diff --git a/test/build-pedantic.c b/test/build-pedantic.c
index 920fc4ab..f602127d 100644
--- a/test/build-pedantic.c
+++ b/test/build-pedantic.c
@@ -3,6 +3,7 @@
/* This is a build-test only */
int
-main(void) {
+main(void)
+{
return 0;
}
diff --git a/test/device.c b/test/device.c
index 4c4e2532..193c3ffb 100644
--- a/test/device.c
+++ b/test/device.c
@@ -313,6 +313,9 @@ START_TEST(device_reenable_syspath_changed)
litest_device = litest_add_device(li, LITEST_MOUSE);
device2 = litest_device->libinput_device;
+ /* Note: if the sysname isn't the same, some other device got added
+ * or removed while this test was running. This is unlikely and
+ * would result in a false positive, so let's fail the test here */
ck_assert_str_eq(libinput_device_get_sysname(device1),
libinput_device_get_sysname(device2));
@@ -814,37 +817,39 @@ START_TEST(abs_mt_device_no_absx)
}
END_TEST
-START_TEST(abs_device_no_range)
+static void
+assert_device_ignored(struct libinput *li, struct input_absinfo *absinfo)
{
struct libevdev_uinput *uinput;
- struct libinput *li;
struct libinput_device *device;
- int code;
+
+ uinput = litest_create_uinput_abs_device("test device", NULL,
+ absinfo,
+ EV_KEY, BTN_LEFT,
+ EV_KEY, BTN_RIGHT,
+ -1);
+ device = libinput_path_add_device(li,
+ libevdev_uinput_get_devnode(uinput));
+ litest_assert_ptr_null(device);
+ libevdev_uinput_destroy(uinput);
+}
+
+START_TEST(abs_device_no_range)
+{
+ struct libinput *li;
+ int code = _i; /* looped test */
/* set x/y so libinput doesn't just reject for missing axes */
struct input_absinfo absinfo[] = {
{ ABS_X, 0, 10, 0, 0, 0 },
{ ABS_Y, 0, 10, 0, 0, 0 },
- { -1, 0, 0, 0, 0, 0 },
+ { code, 0, 0, 0, 0, 0 },
{ -1, -1, -1, -1, -1, -1 }
};
li = litest_create_context();
litest_disable_log_handler(li);
- for (code = 0; code < ABS_MT_SLOT; code++) {
- if (code == ABS_MISC)
- continue;
- absinfo[2].value = code;
- uinput = litest_create_uinput_abs_device("test device", NULL,
- absinfo,
- EV_KEY, BTN_LEFT,
- EV_KEY, BTN_RIGHT,
- -1);
- device = libinput_path_add_device(li,
- libevdev_uinput_get_devnode(uinput));
- ck_assert(device == NULL);
- libevdev_uinput_destroy(uinput);
- }
+ assert_device_ignored(li, absinfo);
litest_restore_log_handler(li);
libinput_unref(li);
@@ -853,10 +858,8 @@ END_TEST
START_TEST(abs_mt_device_no_range)
{
- struct libevdev_uinput *uinput;
struct libinput *li;
- struct libinput_device *device;
- int code;
+ int code = _i; /* looped test */
/* set x/y so libinput doesn't just reject for missing axes */
struct input_absinfo absinfo[] = {
{ ABS_X, 0, 10, 0, 0, 0 },
@@ -865,37 +868,90 @@ START_TEST(abs_mt_device_no_range)
{ ABS_MT_TRACKING_ID, 0, 255, 0, 0, 0 },
{ ABS_MT_POSITION_X, 0, 10, 0, 0, 0 },
{ ABS_MT_POSITION_Y, 0, 10, 0, 0, 0 },
- { -1, 0, 0, 0, 0, 0 },
+ { code, 0, 0, 0, 0, 0 },
{ -1, -1, -1, -1, -1, -1 }
};
li = litest_create_context();
litest_disable_log_handler(li);
- for (code = ABS_MT_SLOT + 1; code < ABS_CNT; code++) {
- if (code == ABS_MT_TOOL_TYPE ||
- code == ABS_MT_TRACKING_ID) /* kernel overrides it */
- continue;
-
- absinfo[6].value = code;
- uinput = litest_create_uinput_abs_device("test device", NULL,
- absinfo,
- EV_KEY, BTN_LEFT,
- EV_KEY, BTN_RIGHT,
- -1);
- device = libinput_path_add_device(li,
- libevdev_uinput_get_devnode(uinput));
- ck_assert(device == NULL);
- libevdev_uinput_destroy(uinput);
- }
+ if (code != ABS_MT_TOOL_TYPE &&
+ code != ABS_MT_TRACKING_ID) /* kernel overrides it */
+ assert_device_ignored(li, absinfo);
litest_restore_log_handler(li);
libinput_unref(li);
}
END_TEST
-int main (int argc, char **argv)
+START_TEST(abs_device_missing_res)
{
+ struct libinput *li;
+ struct input_absinfo absinfo[] = {
+ { ABS_X, 0, 10, 0, 0, 10 },
+ { ABS_Y, 0, 10, 0, 0, 0 },
+ { -1, -1, -1, -1, -1, -1 }
+ };
+
+ li = litest_create_context();
+ litest_disable_log_handler(li);
+
+ assert_device_ignored(li, absinfo);
+
+ absinfo[0].resolution = 0;
+ absinfo[1].resolution = 20;
+
+ assert_device_ignored(li, absinfo);
+
+ litest_restore_log_handler(li);
+ libinput_unref(li);
+}
+END_TEST
+
+START_TEST(abs_mt_device_missing_res)
+{
+ struct libinput *li;
+ struct input_absinfo absinfo[] = {
+ { ABS_X, 0, 10, 0, 0, 10 },
+ { ABS_Y, 0, 10, 0, 0, 10 },
+ { ABS_MT_SLOT, 0, 2, 0, 0, 0 },
+ { ABS_MT_TRACKING_ID, 0, 255, 0, 0, 0 },
+ { ABS_MT_POSITION_X, 0, 10, 0, 0, 10 },
+ { ABS_MT_POSITION_Y, 0, 10, 0, 0, 0 },
+ { -1, -1, -1, -1, -1, -1 }
+ };
+
+ li = litest_create_context();
+ litest_disable_log_handler(li);
+ assert_device_ignored(li, absinfo);
+
+ absinfo[4].resolution = 0;
+ absinfo[5].resolution = 20;
+
+ assert_device_ignored(li, absinfo);
+
+ litest_restore_log_handler(li);
+ libinput_unref(li);
+
+}
+END_TEST
+
+START_TEST(device_wheel_only)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+
+ ck_assert(libinput_device_has_capability(device,
+ LIBINPUT_DEVICE_CAP_POINTER));
+}
+END_TEST
+
+void
+litest_setup_tests(void)
+{
+ struct range abs_range = { 0, ABS_MISC };
+ struct range abs_mt_range = { ABS_MT_SLOT + 1, ABS_CNT };
+
litest_add("device:sendevents", device_sendevents_config, LITEST_ANY, LITEST_TOUCHPAD|LITEST_TABLET);
litest_add("device:sendevents", device_sendevents_config_invalid, LITEST_ANY, LITEST_TABLET);
litest_add("device:sendevents", device_sendevents_config_touchpad, LITEST_TOUCHPAD, LITEST_TABLET);
@@ -906,7 +962,6 @@ int main (int argc, char **argv)
litest_add("device:sendevents", device_disable_events_pending, LITEST_RELATIVE, LITEST_TOUCHPAD|LITEST_TABLET);
litest_add("device:sendevents", device_double_disable, LITEST_ANY, LITEST_TABLET);
litest_add("device:sendevents", device_double_enable, LITEST_ANY, LITEST_TABLET);
-
litest_add_no_device("device:sendevents", device_reenable_syspath_changed);
litest_add_no_device("device:sendevents", device_reenable_device_removed);
litest_add_for_device("device:sendevents", device_disable_release_buttons, LITEST_MOUSE);
@@ -927,8 +982,10 @@ int main (int argc, char **argv)
litest_add_no_device("device:invalid devices", abs_device_no_absy);
litest_add_no_device("device:invalid devices", abs_mt_device_no_absx);
litest_add_no_device("device:invalid devices", abs_mt_device_no_absy);
- litest_add_no_device("device:invalid devices", abs_device_no_range);
- litest_add_no_device("device:invalid devices", abs_mt_device_no_range);
+ litest_add_ranged_no_device("device:invalid devices", abs_device_no_range, &abs_range);
+ litest_add_ranged_no_device("device:invalid devices", abs_mt_device_no_range, &abs_mt_range);
+ litest_add_no_device("device:invalid devices", abs_device_missing_res);
+ litest_add_no_device("device:invalid devices", abs_mt_device_missing_res);
- return litest_run(argc, argv);
+ litest_add("device:wheel", device_wheel_only, LITEST_WHEEL, LITEST_RELATIVE|LITEST_ABSOLUTE);
}
diff --git a/test/keyboard.c b/test/keyboard.c
index cb7ad522..1c8092b5 100644
--- a/test/keyboard.c
+++ b/test/keyboard.c
@@ -61,11 +61,9 @@ START_TEST(keyboard_seat_key_count)
continue;
}
- kev = libinput_event_get_keyboard_event(ev);
- ck_assert_notnull(kev);
- ck_assert_int_eq(libinput_event_keyboard_get_key(kev), KEY_A);
- ck_assert_int_eq(libinput_event_keyboard_get_key_state(kev),
- LIBINPUT_KEY_STATE_PRESSED);
+ kev = litest_is_keyboard_event(ev,
+ KEY_A,
+ LIBINPUT_KEY_STATE_PRESSED);
++expected_key_button_count;
seat_key_count =
@@ -175,31 +173,6 @@ START_TEST(keyboard_ignore_no_pressed_release)
}
END_TEST
-static void
-test_key_event(struct litest_device *dev, unsigned int key, int state)
-{
- struct libinput *li = dev->libinput;
- struct libinput_event *event;
- struct libinput_event_keyboard *kevent;
-
- litest_event(dev, EV_KEY, key, state);
- litest_event(dev, EV_SYN, SYN_REPORT, 0);
-
- libinput_dispatch(li);
-
- event = libinput_get_event(li);
- ck_assert(event != NULL);
- ck_assert_int_eq(libinput_event_get_type(event), LIBINPUT_EVENT_KEYBOARD_KEY);
-
- kevent = libinput_event_get_keyboard_event(event);
- ck_assert(kevent != NULL);
- ck_assert_int_eq(libinput_event_keyboard_get_key(kevent), key);
- ck_assert_int_eq(libinput_event_keyboard_get_key_state(kevent),
- state ? LIBINPUT_KEY_STATE_PRESSED :
- LIBINPUT_KEY_STATE_RELEASED);
- libinput_event_destroy(event);
-}
-
START_TEST(keyboard_key_auto_release)
{
struct libinput *libinput;
@@ -244,7 +217,17 @@ START_TEST(keyboard_key_auto_release)
/* Send pressed events, without releasing */
for (i = 0; i < ARRAY_LENGTH(keys); ++i) {
- test_key_event(dev, keys[i].code, 1);
+ key = keys[i].code;
+ litest_event(dev, EV_KEY, key, 1);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+
+ libinput_dispatch(libinput);
+
+ event = libinput_get_event(libinput);
+ kevent = litest_is_keyboard_event(event,
+ key,
+ LIBINPUT_KEY_STATE_PRESSED);
+ libinput_event_destroy(event);
}
litest_drain_events(libinput);
@@ -290,12 +273,49 @@ START_TEST(keyboard_key_auto_release)
}
END_TEST
-int
-main(int argc, char **argv)
+START_TEST(keyboard_has_key)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+ unsigned int code;
+ int evdev_has, libinput_has;
+
+ ck_assert(libinput_device_has_capability(
+ device,
+ LIBINPUT_DEVICE_CAP_KEYBOARD));
+
+ for (code = 0; code < KEY_CNT; code++) {
+ evdev_has = libevdev_has_event_code(dev->evdev, EV_KEY, code);
+ libinput_has = libinput_device_keyboard_has_key(device, code);
+ ck_assert_int_eq(evdev_has, libinput_has);
+ }
+}
+END_TEST
+
+START_TEST(keyboard_keys_bad_device)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+ unsigned int code;
+ int has_key;
+
+ if (libinput_device_has_capability(device,
+ LIBINPUT_DEVICE_CAP_KEYBOARD))
+ return;
+
+ for (code = 0; code < KEY_CNT; code++) {
+ has_key = libinput_device_keyboard_has_key(device, code);
+ ck_assert_int_eq(has_key, -1);
+ }
+}
+END_TEST
+
+void
+litest_setup_tests(void)
{
litest_add_no_device("keyboard:seat key count", keyboard_seat_key_count);
litest_add_no_device("keyboard:key counting", keyboard_ignore_no_pressed_release);
litest_add_no_device("keyboard:key counting", keyboard_key_auto_release);
-
- return litest_run(argc, argv);
+ litest_add("keyboard:keys", keyboard_has_key, LITEST_KEYS, LITEST_ANY);
+ litest_add("keyboard:keys", keyboard_keys_bad_device, LITEST_ANY, LITEST_ANY);
}
diff --git a/test/litest-atmel-hover.c b/test/litest-atmel-hover.c
new file mode 100644
index 00000000..7da0901b
--- /dev/null
+++ b/test/litest-atmel-hover.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright © 2015 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include
+
+#include "libinput-util.h"
+
+#include "litest.h"
+#include "litest-int.h"
+
+static void
+atmel_hover_create(struct litest_device *d);
+
+static void
+litest_atmel_hover_setup(void)
+{
+ struct litest_device *d = litest_create_device(LITEST_ATMEL_HOVER);
+ litest_set_current_device(d);
+}
+
+static struct input_event down[] = {
+ { .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_MT_DISTANCE, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+ { .type = -1, .code = -1 },
+};
+
+static struct input_event move[] = {
+ { .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_MT_DISTANCE, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+ { .type = -1, .code = -1 },
+};
+
+static struct input_event up[] = {
+ { .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = -1 },
+ { .type = EV_ABS, .code = ABS_MT_DISTANCE, .value = 1 },
+ { .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = 0 },
+ { .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+ { .type = -1, .code = -1 },
+};
+
+static struct litest_device_interface interface = {
+ .touch_down_events = down,
+ .touch_move_events = move,
+ .touch_up_events = up,
+};
+
+static struct input_id input_id = {
+ .bustype = 0x18,
+ .vendor = 0x0,
+ .product = 0x0,
+};
+
+static int events[] = {
+ EV_KEY, BTN_LEFT,
+ EV_KEY, BTN_TOOL_FINGER,
+ EV_KEY, BTN_TOUCH,
+ EV_KEY, BTN_TOOL_DOUBLETAP,
+ EV_KEY, BTN_TOOL_TRIPLETAP,
+ EV_KEY, BTN_TOOL_QUADTAP,
+ EV_KEY, BTN_TOOL_QUINTTAP,
+ INPUT_PROP_MAX, INPUT_PROP_POINTER,
+ INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
+ -1, -1,
+};
+
+static struct input_absinfo absinfo[] = {
+ { ABS_X, 0, 960, 0, 0, 10 },
+ { ABS_Y, 0, 540, 0, 0, 10 },
+ { ABS_PRESSURE, 0, 255, 0, 0, 0 },
+ { ABS_MT_SLOT, 0, 9, 0, 0, 0 },
+ { ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0, 0 },
+ { ABS_MT_ORIENTATION, 0, 255, 0, 0, 0 },
+ { ABS_MT_POSITION_X, 0, 960, 0, 0, 10 },
+ { ABS_MT_POSITION_Y, 0, 540, 0, 0, 10 },
+ { ABS_MT_TOOL_TYPE, 0, 2, 0, 0, 0 },
+ { ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
+ { ABS_MT_PRESSURE, 0, 255, 0, 0, 0 },
+ { ABS_MT_DISTANCE, 0, 1, 0, 0, 0 },
+ { .value = -1 }
+};
+
+struct litest_test_device litest_atmel_hover_device = {
+ .type = LITEST_ATMEL_HOVER,
+ .features = LITEST_TOUCHPAD | LITEST_BUTTON | LITEST_CLICKPAD | LITEST_HOVER,
+ .shortname = "atmel hover",
+ .setup = litest_atmel_hover_setup,
+ .interface = &interface,
+ .create = atmel_hover_create,
+
+ .name = "Atmel maXTouch Touchpad",
+ .id = &input_id,
+ .events = events,
+ .absinfo = absinfo,
+};
+
+static void
+atmel_hover_create(struct litest_device *d)
+{
+ struct litest_semi_mt *semi_mt = zalloc(sizeof(*semi_mt));
+ assert(semi_mt);
+
+ d->private = semi_mt;
+
+ d->uinput = litest_create_uinput_device_from_description(
+ litest_atmel_hover_device.name,
+ litest_atmel_hover_device.id,
+ absinfo,
+ events);
+ d->interface = &interface;
+}
diff --git a/test/litest-bcm5974.c b/test/litest-bcm5974.c
index 035bed22..ddff0387 100644
--- a/test/litest-bcm5974.c
+++ b/test/litest-bcm5974.c
@@ -33,7 +33,7 @@ static void litest_bcm5974_setup(void)
litest_set_current_device(d);
}
-struct input_event down[] = {
+static struct input_event down[] = {
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 30 },
diff --git a/test/litest-keyboard-razer-blackwidow.c b/test/litest-keyboard-razer-blackwidow.c
new file mode 100644
index 00000000..b1e95b3a
--- /dev/null
+++ b/test/litest-keyboard-razer-blackwidow.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright © 2015 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "litest.h"
+#include "litest-int.h"
+
+/* Recording from https://bugs.freedesktop.org/show_bug.cgi?id=89783
+ * This is the second of 4 devices exported by this keyboard, the first is
+ * just a basic keyboard that is identical to the normal litest-keyboard.c
+ * file.
+ */
+
+static void litest_blackwidow_setup(void)
+{
+ struct litest_device *d = litest_create_device(LITEST_KEYBOARD_BLACKWIDOW);
+ litest_set_current_device(d);
+}
+
+static struct input_id input_id = {
+ .bustype = 0x3,
+ .vendor = 0x1532,
+ .product = 0x11b,
+};
+
+static int events[] = {
+ EV_REL, REL_HWHEEL,
+ EV_KEY, KEY_ESC,
+ EV_KEY, KEY_1,
+ EV_KEY, KEY_2,
+ EV_KEY, KEY_3,
+ EV_KEY, KEY_4,
+ EV_KEY, KEY_5,
+ EV_KEY, KEY_6,
+ EV_KEY, KEY_7,
+ EV_KEY, KEY_8,
+ EV_KEY, KEY_9,
+ EV_KEY, KEY_0,
+ EV_KEY, KEY_MINUS,
+ EV_KEY, KEY_EQUAL,
+ EV_KEY, KEY_BACKSPACE,
+ EV_KEY, KEY_TAB,
+ EV_KEY, KEY_Q,
+ EV_KEY, KEY_W,
+ EV_KEY, KEY_E,
+ EV_KEY, KEY_R,
+ EV_KEY, KEY_T,
+ EV_KEY, KEY_Y,
+ EV_KEY, KEY_U,
+ EV_KEY, KEY_I,
+ EV_KEY, KEY_O,
+ EV_KEY, KEY_P,
+ EV_KEY, KEY_LEFTBRACE,
+ EV_KEY, KEY_RIGHTBRACE,
+ EV_KEY, KEY_ENTER,
+ EV_KEY, KEY_LEFTCTRL,
+ EV_KEY, KEY_A,
+ EV_KEY, KEY_S,
+ EV_KEY, KEY_D,
+ EV_KEY, KEY_F,
+ EV_KEY, KEY_G,
+ EV_KEY, KEY_H,
+ EV_KEY, KEY_J,
+ EV_KEY, KEY_K,
+ EV_KEY, KEY_L,
+ EV_KEY, KEY_SEMICOLON,
+ EV_KEY, KEY_APOSTROPHE,
+ EV_KEY, KEY_GRAVE,
+ EV_KEY, KEY_LEFTSHIFT,
+ EV_KEY, KEY_BACKSLASH,
+ EV_KEY, KEY_Z,
+ EV_KEY, KEY_X,
+ EV_KEY, KEY_C,
+ EV_KEY, KEY_V,
+ EV_KEY, KEY_B,
+ EV_KEY, KEY_N,
+ EV_KEY, KEY_M,
+ EV_KEY, KEY_COMMA,
+ EV_KEY, KEY_DOT,
+ EV_KEY, KEY_SLASH,
+ EV_KEY, KEY_RIGHTSHIFT,
+ EV_KEY, KEY_KPASTERISK,
+ EV_KEY, KEY_LEFTALT,
+ EV_KEY, KEY_SPACE,
+ EV_KEY, KEY_CAPSLOCK,
+ EV_KEY, KEY_F1,
+ EV_KEY, KEY_F2,
+ EV_KEY, KEY_F3,
+ EV_KEY, KEY_F4,
+ EV_KEY, KEY_F5,
+ EV_KEY, KEY_F6,
+ EV_KEY, KEY_F7,
+ EV_KEY, KEY_F8,
+ EV_KEY, KEY_F9,
+ EV_KEY, KEY_F10,
+ EV_KEY, KEY_NUMLOCK,
+ EV_KEY, KEY_SCROLLLOCK,
+ EV_KEY, KEY_KP7,
+ EV_KEY, KEY_KP8,
+ EV_KEY, KEY_KP9,
+ EV_KEY, KEY_KPMINUS,
+ EV_KEY, KEY_KP4,
+ EV_KEY, KEY_KP5,
+ EV_KEY, KEY_KP6,
+ EV_KEY, KEY_KPPLUS,
+ EV_KEY, KEY_KP1,
+ EV_KEY, KEY_KP2,
+ EV_KEY, KEY_KP3,
+ EV_KEY, KEY_KP0,
+ EV_KEY, KEY_KPDOT,
+ EV_KEY, KEY_ZENKAKUHANKAKU,
+ EV_KEY, KEY_102ND,
+ EV_KEY, KEY_F11,
+ EV_KEY, KEY_F12,
+ EV_KEY, KEY_RO,
+ EV_KEY, KEY_KATAKANA,
+ EV_KEY, KEY_HIRAGANA,
+ EV_KEY, KEY_HENKAN,
+ EV_KEY, KEY_KATAKANAHIRAGANA,
+ EV_KEY, KEY_MUHENKAN,
+ EV_KEY, KEY_KPJPCOMMA,
+ EV_KEY, KEY_KPENTER,
+ EV_KEY, KEY_RIGHTCTRL,
+ EV_KEY, KEY_KPSLASH,
+ EV_KEY, KEY_SYSRQ,
+ EV_KEY, KEY_RIGHTALT,
+ EV_KEY, KEY_HOME,
+ EV_KEY, KEY_UP,
+ EV_KEY, KEY_PAGEUP,
+ EV_KEY, KEY_LEFT,
+ EV_KEY, KEY_RIGHT,
+ EV_KEY, KEY_END,
+ EV_KEY, KEY_DOWN,
+ EV_KEY, KEY_PAGEDOWN,
+ EV_KEY, KEY_INSERT,
+ EV_KEY, KEY_DELETE,
+ EV_KEY, KEY_MUTE,
+ EV_KEY, KEY_VOLUMEDOWN,
+ EV_KEY, KEY_VOLUMEUP,
+ EV_KEY, KEY_POWER,
+ EV_KEY, KEY_KPEQUAL,
+ EV_KEY, KEY_PAUSE,
+ EV_KEY, KEY_KPCOMMA,
+ EV_KEY, KEY_HANGEUL,
+ EV_KEY, KEY_HANJA,
+ EV_KEY, KEY_YEN,
+ EV_KEY, KEY_LEFTMETA,
+ EV_KEY, KEY_RIGHTMETA,
+ EV_KEY, KEY_COMPOSE,
+ EV_KEY, KEY_STOP,
+ EV_KEY, KEY_AGAIN,
+ EV_KEY, KEY_PROPS,
+ EV_KEY, KEY_UNDO,
+ EV_KEY, KEY_FRONT,
+ EV_KEY, KEY_COPY,
+ EV_KEY, KEY_OPEN,
+ EV_KEY, KEY_PASTE,
+ EV_KEY, KEY_FIND,
+ EV_KEY, KEY_CUT,
+ EV_KEY, KEY_HELP,
+ EV_KEY, KEY_MENU,
+ EV_KEY, KEY_CALC,
+ EV_KEY, KEY_SLEEP,
+ EV_KEY, KEY_WAKEUP,
+ EV_KEY, KEY_FILE,
+ EV_KEY, KEY_WWW,
+ EV_KEY, KEY_COFFEE,
+ EV_KEY, KEY_MAIL,
+ EV_KEY, KEY_BOOKMARKS,
+ EV_KEY, KEY_BACK,
+ EV_KEY, KEY_FORWARD,
+ EV_KEY, KEY_EJECTCD,
+ EV_KEY, KEY_NEXTSONG,
+ EV_KEY, KEY_PLAYPAUSE,
+ EV_KEY, KEY_PREVIOUSSONG,
+ EV_KEY, KEY_STOPCD,
+ EV_KEY, KEY_RECORD,
+ EV_KEY, KEY_REWIND,
+ EV_KEY, KEY_PHONE,
+ EV_KEY, KEY_CONFIG,
+ EV_KEY, KEY_HOMEPAGE,
+ EV_KEY, KEY_REFRESH,
+ EV_KEY, KEY_EXIT,
+ EV_KEY, KEY_EDIT,
+ EV_KEY, KEY_SCROLLUP,
+ EV_KEY, KEY_SCROLLDOWN,
+ EV_KEY, KEY_KPLEFTPAREN,
+ EV_KEY, KEY_KPRIGHTPAREN,
+ EV_KEY, KEY_NEW,
+ EV_KEY, KEY_F13,
+ EV_KEY, KEY_F14,
+ EV_KEY, KEY_F15,
+ EV_KEY, KEY_F16,
+ EV_KEY, KEY_F17,
+ EV_KEY, KEY_F18,
+ EV_KEY, KEY_F19,
+ EV_KEY, KEY_F20,
+ EV_KEY, KEY_F21,
+ EV_KEY, KEY_F22,
+ EV_KEY, KEY_F23,
+ EV_KEY, KEY_F24,
+ EV_KEY, KEY_CLOSE,
+ EV_KEY, KEY_PLAY,
+ EV_KEY, KEY_FASTFORWARD,
+ EV_KEY, KEY_BASSBOOST,
+ EV_KEY, KEY_PRINT,
+ EV_KEY, KEY_CAMERA,
+ EV_KEY, KEY_CHAT,
+ EV_KEY, KEY_SEARCH,
+ EV_KEY, KEY_FINANCE,
+ EV_KEY, KEY_BRIGHTNESSDOWN,
+ EV_KEY, KEY_BRIGHTNESSUP,
+ EV_KEY, KEY_KBDILLUMTOGGLE,
+ EV_KEY, KEY_SAVE,
+ EV_KEY, KEY_DOCUMENTS,
+ EV_KEY, KEY_UNKNOWN,
+ EV_KEY, KEY_VIDEO_NEXT,
+ EV_KEY, KEY_BRIGHTNESS_AUTO,
+ EV_KEY, BTN_0,
+ EV_KEY, KEY_SELECT,
+ EV_KEY, KEY_GOTO,
+ EV_KEY, KEY_INFO,
+ EV_KEY, KEY_PROGRAM,
+ EV_KEY, KEY_PVR,
+ EV_KEY, KEY_SUBTITLE,
+ EV_KEY, KEY_ZOOM,
+ EV_KEY, KEY_KEYBOARD,
+ EV_KEY, KEY_PC,
+ EV_KEY, KEY_TV,
+ EV_KEY, KEY_TV2,
+ EV_KEY, KEY_VCR,
+ EV_KEY, KEY_VCR2,
+ EV_KEY, KEY_SAT,
+ EV_KEY, KEY_CD,
+ EV_KEY, KEY_TAPE,
+ EV_KEY, KEY_TUNER,
+ EV_KEY, KEY_PLAYER,
+ EV_KEY, KEY_DVD,
+ EV_KEY, KEY_AUDIO,
+ EV_KEY, KEY_VIDEO,
+ EV_KEY, KEY_MEMO,
+ EV_KEY, KEY_CALENDAR,
+ EV_KEY, KEY_RED,
+ EV_KEY, KEY_GREEN,
+ EV_KEY, KEY_YELLOW,
+ EV_KEY, KEY_BLUE,
+ EV_KEY, KEY_CHANNELUP,
+ EV_KEY, KEY_CHANNELDOWN,
+ EV_KEY, KEY_LAST,
+ EV_KEY, KEY_NEXT,
+ EV_KEY, KEY_RESTART,
+ EV_KEY, KEY_SLOW,
+ EV_KEY, KEY_SHUFFLE,
+ EV_KEY, KEY_PREVIOUS,
+ EV_KEY, KEY_VIDEOPHONE,
+ EV_KEY, KEY_GAMES,
+ EV_KEY, KEY_ZOOMIN,
+ EV_KEY, KEY_ZOOMOUT,
+ EV_KEY, KEY_ZOOMRESET,
+ EV_KEY, KEY_WORDPROCESSOR,
+ EV_KEY, KEY_EDITOR,
+ EV_KEY, KEY_SPREADSHEET,
+ EV_KEY, KEY_GRAPHICSEDITOR,
+ EV_KEY, KEY_PRESENTATION,
+ EV_KEY, KEY_DATABASE,
+ EV_KEY, KEY_NEWS,
+ EV_KEY, KEY_VOICEMAIL,
+ EV_KEY, KEY_ADDRESSBOOK,
+ EV_KEY, KEY_MESSENGER,
+ EV_KEY, KEY_DISPLAYTOGGLE,
+ EV_KEY, KEY_SPELLCHECK,
+ EV_KEY, KEY_LOGOFF,
+ EV_KEY, KEY_MEDIA_REPEAT,
+ EV_KEY, KEY_IMAGES,
+ EV_KEY, KEY_BUTTONCONFIG,
+ EV_KEY, KEY_TASKMANAGER,
+ EV_KEY, KEY_JOURNAL,
+ EV_KEY, KEY_CONTROLPANEL,
+ EV_KEY, KEY_APPSELECT,
+ EV_KEY, KEY_SCREENSAVER,
+ EV_KEY, KEY_VOICECOMMAND,
+ EV_KEY, KEY_BRIGHTNESS_MIN,
+ EV_KEY, KEY_BRIGHTNESS_MAX,
+ EV_MSC, MSC_SCAN,
+ -1 , -1,
+};
+
+static struct input_absinfo absinfo[] = {
+ { ABS_VOLUME, 0, 572, 0, 0, 0 },
+ { ABS_MISC, 0, 255, 0, 0, 0 },
+ { 0x29, 0, 255, 0, 0, 0 },
+ { 0x2a, 0, 255, 0, 0, 0 },
+ { 0x2b, 0, 255, 0, 0, 0 },
+ { 0x2c, 0, 255, 0, 0, 0 },
+ { 0x2d, 0, 255, 0, 0, 0 },
+ { 0x2e, 0, 255, 0, 0, 0 },
+ { 0x2f, 0, 255, 0, 0, 0 },
+ { 0x30, 0, 255, 0, 0, 0 },
+ { 0x31, 0, 255, 0, 0, 0 },
+ { 0x32, 0, 255, 0, 0, 0 },
+ { 0x33, 0, 255, 0, 0, 0 },
+ { 0x34, 0, 255, 0, 0, 0 },
+ { 0x35, 0, 255, 0, 0, 0 },
+ { 0x36, 0, 255, 0, 0, 0 },
+ { 0x37, 0, 255, 0, 0, 0 },
+ { 0x38, 0, 255, 0, 0, 0 },
+ { 0x39, 0, 255, 0, 0, 0 },
+ { 0x3a, 0, 255, 0, 0, 0 },
+ { 0x3b, 0, 255, 0, 0, 0 },
+ { 0x3c, 0, 255, 0, 0, 0 },
+ { 0x3d, 0, 255, 0, 0, 0 },
+ { 0x3e, 0, 255, 0, 0, 0 },
+ { 0x3f, 0, 255, 0, 0, 0 },
+ { .value = -1 },
+};
+
+struct litest_test_device litest_keyboard_blackwidow_device = {
+ .type = LITEST_KEYBOARD_BLACKWIDOW,
+ .features = LITEST_KEYS | LITEST_WHEEL,
+ .shortname = "blackwidow",
+ .setup = litest_blackwidow_setup,
+ .interface = NULL,
+
+ .name = "Razer Razer BlackWidow 2013",
+ .id = &input_id,
+ .absinfo = absinfo,
+ .events = events,
+};
diff --git a/test/litest-logitech-trackball.c b/test/litest-logitech-trackball.c
new file mode 100644
index 00000000..5e862d86
--- /dev/null
+++ b/test/litest-logitech-trackball.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2015 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "litest.h"
+#include "litest-int.h"
+
+static void litest_logitech_trackball_setup(void)
+{
+ struct litest_device *d = litest_create_device(LITEST_LOGITECH_TRACKBALL);
+ litest_set_current_device(d);
+}
+
+static struct input_id input_id = {
+ .bustype = 0x3,
+ .vendor = 0x46d,
+ .product = 0xc408,
+};
+
+static int events[] = {
+ EV_KEY, BTN_LEFT,
+ EV_KEY, BTN_RIGHT,
+ EV_KEY, BTN_MIDDLE,
+ EV_KEY, BTN_SIDE,
+ EV_KEY, BTN_EXTRA,
+ EV_REL, REL_X,
+ EV_REL, REL_Y,
+ -1 , -1,
+};
+
+struct litest_test_device litest_logitech_trackball_device = {
+ .type = LITEST_LOGITECH_TRACKBALL,
+ .features = LITEST_RELATIVE | LITEST_BUTTON,
+ .shortname = "logitech trackball",
+ .setup = litest_logitech_trackball_setup,
+ .interface = NULL,
+
+ .name = "Logitech USB Trackball",
+ .id = &input_id,
+ .absinfo = NULL,
+ .events = events,
+};
diff --git a/test/litest-mouse-roccat.c b/test/litest-mouse-roccat.c
new file mode 100644
index 00000000..115e970d
--- /dev/null
+++ b/test/litest-mouse-roccat.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright © 2015 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "litest.h"
+#include "litest-int.h"
+
+static void litest_mouse_roccat_setup(void)
+{
+ struct litest_device *d = litest_create_device(LITEST_MOUSE_ROCCAT);
+ litest_set_current_device(d);
+}
+
+static struct input_id input_id = {
+ .bustype = 0x3,
+ .vendor = 0x1e7d,
+ .product = 0x2e22,
+};
+
+static int events[] = {
+ EV_REL, REL_X,
+ EV_REL, REL_Y,
+ EV_REL, REL_WHEEL,
+ EV_REL, REL_HWHEEL,
+ EV_REL, REL_DIAL,
+ EV_KEY, KEY_ESC,
+ EV_KEY, KEY_ENTER,
+ EV_KEY, KEY_KPMINUS,
+ EV_KEY, KEY_KPPLUS,
+ EV_KEY, KEY_UP,
+ EV_KEY, KEY_LEFT,
+ EV_KEY, KEY_RIGHT,
+ EV_KEY, KEY_DOWN,
+ EV_KEY, KEY_MUTE,
+ EV_KEY, KEY_VOLUMEDOWN,
+ EV_KEY, KEY_VOLUMEUP,
+ EV_KEY, KEY_POWER,
+ EV_KEY, KEY_PAUSE,
+ EV_KEY, KEY_STOP,
+ EV_KEY, KEY_PROPS,
+ EV_KEY, KEY_UNDO,
+ EV_KEY, KEY_COPY,
+ EV_KEY, KEY_OPEN,
+ EV_KEY, KEY_PASTE,
+ EV_KEY, KEY_FIND,
+ EV_KEY, KEY_CUT,
+ EV_KEY, KEY_HELP,
+ EV_KEY, KEY_MENU,
+ EV_KEY, KEY_CALC,
+ EV_KEY, KEY_SLEEP,
+ EV_KEY, KEY_FILE,
+ EV_KEY, KEY_WWW,
+ EV_KEY, KEY_COFFEE,
+ EV_KEY, KEY_MAIL,
+ EV_KEY, KEY_BOOKMARKS,
+ EV_KEY, KEY_BACK,
+ EV_KEY, KEY_FORWARD,
+ EV_KEY, KEY_EJECTCD,
+ EV_KEY, KEY_NEXTSONG,
+ EV_KEY, KEY_PLAYPAUSE,
+ EV_KEY, KEY_PREVIOUSSONG,
+ EV_KEY, KEY_STOPCD,
+ EV_KEY, KEY_RECORD,
+ EV_KEY, KEY_REWIND,
+ EV_KEY, KEY_PHONE,
+ EV_KEY, KEY_CONFIG,
+ EV_KEY, KEY_HOMEPAGE,
+ EV_KEY, KEY_REFRESH,
+ EV_KEY, KEY_EXIT,
+ EV_KEY, KEY_SCROLLUP,
+ EV_KEY, KEY_SCROLLDOWN,
+ EV_KEY, KEY_NEW,
+ EV_KEY, KEY_CLOSE,
+ EV_KEY, KEY_PLAY,
+ EV_KEY, KEY_FASTFORWARD,
+ EV_KEY, KEY_BASSBOOST,
+ EV_KEY, KEY_PRINT,
+ EV_KEY, KEY_CAMERA,
+ EV_KEY, KEY_CHAT,
+ EV_KEY, KEY_SEARCH,
+ EV_KEY, KEY_FINANCE,
+ EV_KEY, KEY_BRIGHTNESSDOWN,
+ EV_KEY, KEY_BRIGHTNESSUP,
+ EV_KEY, KEY_KBDILLUMTOGGLE,
+ EV_KEY, KEY_SAVE,
+ EV_KEY, KEY_DOCUMENTS,
+ EV_KEY, KEY_UNKNOWN,
+ EV_KEY, KEY_VIDEO_NEXT,
+ EV_KEY, KEY_BRIGHTNESS_AUTO,
+ EV_KEY, BTN_0,
+ EV_KEY, BTN_LEFT,
+ EV_KEY, BTN_RIGHT,
+ EV_KEY, BTN_MIDDLE,
+ EV_KEY, BTN_SIDE,
+ EV_KEY, BTN_EXTRA,
+ EV_KEY, KEY_SELECT,
+ EV_KEY, KEY_GOTO,
+ EV_KEY, KEY_INFO,
+ EV_KEY, KEY_PROGRAM,
+ EV_KEY, KEY_PVR,
+ EV_KEY, KEY_SUBTITLE,
+ EV_KEY, KEY_ZOOM,
+ EV_KEY, KEY_KEYBOARD,
+ EV_KEY, KEY_PC,
+ EV_KEY, KEY_TV,
+ EV_KEY, KEY_TV2,
+ EV_KEY, KEY_VCR,
+ EV_KEY, KEY_VCR2,
+ EV_KEY, KEY_SAT,
+ EV_KEY, KEY_CD,
+ EV_KEY, KEY_TAPE,
+ EV_KEY, KEY_TUNER,
+ EV_KEY, KEY_PLAYER,
+ EV_KEY, KEY_DVD,
+ EV_KEY, KEY_AUDIO,
+ EV_KEY, KEY_VIDEO,
+ EV_KEY, KEY_MEMO,
+ EV_KEY, KEY_CALENDAR,
+ EV_KEY, KEY_RED,
+ EV_KEY, KEY_GREEN,
+ EV_KEY, KEY_YELLOW,
+ EV_KEY, KEY_BLUE,
+ EV_KEY, KEY_CHANNELUP,
+ EV_KEY, KEY_CHANNELDOWN,
+ EV_KEY, KEY_LAST,
+ EV_KEY, KEY_NEXT,
+ EV_KEY, KEY_RESTART,
+ EV_KEY, KEY_SLOW,
+ EV_KEY, KEY_SHUFFLE,
+ EV_KEY, KEY_PREVIOUS,
+ EV_KEY, KEY_VIDEOPHONE,
+ EV_KEY, KEY_GAMES,
+ EV_KEY, KEY_ZOOMIN,
+ EV_KEY, KEY_ZOOMOUT,
+ EV_KEY, KEY_ZOOMRESET,
+ EV_KEY, KEY_WORDPROCESSOR,
+ EV_KEY, KEY_EDITOR,
+ EV_KEY, KEY_SPREADSHEET,
+ EV_KEY, KEY_GRAPHICSEDITOR,
+ EV_KEY, KEY_PRESENTATION,
+ EV_KEY, KEY_DATABASE,
+ EV_KEY, KEY_NEWS,
+ EV_KEY, KEY_VOICEMAIL,
+ EV_KEY, KEY_ADDRESSBOOK,
+ EV_KEY, KEY_MESSENGER,
+ EV_KEY, KEY_DISPLAYTOGGLE,
+ EV_KEY, KEY_SPELLCHECK,
+ EV_KEY, KEY_LOGOFF,
+ EV_KEY, KEY_MEDIA_REPEAT,
+ EV_KEY, KEY_IMAGES,
+ EV_KEY, KEY_BUTTONCONFIG,
+ EV_KEY, KEY_TASKMANAGER,
+ EV_KEY, KEY_JOURNAL,
+ EV_KEY, KEY_CONTROLPANEL,
+ EV_KEY, KEY_APPSELECT,
+ EV_KEY, KEY_SCREENSAVER,
+ EV_KEY, KEY_VOICECOMMAND,
+ EV_KEY, KEY_BRIGHTNESS_MIN,
+ EV_KEY, KEY_BRIGHTNESS_MAX,
+ -1 , -1,
+};
+
+static struct input_absinfo absinfo[] = {
+ { ABS_VOLUME, 0, 572, 0, 0, 0 },
+ { ABS_MISC, 0, 0, 0, 0, 0 },
+ { ABS_MISC + 1, 0, 0, 0, 0, 0 },
+ { ABS_MISC + 2, 0, 0, 0, 0, 0 },
+ { ABS_MISC + 3, 0, 0, 0, 0, 0 },
+ { .value = -1 }
+};
+
+struct litest_test_device litest_mouse_roccat_device = {
+ .type = LITEST_MOUSE_ROCCAT,
+ .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL | LITEST_KEYS,
+ .shortname = "mouse_roccat",
+ .setup = litest_mouse_roccat_setup,
+ .interface = NULL,
+
+ .name = "ROCCAT ROCCAT Kone XTD",
+ .id = &input_id,
+ .absinfo = absinfo,
+ .events = events,
+};
diff --git a/test/litest-ms-surface-cover.c b/test/litest-ms-surface-cover.c
index 5b9ec233..37fe850c 100644
--- a/test/litest-ms-surface-cover.c
+++ b/test/litest-ms-surface-cover.c
@@ -35,6 +35,8 @@ litest_ms_surface_cover_setup(void)
}
static struct input_event down[] = {
+ { .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
@@ -59,6 +61,8 @@ static struct litest_device_interface interface = {
};
static struct input_absinfo absinfo[] = {
+ { ABS_X, 0, 1022, 0, 0, 12 },
+ { ABS_Y, 0, 487, 0, 0, 12 },
{ ABS_VOLUME, 0, 1023, 0, 0, 0 },
{ ABS_MISC, 0, 255, 0, 0, 0 },
{ 41, 0, 255, 0, 0, 0 },
@@ -90,7 +94,7 @@ static struct input_absinfo absinfo[] = {
static struct input_id input_id = {
.bustype = 0x3,
.vendor = 0x45e,
- .product = 0x7a9,
+ .product = 0x7dc,
};
static int events[] = {
@@ -368,18 +372,17 @@ static int events[] = {
EV_LED, LED_NUML,
EV_LED, LED_CAPSL,
EV_LED, LED_SCROLLL,
- EV_REP, REP_DELAY,
-1, -1,
};
struct litest_test_device litest_ms_surface_cover_device = {
.type = LITEST_MS_SURFACE_COVER,
- .features = LITEST_KEYBOARD | LITEST_RELATIVE | LITEST_FAKE_MT,
+ .features = LITEST_KEYS | LITEST_ABSOLUTE | LITEST_RELATIVE | LITEST_FAKE_MT | LITEST_BUTTON | LITEST_WHEEL,
.shortname = "MS surface cover",
.setup = litest_ms_surface_cover_setup,
.interface = &interface,
- .name = "MICROSOFT SAM",
+ .name = "Microsoft Surface Type Cover",
.id = &input_id,
.events = events,
.absinfo = absinfo,
diff --git a/test/litest-selftest.c b/test/litest-selftest.c
new file mode 100644
index 00000000..f7974770
--- /dev/null
+++ b/test/litest-selftest.c
@@ -0,0 +1,369 @@
+#include
+
+#include
+#include
+
+#include "litest.h"
+
+START_TEST(litest_assert_trigger)
+{
+ litest_assert(1 == 2);
+}
+END_TEST
+
+START_TEST(litest_assert_notrigger)
+{
+ litest_assert(1 == 1);
+}
+END_TEST
+
+START_TEST(litest_assert_msg_trigger)
+{
+ litest_assert_msg(1 == 2, "1 is not 2\n");
+}
+END_TEST
+
+START_TEST(litest_assert_msg_NULL_trigger)
+{
+ litest_assert_msg(1 == 2, NULL);
+}
+END_TEST
+
+START_TEST(litest_assert_msg_notrigger)
+{
+ litest_assert_msg(1 == 1, "1 is not 2\n");
+ litest_assert_msg(1 == 1, NULL);
+}
+END_TEST
+
+START_TEST(litest_abort_msg_trigger)
+{
+ litest_abort_msg("message\n");
+}
+END_TEST
+
+START_TEST(litest_abort_msg_NULL_trigger)
+{
+ litest_abort_msg(NULL);
+}
+END_TEST
+
+START_TEST(litest_int_eq_trigger)
+{
+ int a = 10;
+ int b = 20;
+ litest_assert_int_eq(a, b);
+}
+END_TEST
+
+START_TEST(litest_int_eq_notrigger)
+{
+ int a = 10;
+ int b = 10;
+ litest_assert_int_eq(a, b);
+}
+END_TEST
+
+START_TEST(litest_int_ne_trigger)
+{
+ int a = 10;
+ int b = 10;
+ litest_assert_int_ne(a, b);
+}
+END_TEST
+
+START_TEST(litest_int_ne_notrigger)
+{
+ int a = 10;
+ int b = 20;
+ litest_assert_int_ne(a, b);
+}
+END_TEST
+
+START_TEST(litest_int_lt_trigger_eq)
+{
+ int a = 10;
+ int b = 10;
+ litest_assert_int_lt(a, b);
+}
+END_TEST
+
+START_TEST(litest_int_lt_trigger_gt)
+{
+ int a = 11;
+ int b = 10;
+ litest_assert_int_lt(a, b);
+}
+END_TEST
+
+START_TEST(litest_int_lt_notrigger)
+{
+ int a = 10;
+ int b = 11;
+ litest_assert_int_lt(a, b);
+}
+END_TEST
+
+START_TEST(litest_int_le_trigger)
+{
+ int a = 11;
+ int b = 10;
+ litest_assert_int_le(a, b);
+}
+END_TEST
+
+START_TEST(litest_int_le_notrigger)
+{
+ int a = 10;
+ int b = 11;
+ int c = 10;
+ litest_assert_int_le(a, b);
+ litest_assert_int_le(a, c);
+}
+END_TEST
+
+START_TEST(litest_int_gt_trigger_eq)
+{
+ int a = 10;
+ int b = 10;
+ litest_assert_int_gt(a, b);
+}
+END_TEST
+
+START_TEST(litest_int_gt_trigger_lt)
+{
+ int a = 9;
+ int b = 10;
+ litest_assert_int_gt(a, b);
+}
+END_TEST
+
+START_TEST(litest_int_gt_notrigger)
+{
+ int a = 10;
+ int b = 9;
+ litest_assert_int_gt(a, b);
+}
+END_TEST
+
+START_TEST(litest_int_ge_trigger)
+{
+ int a = 9;
+ int b = 10;
+ litest_assert_int_ge(a, b);
+}
+END_TEST
+
+START_TEST(litest_int_ge_notrigger)
+{
+ int a = 10;
+ int b = 9;
+ int c = 10;
+ litest_assert_int_ge(a, b);
+ litest_assert_int_ge(a, c);
+}
+END_TEST
+
+START_TEST(litest_ptr_eq_notrigger)
+{
+ int v = 10;
+ int *a = &v;
+ int *b = &v;
+ int c = NULL;
+ int d = NULL;
+
+ litest_assert_ptr_eq(a, b);
+ litest_assert_ptr_eq(c, d);
+}
+END_TEST
+
+START_TEST(litest_ptr_eq_trigger)
+{
+ int v = 10;
+ int v2 = 11;
+ int *a = &v;
+ int *b = &v2;
+
+ litest_assert_ptr_eq(a, b);
+}
+END_TEST
+
+START_TEST(litest_ptr_eq_trigger_NULL)
+{
+ int v = 10;
+ int *a = &v;
+ int *b = NULL;
+
+ litest_assert_ptr_eq(a, b);
+}
+END_TEST
+
+START_TEST(litest_ptr_eq_trigger_NULL2)
+{
+ int v = 10;
+ int *a = &v;
+ int *b = NULL;
+
+ litest_assert_ptr_eq(b, a);
+}
+END_TEST
+
+START_TEST(litest_ptr_ne_trigger)
+{
+ int v = 10;
+ int *a = &v;
+ int *b = &v;
+
+ litest_assert_ptr_ne(a, b);
+}
+END_TEST
+
+START_TEST(litest_ptr_ne_trigger_NULL)
+{
+ int *a = NULL;
+
+ litest_assert_ptr_ne(a, NULL);
+}
+END_TEST
+
+START_TEST(litest_ptr_ne_trigger_NULL2)
+{
+ int *a = NULL;
+
+ litest_assert_ptr_ne(NULL, a);
+}
+END_TEST
+
+START_TEST(litest_ptr_ne_notrigger)
+{
+ int v1 = 10;
+ int v2 = 10;
+ int *a = &v1;
+ int *b = &v2;
+ int *c = NULL;
+
+ litest_assert_ptr_ne(a, b);
+ litest_assert_ptr_ne(a, c);
+ litest_assert_ptr_ne(c, b);
+}
+END_TEST
+
+START_TEST(litest_ptr_null_notrigger)
+{
+ int *a = NULL;
+
+ litest_assert_ptr_null(a);
+ litest_assert_ptr_null(NULL);
+}
+END_TEST
+
+START_TEST(litest_ptr_null_trigger)
+{
+ int v;
+ int *a = &v;
+
+ litest_assert_ptr_null(a);
+}
+END_TEST
+
+START_TEST(litest_ptr_notnull_notrigger)
+{
+ int v;
+ int *a = &v;
+
+ litest_assert_ptr_notnull(a);
+}
+END_TEST
+
+START_TEST(litest_ptr_notnull_trigger)
+{
+ int *a = NULL;
+
+ litest_assert_ptr_notnull(a);
+}
+END_TEST
+
+START_TEST(litest_ptr_notnull_trigger_NULL)
+{
+ litest_assert_ptr_notnull(NULL);
+}
+END_TEST
+
+static Suite *
+litest_assert_macros_suite(void)
+{
+ TCase *tc;
+ Suite *s;
+
+ s = suite_create("litest:assert macros");
+ tc = tcase_create("assert");
+ tcase_add_test_raise_signal(tc, litest_assert_trigger, SIGABRT);
+ tcase_add_test(tc, litest_assert_notrigger);
+ tcase_add_test_raise_signal(tc, litest_assert_msg_trigger, SIGABRT);
+ tcase_add_test_raise_signal(tc, litest_assert_msg_NULL_trigger, SIGABRT);
+ tcase_add_test(tc, litest_assert_msg_notrigger);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("abort");
+ tcase_add_test_raise_signal(tc, litest_abort_msg_trigger, SIGABRT);
+ tcase_add_test_raise_signal(tc, litest_abort_msg_NULL_trigger, SIGABRT);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("int comparison ");
+ tcase_add_test_raise_signal(tc, litest_int_eq_trigger, SIGABRT);
+ tcase_add_test(tc, litest_int_eq_notrigger);
+ tcase_add_test_raise_signal(tc, litest_int_ne_trigger, SIGABRT);
+ tcase_add_test(tc, litest_int_ne_notrigger);
+ tcase_add_test_raise_signal(tc, litest_int_le_trigger, SIGABRT);
+ tcase_add_test(tc, litest_int_le_notrigger);
+ tcase_add_test_raise_signal(tc, litest_int_lt_trigger_gt, SIGABRT);
+ tcase_add_test_raise_signal(tc, litest_int_lt_trigger_eq, SIGABRT);
+ tcase_add_test(tc, litest_int_lt_notrigger);
+ tcase_add_test_raise_signal(tc, litest_int_ge_trigger, SIGABRT);
+ tcase_add_test(tc, litest_int_ge_notrigger);
+ tcase_add_test_raise_signal(tc, litest_int_gt_trigger_eq, SIGABRT);
+ tcase_add_test_raise_signal(tc, litest_int_gt_trigger_lt, SIGABRT);
+ tcase_add_test(tc, litest_int_gt_notrigger);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("pointer comparison ");
+ tcase_add_test_raise_signal(tc, litest_ptr_eq_trigger, SIGABRT);
+ tcase_add_test_raise_signal(tc, litest_ptr_eq_trigger_NULL, SIGABRT);
+ tcase_add_test_raise_signal(tc, litest_ptr_eq_trigger_NULL2, SIGABRT);
+ tcase_add_test(tc, litest_ptr_eq_notrigger);
+ tcase_add_test_raise_signal(tc, litest_ptr_ne_trigger, SIGABRT);
+ tcase_add_test_raise_signal(tc, litest_ptr_ne_trigger_NULL, SIGABRT);
+ tcase_add_test_raise_signal(tc, litest_ptr_ne_trigger_NULL2, SIGABRT);
+ tcase_add_test(tc, litest_ptr_ne_notrigger);
+ tcase_add_test_raise_signal(tc, litest_ptr_null_trigger, SIGABRT);
+ tcase_add_test(tc, litest_ptr_null_notrigger);
+ tcase_add_test_raise_signal(tc, litest_ptr_notnull_trigger, SIGABRT);
+ tcase_add_test_raise_signal(tc, litest_ptr_notnull_trigger_NULL, SIGABRT);
+ tcase_add_test(tc, litest_ptr_notnull_notrigger);
+ suite_add_tcase(s, tc);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nfailed;
+ Suite *s;
+ SRunner *sr;
+
+ /* when running under valgrind we're using nofork mode, so a signal
+ * raised by a test will fail in valgrind. There's nothing to
+ * memcheck here anyway, so just skip the valgrind test */
+ if (getenv("USING_VALGRIND"))
+ return EXIT_SUCCESS;
+
+ s = litest_assert_macros_suite();
+ sr = srunner_create(s);
+
+ srunner_run_all(sr, CK_ENV);
+ nfailed = srunner_ntests_failed(sr);
+ srunner_free(sr);
+
+ return (nfailed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/test/litest-wheel-only.c b/test/litest-wheel-only.c
new file mode 100644
index 00000000..e087dd26
--- /dev/null
+++ b/test/litest-wheel-only.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright © 2015 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "litest.h"
+#include "litest-int.h"
+
+static void litest_wheel_only_setup(void)
+{
+ struct litest_device *d = litest_create_device(LITEST_WHEEL_ONLY);
+ litest_set_current_device(d);
+}
+
+static struct input_id input_id = {
+ .bustype = 0x3,
+ .vendor = 0x1,
+ .product = 0x2,
+};
+
+static int events[] = {
+ EV_REL, REL_WHEEL,
+ -1 , -1,
+};
+
+static const char udev_rule[] =
+"ACTION==\"remove\", GOTO=\"wheel_only_end\"\n"
+"KERNEL!=\"event*\", GOTO=\"wheel_only_end\"\n"
+"\n"
+"ATTRS{name}==\"litest wheel only device*\",\\\n"
+" ENV{ID_INPUT_KEY}=\"1\"\n"
+"\n"
+"LABEL=\"wheel_only_end\"";
+
+struct litest_test_device litest_wheel_only_device = {
+ .type = LITEST_WHEEL_ONLY,
+ .features = LITEST_WHEEL,
+ .shortname = "wheel only",
+ .setup = litest_wheel_only_setup,
+ .interface = NULL,
+
+ .name = "wheel only device",
+ .id = &input_id,
+ .absinfo = NULL,
+ .events = events,
+ .udev_rule = udev_rule,
+};
diff --git a/test/litest.c b/test/litest.c
index 9ac7d3c2..60c60115 100644
--- a/test/litest.c
+++ b/test/litest.c
@@ -1,5 +1,6 @@
/*
* Copyright © 2013 Red Hat, Inc.
+ * Copyright © 2013 Marcin Slusarz
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@@ -29,6 +30,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -50,6 +52,237 @@
static int in_debugger = -1;
static int verbose = 0;
+const char *filter_test = NULL;
+const char *filter_device = NULL;
+const char *filter_group = NULL;
+
+#ifdef HAVE_LIBUNWIND
+#define UNW_LOCAL_ONLY
+#include
+#include
+
+/* defined for the litest selftest */
+#ifndef LITEST_DISABLE_BACKTRACE_LOGGING
+#define litest_log(...) fprintf(stderr, __VA_ARGS__)
+#define litest_vlog(format_, args_) vfprintf(stderr, format_, args_)
+#else
+#define litest_log(...) /* __VA_ARGS__ */
+#define litest_vlog(...) /* __VA_ARGS__ */
+#endif
+
+static char cwd[PATH_MAX];
+
+static bool
+litest_backtrace_get_lineno(const char *executable,
+ unw_word_t addr,
+ char *file_return,
+ int *line_return)
+{
+#if HAVE_ADDR2LINE
+ FILE* f;
+ char buffer[PATH_MAX];
+ char *s;
+ int i;
+
+ if (!cwd[0])
+ getcwd(cwd, sizeof(cwd));
+
+ sprintf (buffer,
+ ADDR2LINE " -C -e %s -i %lx",
+ executable,
+ (unsigned long) addr);
+
+ f = popen(buffer, "r");
+ if (f == NULL) {
+ litest_log("Failed to execute: %s\n", buffer);
+ return false;
+ }
+
+ buffer[0] = '?';
+ fgets(buffer, sizeof(buffer), f);
+ fclose(f);
+
+ if (buffer[0] == '?')
+ return false;
+
+ s = strrchr(buffer, ':');
+ if (!s)
+ return false;
+
+ *s = '\0';
+ s++;
+ sscanf(s, "%d", line_return);
+
+ /* now strip cwd from buffer */
+ s = buffer;
+ i = 0;
+ while(cwd[i] == *s) {
+ *s = '\0';
+ s++;
+ i++;
+ }
+
+ if (i > 0)
+ *(--s) = '.';
+ strcpy(file_return, s);
+
+ return true;
+#else /* HAVE_ADDR2LINE */
+ return false;
+#endif
+}
+
+static void
+litest_backtrace(void)
+{
+ unw_cursor_t cursor;
+ unw_context_t context;
+ unw_word_t off;
+ unw_proc_info_t pip;
+ int ret;
+ char procname[256];
+ Dl_info dlinfo;
+ /* filename and i are unused ifdef LITEST_SHUTUP */
+ const char *filename __attribute__((unused));
+ int i __attribute__((unused)) = 0;
+
+ pip.unwind_info = NULL;
+ ret = unw_getcontext(&context);
+ if (ret) {
+ litest_log("unw_getcontext failed: %s [%d]\n",
+ unw_strerror(ret),
+ ret);
+ return;
+ }
+
+ ret = unw_init_local(&cursor, &context);
+ if (ret) {
+ litest_log("unw_init_local failed: %s [%d]\n",
+ unw_strerror(ret),
+ ret);
+ return;
+ }
+
+ litest_log("\nBacktrace:\n");
+ ret = unw_step(&cursor);
+ while (ret > 0) {
+ char file[PATH_MAX];
+ int line;
+ bool have_lineno = false;
+
+ ret = unw_get_proc_info(&cursor, &pip);
+ if (ret) {
+ litest_log("unw_get_proc_info failed: %s [%d]\n",
+ unw_strerror(ret),
+ ret);
+ break;
+ }
+
+ ret = unw_get_proc_name(&cursor, procname, 256, &off);
+ if (ret && ret != -UNW_ENOMEM) {
+ if (ret != -UNW_EUNSPEC)
+ litest_log("unw_get_proc_name failed: %s [%d]\n",
+ unw_strerror(ret),
+ ret);
+ procname[0] = '?';
+ procname[1] = 0;
+ }
+
+ if (dladdr((void *)(pip.start_ip + off), &dlinfo) &&
+ dlinfo.dli_fname &&
+ *dlinfo.dli_fname) {
+ filename = dlinfo.dli_fname;
+ have_lineno = litest_backtrace_get_lineno(filename,
+ (pip.start_ip + off),
+ file,
+ &line);
+ } else {
+ filename = "?";
+ }
+
+ if (have_lineno) {
+ litest_log("%u: %s() (%s:%d)\n",
+ i,
+ procname,
+ file,
+ line);
+ } else {
+ litest_log("%u: %s (%s%s+%#x) [%p]\n",
+ i,
+ filename,
+ procname,
+ ret == -UNW_ENOMEM ? "..." : "",
+ (int)off,
+ (void *)(pip.start_ip + off));
+ }
+
+ i++;
+ ret = unw_step(&cursor);
+ if (ret < 0)
+ litest_log("unw_step failed: %s [%d]\n",
+ unw_strerror(ret),
+ ret);
+ }
+ litest_log("\n");
+}
+#else /* HAVE_LIBUNWIND */
+static inline void
+litest_backtrace(void)
+{
+ /* thou shall install libunwind */
+}
+#endif
+
+void
+litest_fail_condition(const char *file,
+ int line,
+ const char *func,
+ const char *condition,
+ const char *message,
+ ...)
+{
+ litest_log("FAILED: %s\n", condition);
+
+ if (message) {
+ va_list args;
+ va_start(args, message);
+ litest_vlog(message, args);
+ va_end(args);
+ }
+
+ litest_log("in %s() (%s:%d)\n", func, file, line);
+ litest_backtrace();
+ abort();
+}
+
+void
+litest_fail_comparison_int(const char *file,
+ int line,
+ const char *func,
+ const char *operator,
+ int a,
+ int b,
+ const char *astr,
+ const char *bstr)
+{
+ litest_log("FAILED COMPARISON: %s %s %s\n", astr, operator, bstr);
+ litest_log("Resolved to: %d %s %d\n", a, operator, b);
+ litest_log("in %s() (%s:%d)\n", func, file, line);
+ litest_backtrace();
+ abort();
+}
+
+void
+litest_fail_comparison_ptr(const char *file,
+ int line,
+ const char *func,
+ const char *comparison)
+{
+ litest_log("FAILED COMPARISON: %s\n", comparison);
+ litest_log("in %s() (%s:%d)\n", func, file, line);
+ litest_backtrace();
+ abort();
+}
struct test {
struct list node;
@@ -67,11 +300,13 @@ struct suite {
static struct litest_device *current_device;
-struct litest_device *litest_current_device(void) {
+struct litest_device *litest_current_device(void)
+{
return current_device;
}
-void litest_set_current_device(struct litest_device *device) {
+void litest_set_current_device(struct litest_device *device)
+{
current_device = device;
}
@@ -102,6 +337,12 @@ extern struct litest_test_device litest_synaptics_hover_device;
extern struct litest_test_device litest_synaptics_carbon3rd_device;
extern struct litest_test_device litest_protocol_a_screen;
extern struct litest_test_device litest_wacom_finger_device;
+extern struct litest_test_device litest_keyboard_blackwidow_device;
+extern struct litest_test_device litest_wheel_only_device;
+extern struct litest_test_device litest_mouse_roccat_device;
+extern struct litest_test_device litest_ms_surface_cover_device;
+extern struct litest_test_device litest_logitech_trackball_device;
+extern struct litest_test_device litest_atmel_hover_device;
struct litest_test_device* devices[] = {
&litest_synaptics_clickpad_device,
@@ -125,6 +366,12 @@ struct litest_test_device* devices[] = {
&litest_synaptics_carbon3rd_device,
&litest_protocol_a_screen,
&litest_wacom_finger_device,
+ &litest_keyboard_blackwidow_device,
+ &litest_wheel_only_device,
+ &litest_mouse_roccat_device,
+ &litest_ms_surface_cover_device,
+ &litest_logitech_trackball_device,
+ &litest_atmel_hover_device,
NULL,
};
@@ -181,8 +428,10 @@ litest_drop_udev_rules(void)
static void
litest_add_tcase_for_device(struct suite *suite,
+ const char *funcname,
void *func,
- const struct litest_test_device *dev)
+ const struct litest_test_device *dev,
+ const struct range *range)
{
struct test *t;
const char *test_name = dev->shortname;
@@ -191,7 +440,13 @@ litest_add_tcase_for_device(struct suite *suite,
if (strcmp(t->name, test_name) != 0)
continue;
- tcase_add_test(t->tc, func);
+ if (range)
+ tcase_add_loop_test(t->tc,
+ func,
+ range->lower,
+ range->upper);
+ else
+ tcase_add_test(t->tc, func);
return;
}
@@ -214,16 +469,25 @@ litest_add_tcase_for_device(struct suite *suite,
}
static void
-litest_add_tcase_no_device(struct suite *suite, void *func)
+litest_add_tcase_no_device(struct suite *suite,
+ void *func,
+ const struct range *range)
{
struct test *t;
const char *test_name = "no device";
+ if (filter_device &&
+ fnmatch(filter_device, test_name, 0) != 0)
+ return;
+
list_for_each(t, &suite->tests, node) {
if (strcmp(t->name, test_name) != 0)
continue;
- tcase_add_test(t->tc, func);
+ if (range)
+ tcase_add_loop_test(t->tc, func, range->lower, range->upper);
+ else
+ tcase_add_test(t->tc, func);
return;
}
@@ -236,40 +500,6 @@ litest_add_tcase_no_device(struct suite *suite, void *func)
suite_add_tcase(suite->suite, t->tc);
}
-static void
-litest_add_tcase(struct suite *suite, void *func,
- enum litest_device_feature required,
- enum litest_device_feature excluded)
-{
- struct litest_test_device **dev = devices;
-
- assert(required >= LITEST_DISABLE_DEVICE);
- assert(excluded >= LITEST_DISABLE_DEVICE);
-
- if (required == LITEST_DISABLE_DEVICE &&
- excluded == LITEST_DISABLE_DEVICE) {
- litest_add_tcase_no_device(suite, func);
- } else if (required != LITEST_ANY || excluded != LITEST_ANY) {
- while (*dev) {
- if (((*dev)->features & required) == required &&
- ((*dev)->features & excluded) == 0)
- litest_add_tcase_for_device(suite, func, *dev);
- dev++;
- }
- } else {
- while (*dev) {
- litest_add_tcase_for_device(suite, func, *dev);
- dev++;
- }
- }
-}
-
-void
-litest_add_no_device(const char *name, void *func)
-{
- litest_add(name, func, LITEST_DISABLE_DEVICE, LITEST_DISABLE_DEVICE);
-}
-
static struct suite *
get_suite(const char *name)
{
@@ -294,35 +524,151 @@ get_suite(const char *name)
return s;
}
-void
-litest_add(const char *name,
- void *func,
- enum litest_device_feature required,
- enum litest_device_feature excluded)
+static void
+litest_add_tcase(const char *suite_name,
+ const char *funcname,
+ void *func,
+ enum litest_device_feature required,
+ enum litest_device_feature excluded,
+ const struct range *range)
{
- litest_add_tcase(get_suite(name), func, required, excluded);
+ struct litest_test_device **dev = devices;
+ struct suite *suite;
+
+ assert(required >= LITEST_DISABLE_DEVICE);
+ assert(excluded >= LITEST_DISABLE_DEVICE);
+
+ if (filter_test &&
+ fnmatch(filter_test, funcname, 0) != 0)
+ return;
+
+ if (filter_group &&
+ fnmatch(filter_group, suite_name, 0) != 0)
+ return;
+
+ suite = get_suite(suite_name);
+
+ if (required == LITEST_DISABLE_DEVICE &&
+ excluded == LITEST_DISABLE_DEVICE) {
+ litest_add_tcase_no_device(suite, func, range);
+ } else if (required != LITEST_ANY || excluded != LITEST_ANY) {
+ for (; *dev; dev++) {
+ if (filter_device &&
+ fnmatch(filter_device, (*dev)->shortname, 0) != 0)
+ continue;
+ if (((*dev)->features & required) != required ||
+ ((*dev)->features & excluded) != 0)
+ continue;
+
+ litest_add_tcase_for_device(suite,
+ funcname,
+ func,
+ *dev,
+ range);
+ }
+ } else {
+ for (; *dev; dev++) {
+ if (filter_device &&
+ fnmatch(filter_device, (*dev)->shortname, 0) != 0)
+ continue;
+
+ litest_add_tcase_for_device(suite,
+ funcname,
+ func,
+ *dev,
+ range);
+ }
+ }
}
void
-litest_add_for_device(const char *name,
- void *func,
- enum litest_device_type type)
+_litest_add_no_device(const char *name, const char *funcname, void *func)
+{
+ _litest_add(name, funcname, func, LITEST_DISABLE_DEVICE, LITEST_DISABLE_DEVICE);
+}
+
+void
+_litest_add_ranged_no_device(const char *name,
+ const char *funcname,
+ void *func,
+ const struct range *range)
+{
+ _litest_add_ranged(name,
+ funcname,
+ func,
+ LITEST_DISABLE_DEVICE,
+ LITEST_DISABLE_DEVICE,
+ range);
+}
+
+void
+_litest_add(const char *name,
+ const char *funcname,
+ void *func,
+ enum litest_device_feature required,
+ enum litest_device_feature excluded)
+{
+ _litest_add_ranged(name,
+ funcname,
+ func,
+ required,
+ excluded,
+ NULL);
+}
+
+void
+_litest_add_ranged(const char *name,
+ const char *funcname,
+ void *func,
+ enum litest_device_feature required,
+ enum litest_device_feature excluded,
+ const struct range *range)
+{
+ litest_add_tcase(name, funcname, func, required, excluded, range);
+}
+
+void
+_litest_add_for_device(const char *name,
+ const char *funcname,
+ void *func,
+ enum litest_device_type type)
+{
+ _litest_add_ranged_for_device(name, funcname, func, type, NULL);
+}
+
+void
+_litest_add_ranged_for_device(const char *name,
+ const char *funcname,
+ void *func,
+ enum litest_device_type type,
+ const struct range *range)
{
struct suite *s;
struct litest_test_device **dev = devices;
assert(type < LITEST_NO_DEVICE);
+ if (filter_group &&
+ fnmatch(filter_group, name, 0) != 0)
+ return;
+
s = get_suite(name);
- while (*dev) {
+ for (; *dev; dev++) {
+ if (filter_device &&
+ fnmatch(filter_device, (*dev)->shortname, 0) != 0)
+ continue;
+
if ((*dev)->type == type) {
- litest_add_tcase_for_device(s, func, *dev);
+ litest_add_tcase_for_device(s,
+ funcname,
+ func,
+ *dev,
+ range);
return;
}
- dev++;
}
- ck_abort_msg("Invalid test device type");
+ litest_abort_msg("Invalid test device type");
}
static int
@@ -391,7 +737,8 @@ litest_log_handler(struct libinput *libinput,
static int
open_restricted(const char *path, int flags, void *userdata)
{
- return open(path, flags);
+ int fd = open(path, flags);
+ return fd < 0 ? -errno : fd;
}
static void
@@ -405,14 +752,9 @@ struct libinput_interface interface = {
.close_restricted = close_restricted,
};
-static const struct option opts[] = {
- { "list", 0, 0, 'l' },
- { "verbose", 0, 0, 'v' },
- { 0, 0, 0, 0}
-};
-
-int
-litest_run(int argc, char **argv) {
+static inline int
+litest_run(int argc, char **argv)
+{
struct suite *s, *snext;
int failed;
SRunner *sr = NULL;
@@ -430,26 +772,8 @@ litest_run(int argc, char **argv) {
srunner_add_suite(sr, s->suite);
}
- while(1) {
- int c;
- int option_index = 0;
-
- c = getopt_long(argc, argv, "", opts, &option_index);
- if (c == -1)
- break;
- switch(c) {
- case 'l':
- litest_list_tests(&all_tests);
- return 0;
- case 'v':
- verbose = 1;
- break;
- default:
- fprintf(stderr, "usage: %s [--list]\n", argv[0]);
- return 1;
-
- }
- }
+ if (getenv("LITEST_VERBOSE"))
+ verbose = 1;
srunner_run_all(sr, CK_ENV);
failed = srunner_ntests_failed(sr);
@@ -484,13 +808,13 @@ merge_absinfo(const struct input_absinfo *orig,
return NULL;
abs = calloc(sz, sizeof(*abs));
- ck_assert(abs != NULL);
+ litest_assert(abs != NULL);
nelem = 0;
while (orig[nelem].value != -1) {
abs[nelem] = orig[nelem];
nelem++;
- ck_assert_int_lt(nelem, sz);
+ litest_assert_int_lt(nelem, sz);
}
/* just append, if the same axis is present twice, libevdev will
@@ -498,10 +822,10 @@ merge_absinfo(const struct input_absinfo *orig,
i = 0;
while (override && override[i].value != -1) {
abs[nelem++] = override[i++];
- ck_assert_int_lt(nelem, sz);
+ litest_assert_int_lt(nelem, sz);
}
- ck_assert_int_lt(nelem, sz);
+ litest_assert_int_lt(nelem, sz);
abs[nelem].value = -1;
return abs;
@@ -518,13 +842,13 @@ merge_events(const int *orig, const int *override)
return NULL;
events = calloc(sz, sizeof(int));
- ck_assert(events != NULL);
+ litest_assert(events != NULL);
nelem = 0;
while (orig[nelem] != -1) {
events[nelem] = orig[nelem];
nelem++;
- ck_assert_int_lt(nelem, sz);
+ litest_assert_int_lt(nelem, sz);
}
/* just append, if the same axis is present twice, libevdev will
@@ -532,10 +856,10 @@ merge_events(const int *orig, const int *override)
i = 0;
while (override && override[i] != -1) {
events[nelem++] = override[i++];
- ck_assert_int_le(nelem, sz);
+ litest_assert_int_le(nelem, sz);
}
- ck_assert_int_lt(nelem, sz);
+ litest_assert_int_lt(nelem, sz);
events[nelem] = -1;
return events;
@@ -561,13 +885,14 @@ litest_init_udev_rules(struct litest_test_device *dev)
UDEV_RULES_D,
UDEV_RULE_PREFIX,
dev->shortname);
- ck_assert_int_eq(rc,
- strlen(UDEV_RULES_D) +
- strlen(UDEV_RULE_PREFIX) +
- strlen(dev->shortname) + 7);
+ litest_assert_int_eq(rc,
+ (int)(
+ strlen(UDEV_RULES_D) +
+ strlen(UDEV_RULE_PREFIX) +
+ strlen(dev->shortname) + 7));
f = fopen(path, "w");
- ck_assert_notnull(f);
- ck_assert_int_ge(fputs(dev->udev_rule, f), 0);
+ litest_assert_notnull(f);
+ litest_assert_int_ge(fputs(dev->udev_rule, f), 0);
fclose(f);
litest_reload_udev_rules();
@@ -601,7 +926,7 @@ litest_create(enum litest_device_type which,
ck_abort_msg("Invalid device type %d\n", which);
d = zalloc(sizeof(*d));
- ck_assert(d != NULL);
+ litest_assert(d != NULL);
udev_file = litest_init_udev_rules(*dev);
@@ -611,8 +936,7 @@ litest_create(enum litest_device_type which,
if (abs_override || events_override) {
if (udev_file)
unlink(udev_file);
- ck_abort_msg("Custom create cannot"
- "be overridden");
+ litest_abort_msg("Custom create cannot be overridden");
}
return d;
@@ -641,7 +965,7 @@ litest_create_context(void)
{
struct libinput *libinput =
libinput_path_create_context(&interface, NULL);
- ck_assert_notnull(libinput);
+ litest_assert_notnull(libinput);
libinput_log_set_handler(libinput, litest_log_handler);
if (verbose)
@@ -682,16 +1006,16 @@ litest_add_device_with_overrides(struct libinput *libinput,
events_override);
path = libevdev_uinput_get_devnode(d->uinput);
- ck_assert(path != NULL);
+ litest_assert(path != NULL);
fd = open(path, O_RDWR|O_NONBLOCK);
- ck_assert_int_ne(fd, -1);
+ litest_assert_int_ne(fd, -1);
rc = libevdev_new_from_fd(fd, &d->evdev);
- ck_assert_int_eq(rc, 0);
+ litest_assert_int_eq(rc, 0);
d->libinput = libinput;
d->libinput_device = libinput_path_add_device(d->libinput, path);
- ck_assert(d->libinput_device != NULL);
+ litest_assert(d->libinput_device != NULL);
libinput_device_ref(d->libinput_device);
if (d->interface) {
@@ -786,13 +1110,14 @@ litest_event(struct litest_device *d, unsigned int type,
return;
ret = libevdev_uinput_write_event(d->uinput, type, code, value);
- ck_assert_int_eq(ret, 0);
+ litest_assert_int_eq(ret, 0);
}
int
litest_auto_assign_value(struct litest_device *d,
const struct input_event *ev,
- int slot, double x, double y)
+ int slot, double x, double y,
+ bool touching)
{
static int tracking_id;
int value = ev->value;
@@ -815,6 +1140,9 @@ litest_auto_assign_value(struct litest_device *d,
case ABS_MT_SLOT:
value = slot;
break;
+ case ABS_MT_DISTANCE:
+ value = touching ? 0 : 1;
+ break;
}
return value;
@@ -831,9 +1159,9 @@ send_btntool(struct litest_device *d)
litest_event(d, EV_KEY, BTN_TOOL_QUINTTAP, d->ntouches_down == 5);
}
-void
-litest_touch_down(struct litest_device *d, unsigned int slot,
- double x, double y)
+static void
+litest_slot_start(struct litest_device *d, unsigned int slot,
+ double x, double y, bool touching)
{
struct input_event *ev;
@@ -849,12 +1177,25 @@ litest_touch_down(struct litest_device *d, unsigned int slot,
ev = d->interface->touch_down_events;
while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
- int value = litest_auto_assign_value(d, ev, slot, x, y);
+ int value = litest_auto_assign_value(d,
+ ev,
+ slot,
+ x,
+ y,
+ touching);
+
litest_event(d, ev->type, ev->code, value);
ev++;
}
}
+void
+litest_touch_down(struct litest_device *d, unsigned int slot,
+ double x, double y)
+{
+ litest_slot_start(d, slot, x, y, 1);
+}
+
void
litest_touch_up(struct litest_device *d, unsigned int slot)
{
@@ -880,15 +1221,20 @@ litest_touch_up(struct litest_device *d, unsigned int slot)
ev = up;
while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
- int value = litest_auto_assign_value(d, ev, slot, 0, 0);
+ int value = litest_auto_assign_value(d,
+ ev,
+ slot,
+ 0,
+ 0,
+ false);
litest_event(d, ev->type, ev->code, value);
ev++;
}
}
-void
-litest_touch_move(struct litest_device *d, unsigned int slot,
- double x, double y)
+static void
+litest_slot_move(struct litest_device *d, unsigned int slot,
+ double x, double y, bool touching)
{
struct input_event *ev;
@@ -899,12 +1245,24 @@ litest_touch_move(struct litest_device *d, unsigned int slot,
ev = d->interface->touch_move_events;
while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
- int value = litest_auto_assign_value(d, ev, slot, x, y);
+ int value = litest_auto_assign_value(d,
+ ev,
+ slot,
+ x,
+ y,
+ touching);
litest_event(d, ev->type, ev->code, value);
ev++;
}
}
+void
+litest_touch_move(struct litest_device *d, unsigned int slot,
+ double x, double y)
+{
+ litest_slot_move(d, slot, x, y, true);
+}
+
void
litest_touch_move_to(struct litest_device *d,
unsigned int slot,
@@ -1036,6 +1394,98 @@ litest_touch_move_two_touches(struct litest_device *d,
litest_touch_move(d, 1, x1 + dx, y1 + dy);
}
+void
+litest_hover_start(struct litest_device *d, unsigned int slot,
+ double x, double y)
+{
+ litest_slot_start(d, slot, x, y, 0);
+}
+
+void
+litest_hover_end(struct litest_device *d, unsigned int slot)
+{
+ struct input_event *ev;
+ struct input_event up[] = {
+ { .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_MT_DISTANCE, .value = 1 },
+ { .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = -1 },
+ { .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+ { .type = -1, .code = -1 }
+ };
+
+ assert(d->ntouches_down > 0);
+ d->ntouches_down--;
+
+ send_btntool(d);
+
+ if (d->interface->touch_up) {
+ d->interface->touch_up(d, slot);
+ return;
+ } else if (d->interface->touch_up_events) {
+ ev = d->interface->touch_up_events;
+ } else
+ ev = up;
+
+ while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
+ int value = litest_auto_assign_value(d, ev, slot, 0, 0, 0);
+ litest_event(d, ev->type, ev->code, value);
+ ev++;
+ }
+}
+
+void
+litest_hover_move(struct litest_device *d, unsigned int slot,
+ double x, double y)
+{
+ litest_slot_move(d, slot, x, y, false);
+}
+
+void
+litest_hover_move_to(struct litest_device *d,
+ unsigned int slot,
+ double x_from, double y_from,
+ double x_to, double y_to,
+ int steps, int sleep_ms)
+{
+ for (int i = 0; i < steps - 1; i++) {
+ litest_hover_move(d, slot,
+ x_from + (x_to - x_from)/steps * i,
+ y_from + (y_to - y_from)/steps * i);
+ if (sleep_ms) {
+ libinput_dispatch(d->libinput);
+ msleep(sleep_ms);
+ libinput_dispatch(d->libinput);
+ }
+ }
+ litest_hover_move(d, slot, x_to, y_to);
+}
+
+void
+litest_hover_move_two_touches(struct litest_device *d,
+ double x0, double y0,
+ double x1, double y1,
+ double dx, double dy,
+ int steps, int sleep_ms)
+{
+ for (int i = 0; i < steps - 1; i++) {
+ litest_push_event_frame(d);
+ litest_hover_move(d, 0, x0 + dx / steps * i,
+ y0 + dy / steps * i);
+ litest_hover_move(d, 1, x1 + dx / steps * i,
+ y1 + dy / steps * i);
+ litest_pop_event_frame(d);
+ if (sleep_ms) {
+ libinput_dispatch(d->libinput);
+ msleep(sleep_ms);
+ libinput_dispatch(d->libinput);
+ }
+ }
+ litest_push_event_frame(d);
+ litest_hover_move(d, 0, x0 + dx, y0 + dy);
+ litest_hover_move(d, 1, x1 + dx, y1 + dy);
+ litest_pop_event_frame(d);
+}
+
void
litest_button_click(struct litest_device *d, unsigned int button, bool is_press)
{
@@ -1082,9 +1532,9 @@ int
litest_scale(const struct litest_device *d, unsigned int axis, double val)
{
int min, max;
- ck_assert_int_ge(val, 0);
- ck_assert_int_le(val, 100);
- ck_assert_int_le(axis, ABS_Y);
+ litest_assert_int_ge((int)val, 0);
+ litest_assert_int_le((int)val, 100);
+ litest_assert_int_le(axis, (unsigned int)ABS_Y);
min = d->interface->min[axis];
max = d->interface->max[axis];
@@ -1274,7 +1724,7 @@ litest_assert_empty_queue(struct libinput *li)
libinput_dispatch(li);
}
- ck_assert(empty_queue);
+ litest_assert(empty_queue);
}
struct libevdev_uinput *
@@ -1300,7 +1750,7 @@ litest_create_uinput_device_from_description(const char *name,
const char *devnode;
dev = libevdev_new();
- ck_assert(dev != NULL);
+ litest_assert(dev != NULL);
snprintf(buf, sizeof(buf), "litest %s", name);
libevdev_set_name(dev, buf);
@@ -1315,7 +1765,7 @@ litest_create_uinput_device_from_description(const char *name,
while (abs && abs->value != -1) {
rc = libevdev_enable_event_code(dev, EV_ABS,
abs->value, abs);
- ck_assert_int_eq(rc, 0);
+ litest_assert_int_eq(rc, 0);
abs++;
}
@@ -1328,7 +1778,7 @@ litest_create_uinput_device_from_description(const char *name,
rc = libevdev_enable_event_code(dev, type, code,
type == EV_ABS ? &default_abs : NULL);
}
- ck_assert_int_eq(rc, 0);
+ litest_assert_int_eq(rc, 0);
}
rc = libevdev_uinput_create_from_device(dev,
@@ -1338,29 +1788,28 @@ litest_create_uinput_device_from_description(const char *name,
http://cgit.freedesktop.org/libevdev/commit/?id=debe9b030c8069cdf78307888ef3b65830b25122 */
if (rc == -EBADF)
rc = -EACCES;
- ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc));
+ litest_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc));
libevdev_free(dev);
+ devnode = libevdev_uinput_get_devnode(uinput);
+ litest_assert_notnull(devnode);
+ fd = open(devnode, O_RDONLY);
+ litest_assert_int_gt(fd, -1);
+ rc = libevdev_new_from_fd(fd, &dev);
+ litest_assert_int_eq(rc, 0);
+
/* uinput does not yet support setting the resolution, so we set it
* afterwards. This is of course racy as hell but the way we
* _generally_ use this function by the time libinput uses the
* device, we're finished here */
-
- devnode = libevdev_uinput_get_devnode(uinput);
- ck_assert_notnull(devnode);
- fd = open(devnode, O_RDONLY);
- ck_assert_int_gt(fd, -1);
- rc = libevdev_new_from_fd(fd, &dev);
- ck_assert_int_eq(rc, 0);
-
abs = abs_info;
while (abs && abs->value != -1) {
if (abs->resolution != 0) {
rc = libevdev_kernel_set_abs_info(dev,
abs->value,
abs);
- ck_assert_int_eq(rc, 0);
+ litest_assert_int_eq(rc, 0);
}
abs++;
}
@@ -1384,7 +1833,7 @@ litest_create_uinput_abs_device_v(const char *name,
(code = va_arg(args, int)) != -1) {
*event++ = type;
*event++ = code;
- ck_assert(event < &events[ARRAY_LENGTH(events) - 2]);
+ litest_assert(event < &events[ARRAY_LENGTH(events) - 2]);
}
*event++ = -1;
@@ -1423,27 +1872,129 @@ litest_create_uinput_device(const char *name, struct input_id *id, ...)
return uinput;
}
+struct libinput_event_pointer*
+litest_is_button_event(struct libinput_event *event,
+ unsigned int button,
+ enum libinput_button_state state)
+{
+ struct libinput_event_pointer *ptrev;
+ enum libinput_event_type type = LIBINPUT_EVENT_POINTER_BUTTON;
+
+ litest_assert(event != NULL);
+ litest_assert_int_eq(libinput_event_get_type(event), type);
+ ptrev = libinput_event_get_pointer_event(event);
+ litest_assert_int_eq(libinput_event_pointer_get_button(ptrev),
+ button);
+ litest_assert_int_eq(libinput_event_pointer_get_button_state(ptrev),
+ state);
+
+ return ptrev;
+}
+
+struct libinput_event_pointer *
+litest_is_axis_event(struct libinput_event *event,
+ enum libinput_pointer_axis axis,
+ enum libinput_pointer_axis_source source)
+{
+ struct libinput_event_pointer *ptrev;
+ enum libinput_event_type type = LIBINPUT_EVENT_POINTER_AXIS;
+
+ litest_assert(event != NULL);
+ litest_assert_int_eq(libinput_event_get_type(event), type);
+ ptrev = libinput_event_get_pointer_event(event);
+ litest_assert(libinput_event_pointer_has_axis(ptrev, axis));
+
+ if (source != 0)
+ litest_assert_int_eq(libinput_event_pointer_get_axis_source(ptrev),
+ source);
+
+ return ptrev;
+}
+
+struct libinput_event_pointer *
+litest_is_motion_event(struct libinput_event *event)
+{
+ struct libinput_event_pointer *ptrev;
+ enum libinput_event_type type = LIBINPUT_EVENT_POINTER_MOTION;
+ double x, y, ux, uy;
+
+ litest_assert(event != NULL);
+ litest_assert_int_eq(libinput_event_get_type(event), type);
+ ptrev = libinput_event_get_pointer_event(event);
+
+ x = libinput_event_pointer_get_dx(ptrev);
+ y = libinput_event_pointer_get_dy(ptrev);
+ ux = libinput_event_pointer_get_dx_unaccelerated(ptrev);
+ uy = libinput_event_pointer_get_dy_unaccelerated(ptrev);
+
+ /* No 0 delta motion events */
+ litest_assert(x != 0.0 || y != 0.0 ||
+ ux != 0.0 || uy != 0.0);
+
+ return ptrev;
+}
+
void
litest_assert_button_event(struct libinput *li, unsigned int button,
enum libinput_button_state state)
{
struct libinput_event *event;
- struct libinput_event_pointer *ptrev;
litest_wait_for_event(li);
event = libinput_get_event(li);
- ck_assert(event != NULL);
- ck_assert_int_eq(libinput_event_get_type(event),
- LIBINPUT_EVENT_POINTER_BUTTON);
- ptrev = libinput_event_get_pointer_event(event);
- ck_assert_int_eq(libinput_event_pointer_get_button(ptrev),
- button);
- ck_assert_int_eq(libinput_event_pointer_get_button_state(ptrev),
- state);
+ litest_is_button_event(event, button, state);
+
libinput_event_destroy(event);
}
+struct libinput_event_touch *
+litest_is_touch_event(struct libinput_event *event,
+ enum libinput_event_type type)
+{
+ struct libinput_event_touch *touch;
+
+ litest_assert(event != NULL);
+
+ if (type == 0)
+ type = libinput_event_get_type(event);
+
+ switch (type) {
+ case LIBINPUT_EVENT_TOUCH_DOWN:
+ case LIBINPUT_EVENT_TOUCH_UP:
+ case LIBINPUT_EVENT_TOUCH_MOTION:
+ case LIBINPUT_EVENT_TOUCH_FRAME:
+ litest_assert_int_eq(libinput_event_get_type(event), type);
+ break;
+ default:
+ ck_abort_msg("%s: invalid touch type %d\n", __func__, type);
+ }
+
+ touch = libinput_event_get_touch_event(event);
+
+ return touch;
+}
+
+struct libinput_event_keyboard *
+litest_is_keyboard_event(struct libinput_event *event,
+ unsigned int key,
+ enum libinput_key_state state)
+{
+ struct libinput_event_keyboard *kevent;
+ enum libinput_event_type type = LIBINPUT_EVENT_KEYBOARD_KEY;
+
+ litest_assert(event != NULL);
+ litest_assert_int_eq(libinput_event_get_type(event), type);
+
+ kevent = libinput_event_get_keyboard_event(event);
+ litest_assert(kevent != NULL);
+
+ litest_assert_int_eq(libinput_event_keyboard_get_key(kevent), key);
+ litest_assert_int_eq(libinput_event_keyboard_get_key_state(kevent),
+ state);
+ return kevent;
+}
+
void
litest_assert_tablet_button_event(struct libinput *li, unsigned int button,
enum libinput_button_state state)
@@ -1472,33 +2023,27 @@ litest_assert_scroll(struct libinput *li,
{
struct libinput_event *event, *next_event;
struct libinput_event_pointer *ptrev;
+ int value;
event = libinput_get_event(li);
next_event = libinput_get_event(li);
- ck_assert(next_event != NULL); /* At least 1 scroll + stop scroll */
+ litest_assert(next_event != NULL); /* At least 1 scroll + stop scroll */
while (event) {
- ck_assert_int_eq(libinput_event_get_type(event),
- LIBINPUT_EVENT_POINTER_AXIS);
- ptrev = libinput_event_get_pointer_event(event);
- ck_assert(ptrev != NULL);
+ ptrev = litest_is_axis_event(event, axis, 0);
if (next_event) {
+ value = libinput_event_pointer_get_axis_value(ptrev,
+ axis);
/* Normal scroll event, check dir */
if (minimum_movement > 0) {
- ck_assert_int_ge(
- libinput_event_pointer_get_axis_value(ptrev,
- axis),
- minimum_movement);
+ litest_assert_int_ge(value, minimum_movement);
} else {
- ck_assert_int_le(
- libinput_event_pointer_get_axis_value(ptrev,
- axis),
- minimum_movement);
+ litest_assert_int_le(value, minimum_movement);
}
} else {
/* Last scroll event, must be 0 */
- ck_assert_int_eq(
+ litest_assert_int_eq(
libinput_event_pointer_get_axis_value(ptrev, axis),
0);
}
@@ -1518,11 +2063,11 @@ litest_assert_only_typed_events(struct libinput *li,
libinput_dispatch(li);
event = libinput_get_event(li);
- ck_assert_notnull(event);
+ litest_assert_notnull(event);
while (event) {
- ck_assert_int_eq(libinput_event_get_type(event),
- type);
+ litest_assert_int_eq(libinput_event_get_type(event),
+ type);
libinput_event_destroy(event);
libinput_dispatch(li);
event = libinput_get_event(li);
@@ -1535,6 +2080,12 @@ litest_timeout_tap(void)
msleep(200);
}
+void
+litest_timeout_tapndrag(void)
+{
+ msleep(520);
+}
+
void
litest_timeout_softbuttons(void)
{
@@ -1559,6 +2110,12 @@ litest_timeout_edgescroll(void)
msleep(300);
}
+void
+litest_timeout_middlebutton(void)
+{
+ msleep(70);
+}
+
void
litest_push_event_frame(struct litest_device *dev)
{
@@ -1583,11 +2140,11 @@ send_abs_xy(struct litest_device *d, double x, double y)
e.type = EV_ABS;
e.code = ABS_X;
e.value = LITEST_AUTO_ASSIGN;
- val = litest_auto_assign_value(d, &e, 0, x, y);
+ val = litest_auto_assign_value(d, &e, 0, x, y, true);
litest_event(d, EV_ABS, ABS_X, val);
e.code = ABS_Y;
- val = litest_auto_assign_value(d, &e, 0, x, y);
+ val = litest_auto_assign_value(d, &e, 0, x, y, true);
litest_event(d, EV_ABS, ABS_Y, val);
}
@@ -1600,12 +2157,12 @@ send_abs_mt_xy(struct litest_device *d, double x, double y)
e.type = EV_ABS;
e.code = ABS_MT_POSITION_X;
e.value = LITEST_AUTO_ASSIGN;
- val = litest_auto_assign_value(d, &e, 0, x, y);
+ val = litest_auto_assign_value(d, &e, 0, x, y, true);
litest_event(d, EV_ABS, ABS_MT_POSITION_X, val);
e.code = ABS_MT_POSITION_Y;
e.value = LITEST_AUTO_ASSIGN;
- val = litest_auto_assign_value(d, &e, 0, x, y);
+ val = litest_auto_assign_value(d, &e, 0, x, y, true);
litest_event(d, EV_ABS, ABS_MT_POSITION_Y, val);
}
@@ -1714,3 +2271,67 @@ litest_semi_mt_touch_up(struct litest_device *d,
litest_event(d, EV_SYN, SYN_REPORT, 0);
}
+
+static inline int
+litest_parse_argv(int argc, char **argv)
+{
+ enum {
+ OPT_FILTER_TEST,
+ OPT_FILTER_DEVICE,
+ OPT_FILTER_GROUP,
+ OPT_LIST,
+ OPT_VERBOSE,
+ };
+ static const struct option opts[] = {
+ { "filter-test", 1, 0, OPT_FILTER_TEST },
+ { "filter-device", 1, 0, OPT_FILTER_DEVICE },
+ { "filter-group", 1, 0, OPT_FILTER_GROUP },
+ { "list", 0, 0, OPT_LIST },
+ { "verbose", 0, 0, OPT_VERBOSE },
+ { 0, 0, 0, 0}
+ };
+
+ while(1) {
+ int c;
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "", opts, &option_index);
+ if (c == -1)
+ break;
+ switch(c) {
+ case OPT_FILTER_TEST:
+ filter_test = optarg;
+ break;
+ case OPT_FILTER_DEVICE:
+ filter_device = optarg;
+ break;
+ case OPT_FILTER_GROUP:
+ filter_group = optarg;
+ break;
+ case OPT_LIST:
+ litest_list_tests(&all_tests);
+ exit(0);
+ case OPT_VERBOSE:
+ verbose = 1;
+ break;
+ default:
+ fprintf(stderr, "usage: %s [--list]\n", argv[0]);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+#ifndef LITEST_NO_MAIN
+int
+main(int argc, char **argv)
+{
+ if (litest_parse_argv(argc, argv) != 0)
+ return EXIT_FAILURE;
+
+ litest_setup_tests();
+
+ return litest_run(argc, argv);
+}
+#endif
diff --git a/test/litest.h b/test/litest.h
index 0214a7d8..9efacde8 100644
--- a/test/litest.h
+++ b/test/litest.h
@@ -32,6 +32,83 @@
#include
#include
#include
+#include
+
+#define litest_assert(cond) \
+ do { \
+ if (!(cond)) \
+ litest_fail_condition(__FILE__, __LINE__, __func__, \
+ #cond, NULL); \
+ } while(0)
+
+#define litest_assert_msg(cond, ...) \
+ do { \
+ if (!(cond)) \
+ litest_fail_condition(__FILE__, __LINE__, __func__, \
+ #cond, __VA_ARGS__); \
+ } while(0)
+
+#define litest_abort_msg(...) \
+ litest_fail_condition(__FILE__, __LINE__, __func__, \
+ "aborting", __VA_ARGS__); \
+
+#define litest_assert_notnull(cond) \
+ do { \
+ if ((cond) == NULL) \
+ litest_fail_condition(__FILE__, __LINE__, __func__, \
+ #cond, " expected to be not NULL\n"); \
+ } while(0)
+
+#define litest_assert_comparison_int_(a_, op_, b_) \
+ do { \
+ __typeof__(a_) _a = a_; \
+ __typeof__(b_) _b = b_; \
+ if (trunc(_a) != _a || trunc(_b) != _b) \
+ litest_abort_msg("litest_assert_int_* used for non-integer value\n"); \
+ if (!((_a) op_ (_b))) \
+ litest_fail_comparison_int(__FILE__, __LINE__, __func__,\
+ #op_, _a, _b, \
+ #a_, #b_); \
+ } while(0)
+
+#define litest_assert_int_eq(a_, b_) \
+ litest_assert_comparison_int_(a_, ==, b_)
+
+#define litest_assert_int_ne(a_, b_) \
+ litest_assert_comparison_int_(a_, !=, b_)
+
+#define litest_assert_int_lt(a_, b_) \
+ litest_assert_comparison_int_(a_, <, b_)
+
+#define litest_assert_int_le(a_, b_) \
+ litest_assert_comparison_int_(a_, <=, b_)
+
+#define litest_assert_int_ge(a_, b_) \
+ litest_assert_comparison_int_(a_, >=, b_)
+
+#define litest_assert_int_gt(a_, b_) \
+ litest_assert_comparison_int_(a_, >, b_)
+
+#define litest_assert_comparison_ptr_(a_, op_, b_) \
+ do { \
+ __typeof__(a_) _a = a_; \
+ __typeof__(b_) _b = b_; \
+ if (!((_a) op_ (_b))) \
+ litest_fail_comparison_ptr(__FILE__, __LINE__, __func__,\
+ #a_ " " #op_ " " #b_); \
+ } while(0)
+
+#define litest_assert_ptr_eq(a_, b_) \
+ litest_assert_comparison_ptr_(a_, ==, b_)
+
+#define litest_assert_ptr_ne(a_, b_) \
+ litest_assert_comparison_ptr_(a_, !=, b_)
+
+#define litest_assert_ptr_null(a_) \
+ litest_assert_comparison_ptr_(a_, ==, NULL)
+
+#define litest_assert_ptr_notnull(a_) \
+ litest_assert_comparison_ptr_(a_, !=, NULL)
enum litest_device_type {
LITEST_NO_DEVICE = -1,
@@ -53,10 +130,15 @@ enum litest_device_type {
LITEST_SYNAPTICS_TRACKPOINT_BUTTONS = -17,
LITEST_PROTOCOL_A_SCREEN = -18,
LITEST_WACOM_FINGER = -19,
- LITEST_WACOM_BAMBOO = -20,
- LITEST_WACOM_CINTIQ = -21,
- LITEST_WACOM_INTUOS = -22,
- LITEST_WACOM_ISDV4 = -23,
+ LITEST_KEYBOARD_BLACKWIDOW = -20,
+ LITEST_WHEEL_ONLY = -21,
+ LITEST_MOUSE_ROCCAT = -22,
+ LITEST_LOGITECH_TRACKBALL = -23,
+ LITEST_ATMEL_HOVER = -24,
+ LITEST_WACOM_BAMBOO = -25,
+ LITEST_WACOM_CINTIQ = -26,
+ LITEST_WACOM_INTUOS = -27,
+ LITEST_WACOM_ISDV4 = -28,
};
enum litest_device_feature {
@@ -77,9 +159,10 @@ enum litest_device_feature {
LITEST_FAKE_MT = 1 << 12,
LITEST_ABSOLUTE = 1 << 13,
LITEST_PROTOCOL_A = 1 << 14,
- LITEST_TABLET = 1 << 15,
- LITEST_DISTANCE = 1 << 16,
- LITEST_TOOL_SERIAL = 1 << 17,
+ LITEST_HOVER = 1 << 15,
+ LITEST_TABLET = 1 << 16,
+ LITEST_DISTANCE = 1 << 17,
+ LITEST_TOOL_SERIAL = 1 << 18,
};
struct litest_device {
@@ -103,20 +186,81 @@ struct axis_replacement {
int32_t value;
};
+/* A loop range, resolves to:
+ for (i = lower; i < upper; i++)
+ */
+struct range {
+ int lower; /* inclusive */
+ int upper; /* exclusive */
+};
+
struct libinput *litest_create_context(void);
void litest_disable_log_handler(struct libinput *libinput);
void litest_restore_log_handler(struct libinput *libinput);
-void litest_add(const char *name, void *func,
- enum litest_device_feature required_feature,
- enum litest_device_feature excluded_feature);
void
-litest_add_for_device(const char *name,
- void *func,
- enum litest_device_type type);
-void litest_add_no_device(const char *name, void *func);
+litest_fail_condition(const char *file,
+ int line,
+ const char *func,
+ const char *condition,
+ const char *message,
+ ...);
+void
+litest_fail_comparison_int(const char *file,
+ int line,
+ const char *func,
+ const char *operator,
+ int a,
+ int b,
+ const char *astr,
+ const char *bstr);
+void
+litest_fail_comparison_ptr(const char *file,
+ int line,
+ const char *func,
+ const char *comparison);
-int litest_run(int argc, char **argv);
+#define litest_add(name_, func_, ...) \
+ _litest_add(name_, #func_, func_, __VA_ARGS__)
+#define litest_add_ranged(name_, func_, ...) \
+ _litest_add_ranged(name_, #func_, func_, __VA_ARGS__)
+#define litest_add_for_device(name_, func_, ...) \
+ _litest_add_for_device(name_, #func_, func_, __VA_ARGS__)
+#define litest_add_ranged_for_device(name_, func_, ...) \
+ _litest_add_ranged_for_device(name_, #func_, func_, __VA_ARGS__)
+#define litest_add_no_device(name_, func_) \
+ _litest_add_no_device(name_, #func_, func_)
+#define litest_add_ranged_no_device(name_, func_, ...) \
+ _litest_add_ranged_no_device(name_, #func_, func_, __VA_ARGS__)
+void _litest_add(const char *name,
+ const char *funcname,
+ void *func,
+ enum litest_device_feature required_feature,
+ enum litest_device_feature excluded_feature);
+void _litest_add_ranged(const char *name,
+ const char *funcname,
+ void *func,
+ enum litest_device_feature required,
+ enum litest_device_feature excluded,
+ const struct range *range);
+void _litest_add_for_device(const char *name,
+ const char *funcname,
+ void *func,
+ enum litest_device_type type);
+void _litest_add_ranged_for_device(const char *name,
+ const char *funcname,
+ void *func,
+ enum litest_device_type type,
+ const struct range *range);
+void _litest_add_no_device(const char *name,
+ const char *funcname,
+ void *func);
+void _litest_add_ranged_no_device(const char *name,
+ const char *funcname,
+ void *func,
+ const struct range *range);
+
+extern void litest_setup_tests(void);
struct litest_device * litest_create_device(enum litest_device_type which);
struct litest_device * litest_add_device(struct libinput *libinput,
enum litest_device_type which);
@@ -149,7 +293,8 @@ void litest_event(struct litest_device *t,
int value);
int litest_auto_assign_value(struct litest_device *d,
const struct input_event *ev,
- int slot, double x, double y);
+ int slot, double x, double y,
+ bool touching);
void litest_touch_up(struct litest_device *d, unsigned int slot);
void litest_touch_move(struct litest_device *d,
unsigned int slot,
@@ -178,6 +323,25 @@ void litest_tablet_motion(struct litest_device *d,
int x, int y,
struct axis_replacement *axes);
+void litest_hover_start(struct litest_device *d,
+ unsigned int slot,
+ double x,
+ double y);
+void litest_hover_end(struct litest_device *d, unsigned int slot);
+void litest_hover_move(struct litest_device *d,
+ unsigned int slot,
+ double x,
+ double y);
+void litest_hover_move_to(struct litest_device *d,
+ unsigned int slot,
+ double x_from, double y_from,
+ double x_to, double y_to,
+ int steps, int sleep_ms);
+void litest_hover_move_two_touches(struct litest_device *d,
+ double x0, double y0,
+ double x1, double y1,
+ double dx, double dy,
+ int steps, int sleep_ms);
void litest_button_click(struct litest_device *d,
unsigned int button,
bool is_press);
@@ -191,6 +355,23 @@ void litest_wait_for_event(struct libinput *li);
void litest_wait_for_event_of_type(struct libinput *li, ...);
void litest_drain_events(struct libinput *li);
void litest_assert_empty_queue(struct libinput *li);
+struct libinput_event_pointer * litest_is_button_event(
+ struct libinput_event *event,
+ unsigned int button,
+ enum libinput_button_state state);
+struct libinput_event_pointer * litest_is_axis_event(
+ struct libinput_event *event,
+ enum libinput_pointer_axis axis,
+ enum libinput_pointer_axis_source source);
+struct libinput_event_pointer * litest_is_motion_event(
+ struct libinput_event *event);
+struct libinput_event_touch * litest_is_touch_event(
+ struct libinput_event *event,
+ enum libinput_event_type type);
+struct libinput_event_keyboard * litest_is_keyboard_event(
+ struct libinput_event *event,
+ unsigned int key,
+ enum libinput_key_state state);
void litest_assert_button_event(struct libinput *li,
unsigned int button,
enum libinput_button_state state);
@@ -229,10 +410,12 @@ struct libevdev_uinput * litest_create_uinput_abs_device(const char *name,
ck_assert_int_ge((int)((a_) * 256), (int)((b_) * 256))
void litest_timeout_tap(void);
+void litest_timeout_tapndrag(void);
void litest_timeout_softbuttons(void);
void litest_timeout_buttonscroll(void);
void litest_timeout_edgescroll(void);
void litest_timeout_finger_switch(void);
+void litest_timeout_middlebutton(void);
void litest_push_event_frame(struct litest_device *dev);
void litest_pop_event_frame(struct litest_device *dev);
diff --git a/test/log.c b/test/log.c
index a56af151..2ab3d4d8 100644
--- a/test/log.c
+++ b/test/log.c
@@ -26,7 +26,6 @@
#include
#include
#include
-#include
#include
#include "litest.h"
@@ -58,8 +57,8 @@ simple_log_handler(struct libinput *libinput,
{
log_handler_called++;
if (log_handler_context)
- ck_assert(libinput == log_handler_context);
- ck_assert(format != NULL);
+ litest_assert_ptr_eq(libinput, log_handler_context);
+ litest_assert_notnull(format);
}
START_TEST(log_default_priority)
@@ -140,11 +139,11 @@ START_TEST(log_priority)
}
END_TEST
-int main (int argc, char **argv) {
+void
+litest_setup_tests(void)
+{
litest_add_no_device("log:defaults", log_default_priority);
litest_add_no_device("log:logging", log_handler_invoked);
litest_add_no_device("log:logging", log_handler_NULL);
litest_add_no_device("log:logging", log_priority);
-
- return litest_run(argc, argv);
}
diff --git a/test/misc.c b/test/misc.c
index 0b6532ca..39683c80 100644
--- a/test/misc.c
+++ b/test/misc.c
@@ -65,7 +65,7 @@ create_simple_test_device(const char *name, ...)
};
evdev = libevdev_new();
- ck_assert(evdev != NULL);
+ litest_assert_notnull(evdev);
libevdev_set_name(evdev, name);
va_start(args, name);
@@ -83,7 +83,7 @@ create_simple_test_device(const char *name, ...)
rc = libevdev_uinput_create_from_device(evdev,
LIBEVDEV_UINPUT_OPEN_MANAGED,
&uinput);
- ck_assert_int_eq(rc, 0);
+ litest_assert_int_eq(rc, 0);
libevdev_free(evdev);
return uinput;
@@ -595,7 +595,35 @@ START_TEST(wheel_click_parser)
}
END_TEST
-int main (int argc, char **argv) {
+struct parser_test_float {
+ char *tag;
+ double expected_value;
+};
+
+START_TEST(trackpoint_accel_parser)
+{
+ struct parser_test_float tests[] = {
+ { "0.5", 0.5 },
+ { "1.0", 1.0 },
+ { "2.0", 2.0 },
+ { "fail1.0", 0.0 },
+ { "1.0fail", 0.0 },
+ { "0,5", 0.0 },
+ { NULL, 0.0 }
+ };
+ int i;
+ double accel;
+
+ for (i = 0; tests[i].tag != NULL; i++) {
+ accel = parse_trackpoint_accel_property(tests[i].tag);
+ ck_assert(accel == tests[i].expected_value);
+ }
+}
+END_TEST
+
+void
+litest_setup_tests(void)
+{
litest_add_no_device("events:conversion", event_conversion_device_notify);
litest_add_for_device("events:conversion", event_conversion_pointer, LITEST_MOUSE);
litest_add_for_device("events:conversion", event_conversion_pointer, LITEST_MOUSE);
@@ -611,6 +639,5 @@ int main (int argc, char **argv) {
litest_add_no_device("misc:ratelimit", ratelimit_helpers);
litest_add_no_device("misc:dpi parser", dpi_parser);
litest_add_no_device("misc:wheel click parser", wheel_click_parser);
-
- return litest_run(argc, argv);
+ litest_add_no_device("misc:trackpoint accel parser", trackpoint_accel_parser);
}
diff --git a/test/path.c b/test/path.c
index 64c5d7f4..c28c6ff4 100644
--- a/test/path.c
+++ b/test/path.c
@@ -26,7 +26,6 @@
#include
#include
#include
-#include
#include
#include "litest.h"
@@ -875,8 +874,8 @@ START_TEST(path_seat_recycle)
}
END_TEST
-int
-main(int argc, char **argv)
+void
+litest_setup_tests(void)
{
litest_add_no_device("path:create", path_create_NULL);
litest_add_no_device("path:create", path_create_invalid);
@@ -897,6 +896,4 @@ main(int argc, char **argv)
litest_add_for_device("path:device events", path_remove_device, LITEST_SYNAPTICS_CLICKPAD);
litest_add_for_device("path:device events", path_double_remove_device, LITEST_SYNAPTICS_CLICKPAD);
litest_add_no_device("path:seat", path_seat_recycle);
-
- return litest_run(argc, argv);
}
diff --git a/test/pointer.c b/test/pointer.c
index 745af638..2918754a 100644
--- a/test/pointer.c
+++ b/test/pointer.c
@@ -42,12 +42,7 @@ get_accelerated_motion_event(struct libinput *li)
while (1) {
event = libinput_get_event(li);
- ck_assert_notnull(event);
- ck_assert_int_eq(libinput_event_get_type(event),
- LIBINPUT_EVENT_POINTER_MOTION);
-
- ptrev = libinput_event_get_pointer_event(event);
- ck_assert_notnull(ptrev);
+ ptrev = litest_is_motion_event(event);
if (fabs(libinput_event_pointer_get_dx(ptrev)) < DBL_MIN &&
fabs(libinput_event_pointer_get_dy(ptrev)) < DBL_MIN) {
@@ -58,7 +53,7 @@ get_accelerated_motion_event(struct libinput *li)
return ptrev;
}
- ck_abort_msg("No accelerated pointer motion event found");
+ litest_abort_msg("No accelerated pointer motion event found");
return NULL;
}
@@ -95,17 +90,31 @@ test_relative_event(struct litest_device *dev, int dx, int dy)
actual_dir = atan2(ev_dx, ev_dy);
/* Check the length of the motion vector (tolerate 1.0 indifference). */
- ck_assert(fabs(expected_length) >= actual_length);
+ litest_assert(fabs(expected_length) >= actual_length);
/* Check the direction of the motion vector (tolerate 2π/4 radians
* indifference). */
- ck_assert(fabs(expected_dir - actual_dir) < M_PI_2);
+ litest_assert(fabs(expected_dir - actual_dir) < M_PI_2);
libinput_event_destroy(libinput_event_pointer_get_base_event(ptrev));
litest_drain_events(dev->libinput);
}
+static void
+disable_button_scrolling(struct litest_device *device)
+{
+ struct libinput_device *dev = device->libinput_device;
+ enum libinput_config_status status,
+ expected;
+
+ status = libinput_device_config_scroll_set_method(dev,
+ LIBINPUT_CONFIG_SCROLL_NO_SCROLL);
+
+ expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
+ litest_assert_int_eq(status, expected);
+}
+
START_TEST(pointer_motion_relative)
{
struct litest_device *dev = litest_current_device();
@@ -131,21 +140,22 @@ test_absolute_event(struct litest_device *dev, double x, double y)
struct libinput_event *event;
struct libinput_event_pointer *ptrev;
double ex, ey;
+ enum libinput_event_type type = LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE;
litest_touch_down(dev, 0, x, y);
libinput_dispatch(li);
event = libinput_get_event(li);
- ck_assert_int_eq(libinput_event_get_type(event),
- LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE);
+ litest_assert_notnull(event);
+ litest_assert_int_eq(libinput_event_get_type(event), type);
ptrev = libinput_event_get_pointer_event(event);
- ck_assert(ptrev != NULL);
+ litest_assert(ptrev != NULL);
ex = libinput_event_pointer_get_absolute_x_transformed(ptrev, 100);
ey = libinput_event_pointer_get_absolute_y_transformed(ptrev, 100);
- ck_assert_int_eq(ex + 0.5, x);
- ck_assert_int_eq(ey + 0.5, y);
+ litest_assert_int_eq((int)(ex + 0.5), (int)x);
+ litest_assert_int_eq((int)(ey + 0.5), (int)y);
libinput_event_destroy(event);
}
@@ -162,6 +172,61 @@ START_TEST(pointer_motion_absolute)
}
END_TEST
+START_TEST(pointer_absolute_initial_state)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *libinput1, *libinput2;
+ struct libinput_event *ev1, *ev2;
+ struct libinput_event_pointer *p1, *p2;
+ int axis = _i; /* looped test */
+
+ dev = litest_current_device();
+ libinput1 = dev->libinput;
+ litest_touch_down(dev, 0, 40, 60);
+ litest_touch_up(dev, 0);
+
+ /* device is now on some x/y value */
+ litest_drain_events(libinput1);
+
+ libinput2 = litest_create_context();
+ libinput_path_add_device(libinput2,
+ libevdev_uinput_get_devnode(dev->uinput));
+ litest_drain_events(libinput2);
+
+ if (axis == ABS_X)
+ litest_touch_down(dev, 0, 40, 70);
+ else
+ litest_touch_down(dev, 0, 70, 60);
+ litest_touch_up(dev, 0);
+
+ litest_wait_for_event(libinput1);
+ litest_wait_for_event(libinput2);
+
+ while (libinput_next_event_type(libinput1)) {
+ ev1 = libinput_get_event(libinput1);
+ ev2 = libinput_get_event(libinput2);
+
+ ck_assert_int_eq(libinput_event_get_type(ev1),
+ LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE);
+ ck_assert_int_eq(libinput_event_get_type(ev1),
+ libinput_event_get_type(ev2));
+
+ p1 = libinput_event_get_pointer_event(ev1);
+ p2 = libinput_event_get_pointer_event(ev2);
+
+ ck_assert_int_eq(libinput_event_pointer_get_absolute_x(p1),
+ libinput_event_pointer_get_absolute_x(p2));
+ ck_assert_int_eq(libinput_event_pointer_get_absolute_y(p1),
+ libinput_event_pointer_get_absolute_y(p2));
+
+ libinput_event_destroy(ev1);
+ libinput_event_destroy(ev2);
+ }
+
+ libinput_unref(libinput2);
+}
+END_TEST
+
static void
test_unaccel_event(struct litest_device *dev, int dx, int dy)
{
@@ -177,18 +242,13 @@ test_unaccel_event(struct litest_device *dev, int dx, int dy)
libinput_dispatch(li);
event = libinput_get_event(li);
- ck_assert_notnull(event);
- ck_assert_int_eq(libinput_event_get_type(event),
- LIBINPUT_EVENT_POINTER_MOTION);
-
- ptrev = libinput_event_get_pointer_event(event);
- ck_assert(ptrev != NULL);
+ ptrev = litest_is_motion_event(event);
ev_dx = libinput_event_pointer_get_dx_unaccelerated(ptrev);
ev_dy = libinput_event_pointer_get_dy_unaccelerated(ptrev);
- ck_assert_int_eq(dx, ev_dx);
- ck_assert_int_eq(dy, ev_dy);
+ litest_assert_int_eq(dx, ev_dx);
+ litest_assert_int_eq(dy, ev_dy);
libinput_event_destroy(event);
@@ -230,6 +290,8 @@ START_TEST(pointer_button)
{
struct litest_device *dev = litest_current_device();
+ disable_button_scrolling(dev);
+
litest_drain_events(dev->libinput);
test_button_event(dev, BTN_LEFT, 1);
@@ -245,8 +307,7 @@ START_TEST(pointer_button)
}
/* Skip middle button test on trackpoints (used for scrolling) */
- if (!libevdev_has_property(dev->evdev, INPUT_PROP_POINTING_STICK) &&
- libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) {
+ if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) {
test_button_event(dev, BTN_MIDDLE, 1);
test_button_event(dev, BTN_MIDDLE, 0);
}
@@ -369,24 +430,18 @@ test_wheel_event(struct litest_device *dev, int which, int amount)
libinput_dispatch(li);
- event = libinput_get_event(li);
- ck_assert(event != NULL);
- ck_assert_int_eq(libinput_event_get_type(event),
- LIBINPUT_EVENT_POINTER_AXIS);
-
- ptrev = libinput_event_get_pointer_event(event);
- ck_assert(ptrev != NULL);
-
axis = (which == REL_WHEEL) ?
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL :
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
+ event = libinput_get_event(li);
+ ptrev = litest_is_axis_event(event,
+ axis,
+ LIBINPUT_POINTER_AXIS_SOURCE_WHEEL);
- ck_assert_int_eq(libinput_event_pointer_get_axis_value(ptrev, axis),
+ litest_assert_int_eq(libinput_event_pointer_get_axis_value(ptrev, axis),
expected);
- ck_assert_int_eq(libinput_event_pointer_get_axis_source(ptrev),
- LIBINPUT_POINTER_AXIS_SOURCE_WHEEL);
- ck_assert_int_eq(libinput_event_pointer_get_axis_value_discrete(ptrev, axis),
- discrete);
+ litest_assert_int_eq(libinput_event_pointer_get_axis_value_discrete(ptrev, axis),
+ discrete);
libinput_event_destroy(event);
}
@@ -396,11 +451,17 @@ START_TEST(pointer_scroll_wheel)
litest_drain_events(dev->libinput);
- test_wheel_event(dev, REL_WHEEL, -1);
- test_wheel_event(dev, REL_WHEEL, 1);
+ /* make sure we hit at least one of the below two conditions */
+ ck_assert(libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL) ||
+ libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL));
- test_wheel_event(dev, REL_WHEEL, -5);
- test_wheel_event(dev, REL_WHEEL, 6);
+ if (libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL)) {
+ test_wheel_event(dev, REL_WHEEL, -1);
+ test_wheel_event(dev, REL_WHEEL, 1);
+
+ test_wheel_event(dev, REL_WHEEL, -5);
+ test_wheel_event(dev, REL_WHEEL, 6);
+ }
if (libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL)) {
test_wheel_event(dev, REL_HWHEEL, -1);
@@ -446,11 +507,17 @@ START_TEST(pointer_scroll_natural_wheel)
libinput_device_config_scroll_set_natural_scroll_enabled(device, 1);
- test_wheel_event(dev, REL_WHEEL, -1);
- test_wheel_event(dev, REL_WHEEL, 1);
+ /* make sure we hit at least one of the below two conditions */
+ ck_assert(libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL) ||
+ libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL));
- test_wheel_event(dev, REL_WHEEL, -5);
- test_wheel_event(dev, REL_WHEEL, 6);
+ if (libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL)) {
+ test_wheel_event(dev, REL_WHEEL, -1);
+ test_wheel_event(dev, REL_WHEEL, 1);
+
+ test_wheel_event(dev, REL_WHEEL, -5);
+ test_wheel_event(dev, REL_WHEEL, 6);
+ }
if (libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL)) {
test_wheel_event(dev, REL_HWHEEL, -1);
@@ -732,6 +799,26 @@ START_TEST(pointer_scroll_button)
}
END_TEST
+START_TEST(pointer_scroll_nowheel_defaults)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+ enum libinput_config_scroll_method method;
+ uint32_t button;
+
+ method = libinput_device_config_scroll_get_method(device);
+ ck_assert_int_eq(method, LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
+
+ method = libinput_device_config_scroll_get_default_method(device);
+ ck_assert_int_eq(method, LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
+
+ button = libinput_device_config_scroll_get_button(device);
+ ck_assert_int_eq(button, BTN_MIDDLE);
+ button = libinput_device_config_scroll_get_default_button(device);
+ ck_assert_int_eq(button, BTN_MIDDLE);
+}
+END_TEST
+
START_TEST(pointer_accel_defaults)
{
struct litest_device *dev = litest_current_device();
@@ -812,6 +899,17 @@ START_TEST(pointer_accel_defaults_absolute)
}
END_TEST
+START_TEST(pointer_accel_defaults_absolute_relative)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+
+ ck_assert(libinput_device_config_accel_is_available(device));
+ ck_assert(libinput_device_config_accel_get_default_speed(device) == 0.0);
+ ck_assert(libinput_device_config_accel_get_speed(device) == 0.0);
+}
+END_TEST
+
START_TEST(pointer_accel_direction_change)
{
struct litest_device *dev = litest_current_device();
@@ -854,19 +952,389 @@ START_TEST(pointer_accel_direction_change)
}
END_TEST
-int main (int argc, char **argv) {
+START_TEST(middlebutton)
+{
+ struct litest_device *device = litest_current_device();
+ struct libinput *li = device->libinput;
+ enum libinput_config_status status;
+ unsigned int i;
+ const int btn[][4] = {
+ { BTN_LEFT, BTN_RIGHT, BTN_LEFT, BTN_RIGHT },
+ { BTN_LEFT, BTN_RIGHT, BTN_RIGHT, BTN_LEFT },
+ { BTN_RIGHT, BTN_LEFT, BTN_LEFT, BTN_RIGHT },
+ { BTN_RIGHT, BTN_LEFT, BTN_RIGHT, BTN_LEFT },
+ };
+
+ disable_button_scrolling(device);
+
+ status = libinput_device_config_middle_emulation_set_enabled(
+ device->libinput_device,
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+ if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
+ return;
+
+ litest_drain_events(li);
+
+ for (i = 0; i < ARRAY_LENGTH(btn); i++) {
+ litest_button_click(device, btn[i][0], true);
+ litest_button_click(device, btn[i][1], true);
+
+ litest_assert_button_event(li,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ litest_assert_empty_queue(li);
+
+ litest_button_click(device, btn[i][2], false);
+ litest_button_click(device, btn[i][3], false);
+ litest_assert_button_event(li,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ litest_assert_empty_queue(li);
+ }
+}
+END_TEST
+
+START_TEST(middlebutton_timeout)
+{
+ struct litest_device *device = litest_current_device();
+ struct libinput *li = device->libinput;
+ enum libinput_config_status status;
+ unsigned int button;
+
+ disable_button_scrolling(device);
+
+ status = libinput_device_config_middle_emulation_set_enabled(
+ device->libinput_device,
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+ if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
+ return;
+
+ for (button = BTN_LEFT; button <= BTN_RIGHT; button++) {
+ litest_drain_events(li);
+ litest_button_click(device, button, true);
+ litest_assert_empty_queue(li);
+ litest_timeout_middlebutton();
+
+ litest_assert_button_event(li,
+ button,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+
+ litest_button_click(device, button, false);
+ litest_assert_button_event(li,
+ button,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ litest_assert_empty_queue(li);
+ }
+}
+END_TEST
+
+START_TEST(middlebutton_doubleclick)
+{
+ struct litest_device *device = litest_current_device();
+ struct libinput *li = device->libinput;
+ enum libinput_config_status status;
+ unsigned int i;
+ const int btn[][4] = {
+ { BTN_LEFT, BTN_RIGHT, BTN_LEFT, BTN_RIGHT },
+ { BTN_LEFT, BTN_RIGHT, BTN_RIGHT, BTN_LEFT },
+ { BTN_RIGHT, BTN_LEFT, BTN_LEFT, BTN_RIGHT },
+ { BTN_RIGHT, BTN_LEFT, BTN_RIGHT, BTN_LEFT },
+ };
+
+ disable_button_scrolling(device);
+
+ status = libinput_device_config_middle_emulation_set_enabled(
+ device->libinput_device,
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+ if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
+ return;
+
+ litest_drain_events(li);
+
+ for (i = 0; i < ARRAY_LENGTH(btn); i++) {
+ litest_button_click(device, btn[i][0], true);
+ litest_button_click(device, btn[i][1], true);
+ litest_assert_button_event(li,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ litest_assert_empty_queue(li);
+
+ litest_button_click(device, btn[i][2], false);
+ litest_button_click(device, btn[i][2], true);
+ litest_assert_button_event(li,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ litest_assert_button_event(li,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ litest_button_click(device, btn[i][3], false);
+
+ litest_assert_button_event(li,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ litest_assert_empty_queue(li);
+ }
+}
+END_TEST
+
+START_TEST(middlebutton_middleclick)
+{
+ struct litest_device *device = litest_current_device();
+ struct libinput *li = device->libinput;
+ enum libinput_config_status status;
+ unsigned int button;
+
+ disable_button_scrolling(device);
+
+ if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE))
+ return;
+
+ status = libinput_device_config_middle_emulation_set_enabled(
+ device->libinput_device,
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+ if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
+ return;
+
+ /* one button down, then middle -> release buttons */
+ for (button = BTN_LEFT; button <= BTN_RIGHT; button++) {
+ /* release button before middle */
+ litest_drain_events(li);
+ litest_button_click(device, button, true);
+ litest_button_click(device, BTN_MIDDLE, true);
+ litest_assert_button_event(li,
+ button,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ litest_assert_button_event(li,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ litest_assert_empty_queue(li);
+ litest_button_click(device, button, false);
+ litest_assert_button_event(li,
+ button,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ litest_button_click(device, BTN_MIDDLE, false);
+ litest_assert_button_event(li,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ litest_assert_empty_queue(li);
+
+ /* release middle before button */
+ litest_button_click(device, button, true);
+ litest_button_click(device, BTN_MIDDLE, true);
+ litest_assert_button_event(li,
+ button,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ litest_assert_button_event(li,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ litest_assert_empty_queue(li);
+ litest_button_click(device, BTN_MIDDLE, false);
+ litest_assert_button_event(li,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ litest_button_click(device, button, false);
+ litest_assert_button_event(li,
+ button,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ litest_assert_empty_queue(li);
+ }
+}
+END_TEST
+
+START_TEST(middlebutton_middleclick_during)
+{
+ struct litest_device *device = litest_current_device();
+ struct libinput *li = device->libinput;
+ enum libinput_config_status status;
+ unsigned int button;
+
+ disable_button_scrolling(device);
+
+ if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE))
+ return;
+
+ status = libinput_device_config_middle_emulation_set_enabled(
+ device->libinput_device,
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+ if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
+ return;
+
+ litest_drain_events(li);
+
+ /* trigger emulation, then real middle */
+ for (button = BTN_LEFT; button <= BTN_RIGHT; button++) {
+ litest_button_click(device, BTN_LEFT, true);
+ litest_button_click(device, BTN_RIGHT, true);
+
+ litest_assert_button_event(li,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+
+ litest_button_click(device, BTN_MIDDLE, true);
+ litest_assert_button_event(li,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ litest_assert_button_event(li,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+
+ litest_assert_empty_queue(li);
+
+ /* middle still down, release left/right */
+ litest_button_click(device, button, false);
+ litest_assert_empty_queue(li);
+ litest_button_click(device, button, true);
+ litest_assert_button_event(li,
+ button,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ litest_assert_empty_queue(li);
+
+ /* release both */
+ litest_button_click(device, BTN_LEFT, false);
+ litest_button_click(device, BTN_RIGHT, false);
+ litest_assert_button_event(li,
+ button,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ litest_assert_empty_queue(li);
+
+ litest_button_click(device, BTN_MIDDLE, false);
+ litest_assert_button_event(li,
+ BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ litest_assert_empty_queue(li);
+ }
+}
+END_TEST
+
+START_TEST(middlebutton_default_enabled)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+ enum libinput_config_status status;
+ int available;
+ enum libinput_config_middle_emulation_state deflt, state;
+
+ available = libinput_device_config_middle_emulation_is_available(device);
+ ck_assert(available);
+
+ if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE))
+ deflt = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
+ else
+ deflt = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED;
+
+ state = libinput_device_config_middle_emulation_get_enabled(device);
+ ck_assert_int_eq(state, deflt);
+
+ state = libinput_device_config_middle_emulation_get_default_enabled(
+ device);
+ ck_assert_int_eq(state, deflt);
+
+ status = libinput_device_config_middle_emulation_set_enabled(device,
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+
+ status = libinput_device_config_middle_emulation_set_enabled(device,
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+
+ status = libinput_device_config_middle_emulation_set_enabled(device, 3);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
+}
+END_TEST
+
+START_TEST(middlebutton_default_clickpad)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+ enum libinput_config_status status;
+ enum libinput_config_middle_emulation_state state;
+ int available;
+
+ available = libinput_device_config_middle_emulation_is_available(device);
+ ck_assert(!available);
+
+ state = libinput_device_config_middle_emulation_get_enabled(device);
+ ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
+ state = libinput_device_config_middle_emulation_get_default_enabled(
+ device);
+ ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
+
+ status = libinput_device_config_middle_emulation_set_enabled(device,
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+
+ status = libinput_device_config_middle_emulation_set_enabled(device,
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+
+ status = libinput_device_config_middle_emulation_set_enabled(device, 3);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
+}
+END_TEST
+
+START_TEST(middlebutton_default_touchpad)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+ enum libinput_config_middle_emulation_state state;
+ int available;
+
+ available = libinput_device_config_middle_emulation_is_available(device);
+ ck_assert(!available);
+
+ if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE))
+ return;
+
+ state = libinput_device_config_middle_emulation_get_enabled(
+ device);
+ ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
+ state = libinput_device_config_middle_emulation_get_default_enabled(
+ device);
+ ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
+}
+END_TEST
+
+START_TEST(middlebutton_default_disabled)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+ enum libinput_config_middle_emulation_state state;
+ enum libinput_config_status status;
+ int available;
+
+ available = libinput_device_config_middle_emulation_is_available(device);
+ ck_assert(!available);
+ state = libinput_device_config_middle_emulation_get_enabled(device);
+ ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
+ state = libinput_device_config_middle_emulation_get_default_enabled(
+ device);
+ ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
+ status = libinput_device_config_middle_emulation_set_enabled(device,
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+ status = libinput_device_config_middle_emulation_set_enabled(device,
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+}
+END_TEST
+
+void
+litest_setup_tests(void)
+{
+ struct range axis_range = {ABS_X, ABS_Y + 1};
litest_add("pointer:motion", pointer_motion_relative, LITEST_RELATIVE, LITEST_ANY);
litest_add("pointer:motion", pointer_motion_absolute, LITEST_ABSOLUTE, LITEST_ANY);
litest_add("pointer:motion", pointer_motion_unaccel, LITEST_RELATIVE, LITEST_ANY);
litest_add("pointer:button", pointer_button, LITEST_BUTTON, LITEST_CLICKPAD);
- litest_add_no_device("pointer:button_auto_release", pointer_button_auto_release);
+ litest_add_no_device("pointer:button", pointer_button_auto_release);
+ litest_add_no_device("pointer:button", pointer_seat_button_count);
litest_add("pointer:scroll", pointer_scroll_wheel, LITEST_WHEEL, LITEST_ANY);
litest_add("pointer:scroll", pointer_scroll_button, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY);
+ litest_add("pointer:scroll", pointer_scroll_nowheel_defaults, LITEST_RELATIVE|LITEST_BUTTON, LITEST_WHEEL);
litest_add("pointer:scroll", pointer_scroll_natural_defaults, LITEST_WHEEL, LITEST_ANY);
litest_add("pointer:scroll", pointer_scroll_natural_enable_config, LITEST_WHEEL, LITEST_ANY);
litest_add("pointer:scroll", pointer_scroll_natural_wheel, LITEST_WHEEL, LITEST_ANY);
- litest_add_no_device("pointer:seat button count", pointer_seat_button_count);
litest_add("pointer:calibration", pointer_no_calibration, LITEST_ANY, LITEST_TOUCH|LITEST_SINGLE_TOUCH|LITEST_ABSOLUTE|LITEST_PROTOCOL_A);
@@ -878,8 +1346,19 @@ int main (int argc, char **argv) {
litest_add("pointer:accel", pointer_accel_defaults, LITEST_RELATIVE, LITEST_ANY);
litest_add("pointer:accel", pointer_accel_invalid, LITEST_RELATIVE, LITEST_ANY);
- litest_add("pointer:accel", pointer_accel_defaults_absolute, LITEST_ABSOLUTE, LITEST_ANY);
+ litest_add("pointer:accel", pointer_accel_defaults_absolute, LITEST_ABSOLUTE, LITEST_RELATIVE);
+ litest_add("pointer:accel", pointer_accel_defaults_absolute_relative, LITEST_ABSOLUTE|LITEST_RELATIVE, LITEST_ANY);
litest_add("pointer:accel", pointer_accel_direction_change, LITEST_RELATIVE, LITEST_ANY);
- return litest_run(argc, argv);
+ litest_add("pointer:middlebutton", middlebutton, LITEST_BUTTON, LITEST_ANY);
+ litest_add("pointer:middlebutton", middlebutton_timeout, LITEST_BUTTON, LITEST_ANY);
+ litest_add("pointer:middlebutton", middlebutton_doubleclick, LITEST_BUTTON, LITEST_ANY);
+ litest_add("pointer:middlebutton", middlebutton_middleclick, LITEST_BUTTON, LITEST_ANY);
+ litest_add("pointer:middlebutton", middlebutton_middleclick_during, LITEST_BUTTON, LITEST_ANY);
+ litest_add("pointer:middlebutton", middlebutton_default_enabled, LITEST_BUTTON, LITEST_TOUCHPAD|LITEST_POINTINGSTICK);
+ litest_add("pointer:middlebutton", middlebutton_default_clickpad, LITEST_CLICKPAD, LITEST_ANY);
+ litest_add("pointer:middlebutton", middlebutton_default_touchpad, LITEST_TOUCHPAD, LITEST_CLICKPAD);
+ litest_add("pointer:middlebutton", middlebutton_default_disabled, LITEST_ANY, LITEST_BUTTON);
+
+ litest_add_ranged("pointer:state", pointer_absolute_initial_state, LITEST_ABSOLUTE, LITEST_ANY, &axis_range);
}
diff --git a/test/tablet.c b/test/tablet.c
index ba61e0e0..a4bf6b2b 100644
--- a/test/tablet.c
+++ b/test/tablet.c
@@ -1662,8 +1662,8 @@ START_TEST(artpen_rotation)
}
END_TEST
-int
-main(int argc, char **argv)
+void
+litest_setup_tests(void)
{
litest_add("tablet:tool", tool_ref, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY);
litest_add_no_device("tablet:tool", tool_capabilities);
@@ -1692,6 +1692,4 @@ main(int argc, char **argv)
litest_add("tablet:airbrush", airbrush_wheel, LITEST_TABLET, LITEST_ANY);
litest_add("tablet:artpen", artpen_tool, LITEST_TABLET, LITEST_ANY);
litest_add("tablet:artpen", artpen_rotation, LITEST_TABLET, LITEST_ANY);
-
- return litest_run(argc, argv);
}
diff --git a/test/touch.c b/test/touch.c
index ac97c52b..2c07e090 100644
--- a/test/touch.c
+++ b/test/touch.c
@@ -182,6 +182,9 @@ START_TEST(touch_double_touch_down_up)
dev = litest_current_device();
libinput = dev->libinput;
+ /* note: this test is a false negative, libevdev will filter
+ * tracking IDs re-used in the same slot. */
+
litest_touch_down(dev, 0, 0, 0);
litest_touch_down(dev, 0, 0, 0);
litest_touch_up(dev, 0);
@@ -241,9 +244,7 @@ START_TEST(touch_calibration_scale)
litest_wait_for_event(li);
ev = libinput_get_event(li);
- ck_assert_int_eq(libinput_event_get_type(ev),
- LIBINPUT_EVENT_TOUCH_DOWN);
- tev = libinput_event_get_touch_event(ev);
+ tev = litest_is_touch_event(ev, LIBINPUT_EVENT_TOUCH_DOWN);
x = libinput_event_touch_get_x_transformed(tev, width);
y = libinput_event_touch_get_y_transformed(tev, height);
@@ -312,9 +313,7 @@ START_TEST(touch_calibration_rotation)
litest_touch_up(dev, 0);
litest_wait_for_event(li);
ev = libinput_get_event(li);
- ck_assert_int_eq(libinput_event_get_type(ev),
- LIBINPUT_EVENT_TOUCH_DOWN);
- tev = libinput_event_get_touch_event(ev);
+ tev = litest_is_touch_event(ev, LIBINPUT_EVENT_TOUCH_DOWN);
x = libinput_event_touch_get_x_transformed(tev, width);
y = libinput_event_touch_get_y_transformed(tev, height);
@@ -378,9 +377,7 @@ START_TEST(touch_calibration_translation)
litest_wait_for_event(li);
ev = libinput_get_event(li);
- ck_assert_int_eq(libinput_event_get_type(ev),
- LIBINPUT_EVENT_TOUCH_DOWN);
- tev = libinput_event_get_touch_event(ev);
+ tev = litest_is_touch_event(ev, LIBINPUT_EVENT_TOUCH_DOWN);
x = libinput_event_touch_get_x_transformed(tev, width);
y = libinput_event_touch_get_y_transformed(tev, height);
@@ -439,6 +436,8 @@ START_TEST(fake_mt_exists)
* have different capabilities */
ck_assert(libinput_device_has_capability(device,
LIBINPUT_DEVICE_CAP_POINTER));
+
+ libinput_event_destroy(event);
}
END_TEST
@@ -460,7 +459,8 @@ START_TEST(fake_mt_no_touch_events)
litest_touch_up(dev, 0);
litest_touch_up(dev, 1);
- litest_assert_empty_queue(li);
+ litest_assert_only_typed_events(li,
+ LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE);
}
END_TEST
@@ -493,7 +493,7 @@ START_TEST(touch_protocol_a_touch)
litest_wait_for_event_of_type(li, LIBINPUT_EVENT_TOUCH_DOWN, -1);
ev = libinput_get_event(li);
- tev = libinput_event_get_touch_event(ev);
+ tev = litest_is_touch_event(ev, LIBINPUT_EVENT_TOUCH_DOWN);
oldx = libinput_event_touch_get_x(tev);
oldy = libinput_event_touch_get_y(tev);
@@ -582,9 +582,79 @@ START_TEST(touch_protocol_a_2fg_touch)
}
END_TEST
-int
-main(int argc, char **argv)
+START_TEST(touch_initial_state)
{
+ struct litest_device *dev;
+ struct libinput *libinput1, *libinput2;
+ struct libinput_event *ev1, *ev2;
+ struct libinput_event_touch *t1, *t2;
+ struct libinput_device *device1, *device2;
+ int axis = _i; /* looped test */
+
+ dev = litest_current_device();
+ device1 = dev->libinput_device;
+ libinput_device_config_tap_set_enabled(device1,
+ LIBINPUT_CONFIG_TAP_DISABLED);
+
+ libinput1 = dev->libinput;
+ litest_touch_down(dev, 0, 40, 60);
+ litest_touch_up(dev, 0);
+
+ /* device is now on some x/y value */
+ litest_drain_events(libinput1);
+
+ libinput2 = litest_create_context();
+ device2 = libinput_path_add_device(libinput2,
+ libevdev_uinput_get_devnode(
+ dev->uinput));
+ libinput_device_config_tap_set_enabled(device2,
+ LIBINPUT_CONFIG_TAP_DISABLED);
+ litest_drain_events(libinput2);
+
+ if (axis == ABS_X)
+ litest_touch_down(dev, 0, 40, 70);
+ else
+ litest_touch_down(dev, 0, 70, 60);
+ litest_touch_up(dev, 0);
+
+ litest_wait_for_event(libinput1);
+ litest_wait_for_event(libinput2);
+
+ while (libinput_next_event_type(libinput1)) {
+ ev1 = libinput_get_event(libinput1);
+ ev2 = libinput_get_event(libinput2);
+
+ t1 = litest_is_touch_event(ev1, 0);
+ t2 = litest_is_touch_event(ev2, 0);
+
+ ck_assert_int_eq(libinput_event_get_type(ev1),
+ libinput_event_get_type(ev2));
+
+ if (libinput_event_get_type(ev1) == LIBINPUT_EVENT_TOUCH_UP ||
+ libinput_event_get_type(ev1) == LIBINPUT_EVENT_TOUCH_FRAME)
+ break;
+
+ ck_assert_int_eq(libinput_event_touch_get_x(t1),
+ libinput_event_touch_get_x(t2));
+ ck_assert_int_eq(libinput_event_touch_get_y(t1),
+ libinput_event_touch_get_y(t2));
+
+ libinput_event_destroy(ev1);
+ libinput_event_destroy(ev2);
+ }
+
+ libinput_event_destroy(ev1);
+ libinput_event_destroy(ev2);
+
+ libinput_unref(libinput2);
+}
+END_TEST
+
+void
+litest_setup_tests(void)
+{
+ struct range axes = { ABS_X, ABS_Y + 1};
+
litest_add("touch:frame", touch_frame_events, LITEST_TOUCH, LITEST_ANY);
litest_add_no_device("touch:abs-transform", touch_abs_transform);
litest_add_no_device("touch:many-slots", touch_many_slots);
@@ -605,5 +675,5 @@ main(int argc, char **argv)
litest_add("touch:protocol a", touch_protocol_a_touch, LITEST_PROTOCOL_A, LITEST_ANY);
litest_add("touch:protocol a", touch_protocol_a_2fg_touch, LITEST_PROTOCOL_A, LITEST_ANY);
- return litest_run(argc, argv);
+ litest_add_ranged("touch:state", touch_initial_state, LITEST_TOUCH, LITEST_PROTOCOL_A, &axes);
}
diff --git a/test/touchpad.c b/test/touchpad.c
index 6fa23016..c8ecb322 100644
--- a/test/touchpad.c
+++ b/test/touchpad.c
@@ -123,6 +123,544 @@ START_TEST(touchpad_1fg_tap)
}
END_TEST
+START_TEST(touchpad_1fg_doubletap)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_pointer *ptrev;
+ uint32_t oldtime, curtime;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_up(dev, 0);
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_up(dev, 0);
+ libinput_dispatch(li);
+
+ litest_timeout_tap();
+
+ libinput_dispatch(li);
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ oldtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_le(oldtime, curtime);
+
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_lt(oldtime, curtime);
+ oldtime = curtime;
+
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_le(oldtime, curtime);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_1fg_multitap)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_pointer *ptrev;
+ uint32_t oldtime = 0,
+ curtime;
+ int range = _i, /* looped test */
+ ntaps;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ litest_drain_events(li);
+
+ for (ntaps = 0; ntaps <= range; ntaps++) {
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_up(dev, 0);
+ libinput_dispatch(li);
+ msleep(10);
+ }
+
+ litest_timeout_tap();
+ libinput_dispatch(li);
+
+ for (ntaps = 0; ntaps <= range; ntaps++) {
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_gt(curtime, oldtime);
+
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_ge(curtime, oldtime);
+ oldtime = curtime;
+ }
+ litest_timeout_tap();
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_1fg_multitap_n_drag_move)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_pointer *ptrev;
+ uint32_t oldtime = 0,
+ curtime;
+ int range = _i, /* looped test */
+ ntaps;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ litest_drain_events(li);
+
+ for (ntaps = 0; ntaps <= range; ntaps++) {
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_up(dev, 0);
+ libinput_dispatch(li);
+ msleep(10);
+ }
+
+ libinput_dispatch(li);
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10, 4);
+ libinput_dispatch(li);
+
+ for (ntaps = 0; ntaps <= range; ntaps++) {
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_gt(curtime, oldtime);
+
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_ge(curtime, oldtime);
+ oldtime = curtime;
+ }
+
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_gt(curtime, oldtime);
+
+ litest_assert_only_typed_events(li,
+ LIBINPUT_EVENT_POINTER_MOTION);
+
+ litest_touch_up(dev, 0);
+ litest_assert_button_event(li,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_1fg_multitap_n_drag_2fg)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_pointer *ptrev;
+ uint32_t oldtime = 0,
+ curtime;
+ int range = _i,
+ ntaps;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ litest_drain_events(li);
+
+ for (ntaps = 0; ntaps <= range; ntaps++) {
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_up(dev, 0);
+ libinput_dispatch(li);
+ msleep(10);
+ }
+
+ libinput_dispatch(li);
+ litest_touch_down(dev, 0, 50, 50);
+ msleep(10);
+ litest_touch_down(dev, 1, 70, 50);
+ libinput_dispatch(li);
+
+ for (ntaps = 0; ntaps <= range; ntaps++) {
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_gt(curtime, oldtime);
+
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_ge(curtime, oldtime);
+ oldtime = curtime;
+ }
+
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_gt(curtime, oldtime);
+
+ litest_touch_move_to(dev, 1, 70, 50, 90, 50, 10, 4);
+
+ litest_assert_only_typed_events(li,
+ LIBINPUT_EVENT_POINTER_MOTION);
+
+ litest_touch_up(dev, 1);
+ litest_touch_up(dev, 0);
+ litest_timeout_tap();
+ litest_assert_button_event(li,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_1fg_multitap_n_drag_click)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_pointer *ptrev;
+ uint32_t oldtime = 0,
+ curtime;
+ int range = _i, /* looped test */
+ ntaps;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ litest_drain_events(li);
+
+ for (ntaps = 0; ntaps <= range; ntaps++) {
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_up(dev, 0);
+ libinput_dispatch(li);
+ msleep(10);
+ }
+
+ litest_touch_down(dev, 0, 50, 50);
+ libinput_dispatch(li);
+ litest_button_click(dev, BTN_LEFT, true);
+ litest_button_click(dev, BTN_LEFT, false);
+ libinput_dispatch(li);
+
+ for (ntaps = 0; ntaps <= range; ntaps++) {
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_gt(curtime, oldtime);
+
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_ge(curtime, oldtime);
+ oldtime = curtime;
+ }
+
+ litest_assert_button_event(li,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ litest_assert_button_event(li,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ litest_touch_up(dev, 0);
+ litest_timeout_tap();
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_1fg_multitap_n_drag_timeout)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_pointer *ptrev;
+ uint32_t oldtime = 0,
+ curtime;
+ int range = _i, /* looped test */
+ ntaps;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ litest_drain_events(li);
+
+ for (ntaps = 0; ntaps <= range; ntaps++) {
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_up(dev, 0);
+ libinput_dispatch(li);
+ msleep(10);
+ }
+
+ libinput_dispatch(li);
+ litest_touch_down(dev, 0, 50, 50);
+ libinput_dispatch(li);
+
+ litest_timeout_tap();
+ libinput_dispatch(li);
+
+ for (ntaps = 0; ntaps <= range; ntaps++) {
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_gt(curtime, oldtime);
+
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_ge(curtime, oldtime);
+ oldtime = curtime;
+ }
+
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_gt(curtime, oldtime);
+
+ litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10, 4);
+
+ litest_assert_only_typed_events(li,
+ LIBINPUT_EVENT_POINTER_MOTION);
+
+ litest_touch_up(dev, 0);
+ litest_assert_button_event(li,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_1fg_multitap_n_drag_tap)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_pointer *ptrev;
+ uint32_t oldtime = 0,
+ curtime;
+ int range = _i, /* looped test */
+ ntaps;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ litest_drain_events(li);
+
+ for (ntaps = 0; ntaps <= range; ntaps++) {
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_up(dev, 0);
+ libinput_dispatch(li);
+ msleep(10);
+ }
+
+ libinput_dispatch(li);
+ litest_touch_down(dev, 0, 50, 50);
+ libinput_dispatch(li);
+
+ litest_timeout_tap();
+ libinput_dispatch(li);
+
+ for (ntaps = 0; ntaps <= range; ntaps++) {
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_gt(curtime, oldtime);
+
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_ge(curtime, oldtime);
+ oldtime = curtime;
+ }
+
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_gt(curtime, oldtime);
+
+ litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10, 4);
+
+ litest_assert_only_typed_events(li,
+ LIBINPUT_EVENT_POINTER_MOTION);
+
+ litest_touch_up(dev, 0);
+ litest_touch_down(dev, 0, 70, 50);
+ litest_touch_up(dev, 0);
+ litest_assert_button_event(li,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_1fg_multitap_n_drag_tap_click)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_pointer *ptrev;
+ uint32_t oldtime = 0,
+ curtime;
+ int i, ntaps;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ litest_drain_events(li);
+
+ for (i = 3; i < 5; i++) {
+
+ for (ntaps = 0; ntaps <= i; ntaps++) {
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_up(dev, 0);
+ libinput_dispatch(li);
+ msleep(10);
+ }
+
+ libinput_dispatch(li);
+ litest_touch_down(dev, 0, 50, 50);
+ libinput_dispatch(li);
+
+ litest_timeout_tap();
+ libinput_dispatch(li);
+
+ for (ntaps = 0; ntaps <= i; ntaps++) {
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_gt(curtime, oldtime);
+
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_ge(curtime, oldtime);
+ oldtime = curtime;
+ }
+
+ event = libinput_get_event(li);
+ ptrev = litest_is_button_event(event,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ curtime = libinput_event_pointer_get_time(ptrev);
+ libinput_event_destroy(event);
+ ck_assert_int_gt(curtime, oldtime);
+
+ litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10, 4);
+
+ litest_assert_only_typed_events(li,
+ LIBINPUT_EVENT_POINTER_MOTION);
+
+ litest_touch_up(dev, 0);
+ litest_touch_down(dev, 0, 70, 50);
+ litest_button_click(dev, BTN_LEFT, true);
+ litest_button_click(dev, BTN_LEFT, false);
+ libinput_dispatch(li);
+
+ litest_assert_button_event(li,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+
+ /* the physical click */
+ litest_assert_button_event(li,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ litest_assert_button_event(li,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ litest_touch_up(dev, 0);
+
+ litest_assert_empty_queue(li);
+ }
+}
+END_TEST
+
START_TEST(touchpad_1fg_tap_n_drag)
{
struct litest_device *dev = litest_current_device();
@@ -164,6 +702,101 @@ START_TEST(touchpad_1fg_tap_n_drag)
}
END_TEST
+START_TEST(touchpad_1fg_tap_n_drag_tap)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_up(dev, 0);
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_move_to(dev, 0, 50, 50, 80, 80, 5, 40);
+ litest_touch_up(dev, 0);
+
+ libinput_dispatch(li);
+
+ litest_assert_button_event(li, BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+
+ libinput_dispatch(li);
+
+ litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
+
+ /* lift finger, set down again, should continue dragging */
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_move_to(dev, 0, 50, 50, 80, 80, 5, 40);
+
+ litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
+
+ litest_touch_up(dev, 0);
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_up(dev, 0);
+
+ litest_assert_button_event(li, BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_1fg_tap_n_drag_tap_click)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_up(dev, 0);
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_move_to(dev, 0, 50, 50, 80, 80, 5, 40);
+ litest_touch_up(dev, 0);
+
+ libinput_dispatch(li);
+
+ litest_assert_button_event(li, BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+
+ libinput_dispatch(li);
+
+ litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
+
+ /* lift finger, set down again, should continue dragging */
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_move_to(dev, 0, 50, 50, 80, 80, 5, 40);
+
+ litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
+
+ litest_touch_up(dev, 0);
+ litest_touch_down(dev, 0, 50, 50);
+ litest_button_click(dev, BTN_LEFT, true);
+ litest_button_click(dev, BTN_LEFT, false);
+ libinput_dispatch(li);
+
+ litest_assert_button_event(li, BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+
+ /* the physical click */
+ litest_assert_button_event(li,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ litest_assert_button_event(li,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ litest_touch_up(dev, 0);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
START_TEST(touchpad_1fg_tap_n_drag_timeout)
{
struct litest_device *dev = litest_current_device();
@@ -186,6 +819,7 @@ START_TEST(touchpad_1fg_tap_n_drag_timeout)
litest_assert_empty_queue(li);
litest_touch_up(dev, 0);
+ litest_timeout_tapndrag();
litest_assert_button_event(li, BTN_LEFT,
LIBINPUT_BUTTON_STATE_RELEASED);
@@ -369,6 +1003,38 @@ START_TEST(touchpad_2fg_tap_inverted)
}
END_TEST
+START_TEST(touchpad_2fg_tap_quickrelease)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ litest_drain_events(dev->libinput);
+
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_down(dev, 1, 70, 70);
+ litest_event(dev, EV_ABS, ABS_MT_SLOT, 0);
+ litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ litest_event(dev, EV_ABS, ABS_MT_SLOT, 1);
+ litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
+ litest_event(dev, EV_KEY, BTN_TOUCH, 0);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+
+ libinput_dispatch(li);
+
+ litest_assert_button_event(li, BTN_RIGHT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ litest_timeout_tap();
+ litest_assert_button_event(li, BTN_RIGHT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
START_TEST(touchpad_1fg_tap_click)
{
struct litest_device *dev = litest_current_device();
@@ -705,6 +1371,46 @@ START_TEST(touchpad_3fg_tap)
}
END_TEST
+START_TEST(touchpad_3fg_tap_quickrelease)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ if (libevdev_get_abs_maximum(dev->evdev,
+ ABS_MT_SLOT) <= 2)
+ return;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_down(dev, 1, 70, 50);
+ litest_touch_down(dev, 2, 80, 50);
+ litest_event(dev, EV_ABS, ABS_MT_SLOT, 0);
+ litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ litest_event(dev, EV_ABS, ABS_MT_SLOT, 1);
+ litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ litest_event(dev, EV_ABS, ABS_MT_SLOT, 2);
+ litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 0);
+ litest_event(dev, EV_KEY, BTN_TOUCH, 0);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+
+ libinput_dispatch(li);
+
+ litest_assert_button_event(li, BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ litest_timeout_tap();
+ litest_assert_button_event(li, BTN_MIDDLE,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+
+ libinput_dispatch(li);
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
START_TEST(touchpad_3fg_tap_btntool)
{
struct litest_device *dev = litest_current_device();
@@ -783,6 +1489,161 @@ START_TEST(touchpad_3fg_tap_btntool_inverted)
}
END_TEST
+START_TEST(touchpad_4fg_tap)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ int i;
+
+ if (libevdev_get_abs_maximum(dev->evdev,
+ ABS_MT_SLOT) <= 3)
+ return;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ for (i = 0; i < 4; i++) {
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_down(dev, 1, 70, 50);
+ litest_touch_down(dev, 2, 80, 50);
+ litest_touch_down(dev, 3, 90, 50);
+
+ litest_touch_up(dev, (i + 3) % 4);
+ litest_touch_up(dev, (i + 2) % 4);
+ litest_touch_up(dev, (i + 1) % 4);
+ litest_touch_up(dev, (i + 0) % 4);
+
+ libinput_dispatch(li);
+ litest_assert_empty_queue(li);
+ litest_timeout_tap();
+ litest_assert_empty_queue(li);
+ event = libinput_get_event(li);
+ ck_assert(event == NULL);
+ }
+}
+END_TEST
+
+START_TEST(touchpad_4fg_tap_quickrelease)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ if (libevdev_get_abs_maximum(dev->evdev,
+ ABS_MT_SLOT) <= 3)
+ return;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_down(dev, 1, 70, 50);
+ litest_touch_down(dev, 2, 80, 50);
+ litest_touch_down(dev, 3, 90, 50);
+
+ litest_event(dev, EV_ABS, ABS_MT_SLOT, 0);
+ litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ litest_event(dev, EV_ABS, ABS_MT_SLOT, 1);
+ litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ litest_event(dev, EV_ABS, ABS_MT_SLOT, 2);
+ litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ litest_event(dev, EV_ABS, ABS_MT_SLOT, 3);
+ litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ litest_event(dev, EV_KEY, BTN_TOOL_QUADTAP, 0);
+ litest_event(dev, EV_KEY, BTN_TOUCH, 0);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+
+ libinput_dispatch(li);
+ litest_assert_empty_queue(li);
+ litest_timeout_tap();
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_5fg_tap)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ int i;
+
+ if (libevdev_get_abs_maximum(dev->evdev,
+ ABS_MT_SLOT) <= 4)
+ return;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ for (i = 0; i < 5; i++) {
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 20, 50);
+ litest_touch_down(dev, 1, 30, 50);
+ litest_touch_down(dev, 2, 40, 50);
+ litest_touch_down(dev, 3, 50, 50);
+ litest_touch_down(dev, 4, 60, 50);
+
+ litest_touch_up(dev, (i + 4) % 5);
+ litest_touch_up(dev, (i + 3) % 5);
+ litest_touch_up(dev, (i + 2) % 5);
+ litest_touch_up(dev, (i + 1) % 5);
+ litest_touch_up(dev, (i + 0) % 5);
+
+ libinput_dispatch(li);
+ litest_assert_empty_queue(li);
+ litest_timeout_tap();
+ litest_assert_empty_queue(li);
+ event = libinput_get_event(li);
+ ck_assert(event == NULL);
+ }
+}
+END_TEST
+
+START_TEST(touchpad_5fg_tap_quickrelease)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ if (libevdev_get_abs_maximum(dev->evdev,
+ ABS_MT_SLOT) <= 4)
+ return;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 20, 50);
+ litest_touch_down(dev, 1, 30, 50);
+ litest_touch_down(dev, 2, 40, 50);
+ litest_touch_down(dev, 3, 70, 50);
+ litest_touch_down(dev, 4, 90, 50);
+
+ litest_event(dev, EV_ABS, ABS_MT_SLOT, 0);
+ litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ litest_event(dev, EV_ABS, ABS_MT_SLOT, 1);
+ litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ litest_event(dev, EV_ABS, ABS_MT_SLOT, 2);
+ litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ litest_event(dev, EV_ABS, ABS_MT_SLOT, 3);
+ litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ litest_event(dev, EV_ABS, ABS_MT_SLOT, 4);
+ litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ litest_event(dev, EV_KEY, BTN_TOOL_QUINTTAP, 0);
+ litest_event(dev, EV_KEY, BTN_TOUCH, 0);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+
+ libinput_dispatch(li);
+ litest_assert_empty_queue(li);
+ litest_timeout_tap();
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
START_TEST(touchpad_click_defaults_clickfinger)
{
struct litest_device *dev = litest_current_device();
@@ -891,6 +1752,32 @@ START_TEST(touchpad_1fg_clickfinger)
}
END_TEST
+START_TEST(touchpad_1fg_clickfinger_no_touch)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ enum libinput_config_status status;
+
+ status = libinput_device_config_click_set_method(dev->libinput_device,
+ LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+
+ litest_drain_events(li);
+
+ litest_event(dev, EV_KEY, BTN_LEFT, 1);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+ litest_event(dev, EV_KEY, BTN_LEFT, 0);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+
+ libinput_dispatch(li);
+
+ litest_assert_button_event(li, BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ litest_assert_button_event(li, BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+}
+END_TEST
+
START_TEST(touchpad_2fg_clickfinger)
{
struct litest_device *dev = litest_current_device();
@@ -1170,6 +2057,9 @@ START_TEST(clickpad_btn_left)
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
+ libinput_device_config_click_set_method(dev->libinput_device,
+ LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS);
+
litest_drain_events(li);
/* A clickpad always needs a finger down to tell where the
@@ -1820,7 +2710,7 @@ test_2fg_scroll(struct litest_device *dev, double dx, double dy, int want_sleep)
litest_touch_down(dev, 0, 49, 50);
litest_touch_down(dev, 1, 51, 50);
- litest_touch_move_two_touches(dev, 49, 50, 51, 50, dx, dy, 5, 0);
+ litest_touch_move_two_touches(dev, 49, 50, 51, 50, dx, dy, 10, 0);
/* Avoid a small scroll being seen as a tap */
if (want_sleep) {
@@ -1935,6 +2825,26 @@ START_TEST(touchpad_2fg_scroll_source)
}
END_TEST
+START_TEST(touchpad_2fg_scroll_semi_mt)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 20, 20);
+ litest_touch_down(dev, 1, 30, 20);
+ libinput_dispatch(li);
+ litest_touch_move_to(dev, 1, 30, 20, 30, 70, 10, 5);
+
+ litest_assert_empty_queue(li);
+
+ litest_touch_move_to(dev, 0, 20, 20, 20, 70, 10, 5);
+
+ litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS);
+}
+END_TEST
+
START_TEST(touchpad_2fg_scroll_return_to_motion)
{
struct litest_device *dev = litest_current_device();
@@ -2023,12 +2933,26 @@ START_TEST(touchpad_scroll_natural)
}
END_TEST
+static void
+enable_edge_scroll(struct litest_device *dev)
+{
+ enum libinput_config_status status, expected;
+ struct libinput_device *device = dev->libinput_device;
+
+ status = libinput_device_config_scroll_set_method(device,
+ LIBINPUT_CONFIG_SCROLL_EDGE);
+
+ expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
+ litest_assert_int_eq(status, expected);
+}
+
START_TEST(touchpad_edge_scroll)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
litest_drain_events(li);
+ enable_edge_scroll(dev);
litest_touch_down(dev, 0, 99, 20);
litest_touch_move_to(dev, 0, 99, 20, 99, 80, 10, 0);
@@ -2070,15 +2994,28 @@ START_TEST(touchpad_edge_scroll_timeout)
struct libinput *li = dev->libinput;
struct libinput_event *event;
struct libinput_event_pointer *ptrev;
+ double width = 0, height = 0;
+ int y_movement = 30; /* in percent of height */
+
+ /* account for different touchpad heights, let's move 100% on a 15mm
+ high touchpad, less on anything else. This number is picked at
+ random, we just want deltas less than 5.
+ */
+ if (libinput_device_get_size(dev->libinput_device,
+ &width,
+ &height) != -1) {
+ y_movement = 100 * 15/height;
+ }
litest_drain_events(li);
+ enable_edge_scroll(dev);
litest_touch_down(dev, 0, 99, 20);
libinput_dispatch(li);
litest_timeout_edgescroll();
libinput_dispatch(li);
- litest_touch_move_to(dev, 0, 99, 20, 99, 80, 60, 10);
+ litest_touch_move_to(dev, 0, 99, 20, 99, 20 + y_movement, 60, 10);
litest_touch_up(dev, 0);
libinput_dispatch(li);
@@ -2117,6 +3054,7 @@ START_TEST(touchpad_edge_scroll_no_motion)
struct libinput *li = dev->libinput;
litest_drain_events(li);
+ enable_edge_scroll(dev);
litest_touch_down(dev, 0, 99, 10);
litest_touch_move_to(dev, 0, 99, 10, 99, 70, 10, 0);
@@ -2138,6 +3076,7 @@ START_TEST(touchpad_edge_scroll_no_edge_after_motion)
struct libinput *li = dev->libinput;
litest_drain_events(li);
+ enable_edge_scroll(dev);
/* moving into the edge zone must not trigger scroll events */
litest_touch_down(dev, 0, 20, 20);
@@ -2159,6 +3098,7 @@ START_TEST(touchpad_edge_scroll_source)
struct libinput_event_pointer *ptrev;
litest_drain_events(li);
+ enable_edge_scroll(dev);
litest_touch_down(dev, 0, 99, 20);
litest_touch_move_to(dev, 0, 99, 20, 99, 80, 10, 0);
@@ -2177,6 +3117,26 @@ START_TEST(touchpad_edge_scroll_source)
}
END_TEST
+START_TEST(touchpad_edge_scroll_no_2fg)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ litest_drain_events(li);
+ enable_edge_scroll(dev);
+
+ litest_touch_down(dev, 0, 49, 50);
+ litest_touch_down(dev, 1, 51, 50);
+ litest_touch_move_two_touches(dev, 49, 50, 51, 50, 20, 30, 5, 0);
+ libinput_dispatch(li);
+ litest_touch_up(dev, 0);
+ litest_touch_up(dev, 1);
+ libinput_dispatch(li);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
START_TEST(touchpad_tap_is_available)
{
struct litest_device *dev = litest_current_device();
@@ -2195,6 +3155,9 @@ START_TEST(touchpad_tap_is_not_available)
ck_assert_int_eq(libinput_device_config_tap_set_enabled(dev->libinput_device,
LIBINPUT_CONFIG_TAP_ENABLED),
LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+ ck_assert_int_eq(libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_DISABLED),
+ LIBINPUT_CONFIG_STATUS_SUCCESS);
}
END_TEST
@@ -2242,7 +3205,7 @@ touchpad_has_palm_detect_size(struct litest_device *dev)
rc = libinput_device_get_size(dev->libinput_device, &width, &height);
- return rc == 0 && width >= 80;
+ return rc == 0 && width >= 70;
}
START_TEST(touchpad_palm_detect_at_edge)
@@ -2399,6 +3362,49 @@ START_TEST(touchpad_palm_detect_no_palm_moving_into_edges)
}
END_TEST
+START_TEST(touchpad_palm_detect_tap)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ if (!touchpad_has_palm_detect_size(dev))
+ return;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_ENABLED);
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 95, 5);
+ litest_touch_up(dev, 0);
+ litest_assert_empty_queue(li);
+
+ litest_touch_down(dev, 0, 5, 5);
+ litest_touch_up(dev, 0);
+ litest_assert_empty_queue(li);
+
+ litest_touch_down(dev, 0, 5, 90);
+ litest_touch_up(dev, 0);
+ litest_assert_button_event(li,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ litest_assert_button_event(li,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ litest_assert_empty_queue(li);
+
+ litest_touch_down(dev, 0, 95, 90);
+ litest_touch_up(dev, 0);
+ litest_assert_button_event(li,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ litest_assert_button_event(li,
+ BTN_LEFT,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
START_TEST(touchpad_left_handed)
{
struct litest_device *dev = litest_current_device();
@@ -2625,6 +3631,9 @@ START_TEST(touchpad_left_handed_delayed)
/* left-handed takes effect now */
litest_button_click(dev, BTN_RIGHT, 1);
+ libinput_dispatch(li);
+ litest_timeout_middlebutton();
+ libinput_dispatch(li);
litest_button_click(dev, BTN_LEFT, 1);
libinput_dispatch(li);
@@ -2721,7 +3730,7 @@ hover_start(struct litest_device *dev, unsigned int slot,
/* WARNING: no SYN_REPORT! */
}
-START_TEST(touchpad_hover_noevent)
+START_TEST(touchpad_semi_mt_hover_noevent)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
@@ -2752,7 +3761,7 @@ START_TEST(touchpad_hover_noevent)
}
END_TEST
-START_TEST(touchpad_hover_down)
+START_TEST(touchpad_semi_mt_hover_down)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
@@ -2827,7 +3836,7 @@ START_TEST(touchpad_hover_down)
}
END_TEST
-START_TEST(touchpad_hover_down_hover_down)
+START_TEST(touchpad_semi_mt_hover_down_hover_down)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
@@ -2914,7 +3923,7 @@ START_TEST(touchpad_hover_down_hover_down)
}
END_TEST
-START_TEST(touchpad_hover_down_up)
+START_TEST(touchpad_semi_mt_hover_down_up)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
@@ -2981,7 +3990,7 @@ START_TEST(touchpad_hover_down_up)
}
END_TEST
-START_TEST(touchpad_hover_2fg_noevent)
+START_TEST(touchpad_semi_mt_hover_2fg_noevent)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
@@ -3026,7 +4035,7 @@ START_TEST(touchpad_hover_2fg_noevent)
}
END_TEST
-START_TEST(touchpad_hover_2fg_1fg_down)
+START_TEST(touchpad_semi_mt_hover_2fg_1fg_down)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
@@ -3075,6 +4084,179 @@ START_TEST(touchpad_hover_2fg_1fg_down)
}
END_TEST
+START_TEST(touchpad_hover_noevent)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ litest_drain_events(li);
+
+ litest_hover_start(dev, 0, 50, 50);
+ litest_hover_move_to(dev, 0, 50, 50, 70, 70, 10, 10);
+ litest_hover_end(dev, 0);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_hover_down)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ litest_drain_events(li);
+
+ /* hover the finger */
+ litest_hover_start(dev, 0, 50, 50);
+
+ litest_hover_move_to(dev, 0, 50, 50, 70, 70, 10, 10);
+
+ litest_assert_empty_queue(li);
+
+ /* touch the finger on the sensor */
+ litest_touch_move_to(dev, 0, 70, 70, 50, 50, 10, 10);
+
+ libinput_dispatch(li);
+
+ litest_assert_only_typed_events(li,
+ LIBINPUT_EVENT_POINTER_MOTION);
+
+ /* go back to hover */
+ litest_hover_move_to(dev, 0, 50, 50, 70, 70, 10, 10);
+ litest_hover_end(dev, 0);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_hover_down_hover_down)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ int i;
+
+ litest_drain_events(li);
+
+ litest_hover_start(dev, 0, 50, 50);
+
+ for (i = 0; i < 3; i++) {
+
+ /* hover the finger */
+ litest_hover_move_to(dev, 0, 50, 50, 70, 70, 10, 10);
+
+ litest_assert_empty_queue(li);
+
+ /* touch the finger */
+ litest_touch_move_to(dev, 0, 70, 70, 50, 50, 10, 10);
+
+ libinput_dispatch(li);
+
+ litest_assert_only_typed_events(li,
+ LIBINPUT_EVENT_POINTER_MOTION);
+ }
+
+ litest_hover_end(dev, 0);
+
+ /* start a new touch to be sure */
+ litest_touch_down(dev, 0, 50, 50);
+ litest_touch_move_to(dev, 0, 50, 50, 70, 70, 10, 10);
+ litest_touch_up(dev, 0);
+
+ litest_assert_only_typed_events(li,
+ LIBINPUT_EVENT_POINTER_MOTION);
+}
+END_TEST
+
+START_TEST(touchpad_hover_down_up)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ litest_drain_events(li);
+
+ /* hover two fingers, and a touch */
+ litest_push_event_frame(dev);
+ litest_hover_start(dev, 0, 50, 50);
+ litest_hover_start(dev, 1, 50, 50);
+ litest_touch_down(dev, 2, 50, 50);
+ litest_pop_event_frame(dev);;
+
+ litest_assert_empty_queue(li);
+
+ /* hover first finger, end second and third in same frame */
+ litest_push_event_frame(dev);
+ litest_hover_move(dev, 0, 70, 70);
+ litest_hover_end(dev, 1);
+ litest_touch_up(dev, 2);
+ litest_pop_event_frame(dev);;
+
+ litest_assert_empty_queue(li);
+
+ /* now move the finger */
+ litest_touch_move_to(dev, 0, 50, 50, 70, 70, 10, 10);
+
+ litest_touch_up(dev, 0);
+
+ litest_assert_only_typed_events(li,
+ LIBINPUT_EVENT_POINTER_MOTION);
+}
+END_TEST
+
+START_TEST(touchpad_hover_2fg_noevent)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+
+ litest_drain_events(li);
+
+ /* hover two fingers */
+ litest_push_event_frame(dev);
+ litest_hover_start(dev, 0, 25, 25);
+ litest_hover_start(dev, 1, 50, 50);
+ litest_pop_event_frame(dev);;
+
+ litest_hover_move_two_touches(dev, 25, 25, 50, 50, 50, 50, 10, 0);
+
+ litest_push_event_frame(dev);
+ litest_hover_end(dev, 0);
+ litest_hover_end(dev, 1);
+ litest_pop_event_frame(dev);;
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_hover_2fg_1fg_down)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ int i;
+
+ litest_drain_events(li);
+
+ /* hover two fingers */
+ litest_push_event_frame(dev);
+ litest_hover_start(dev, 0, 25, 25);
+ litest_touch_down(dev, 1, 50, 50);
+ litest_pop_event_frame(dev);;
+
+ for (i = 0; i < 10; i++) {
+ litest_push_event_frame(dev);
+ litest_hover_move(dev, 0, 25 + 5 * i, 25 + 5 * i);
+ litest_touch_move(dev, 1, 50 + 5 * i, 50 - 5 * i);
+ litest_pop_event_frame(dev);;
+ }
+
+ litest_push_event_frame(dev);
+ litest_hover_end(dev, 0);
+ litest_touch_up(dev, 1);
+ litest_pop_event_frame(dev);;
+
+ litest_assert_only_typed_events(li,
+ LIBINPUT_EVENT_POINTER_MOTION);
+}
+END_TEST
+
static void
assert_btnevent_from_device(struct litest_device *device,
unsigned int button,
@@ -3082,20 +4264,12 @@ assert_btnevent_from_device(struct litest_device *device,
{
struct libinput *li = device->libinput;
struct libinput_event *e;
- struct libinput_event_pointer *pev;
libinput_dispatch(li);
e = libinput_get_event(li);
- ck_assert_notnull(e);
- ck_assert_int_eq(libinput_event_get_type(e),
- LIBINPUT_EVENT_POINTER_BUTTON);
- pev = libinput_event_get_pointer_event(e);
+ litest_is_button_event(e, button, state);
- ck_assert_ptr_eq(libinput_event_get_device(e), device->libinput_device);
- ck_assert_int_eq(libinput_event_pointer_get_button(pev),
- button);
- ck_assert_int_eq(libinput_event_pointer_get_button_state(pev),
- state);
+ litest_assert_ptr_eq(libinput_event_get_device(e), device->libinput_device);
libinput_event_destroy(e);
}
@@ -3357,19 +4531,91 @@ START_TEST(touchpad_trackpoint_no_trackpoint)
}
END_TEST
-int main(int argc, char **argv) {
+START_TEST(touchpad_initial_state)
+{
+ struct litest_device *dev;
+ struct libinput *libinput1, *libinput2;
+ struct libinput_event *ev1, *ev2;
+ struct libinput_event_pointer *p1, *p2;
+ int axis = _i; /* looped test */
+ int x = 40, y = 60;
+
+ dev = litest_current_device();
+ libinput1 = dev->libinput;
+
+ libinput_device_config_tap_set_enabled(dev->libinput_device,
+ LIBINPUT_CONFIG_TAP_DISABLED);
+
+ litest_touch_down(dev, 0, x, y);
+ litest_touch_up(dev, 0);
+
+ /* device is now on some x/y value */
+ litest_drain_events(libinput1);
+
+ libinput2 = litest_create_context();
+ libinput_path_add_device(libinput2,
+ libevdev_uinput_get_devnode(dev->uinput));
+ litest_drain_events(libinput2);
+
+ if (axis == ABS_X)
+ x = 30;
+ else
+ y = 30;
+ litest_touch_down(dev, 0, x, y);
+ litest_touch_move_to(dev, 0, x, y, 80, 80, 10, 1);
+ litest_touch_up(dev, 0);
+
+ litest_wait_for_event(libinput1);
+ litest_wait_for_event(libinput2);
+
+ while (libinput_next_event_type(libinput1)) {
+ ev1 = libinput_get_event(libinput1);
+ ev2 = libinput_get_event(libinput2);
+
+ p1 = litest_is_motion_event(ev1);
+ p2 = litest_is_motion_event(ev2);
+
+ ck_assert_int_eq(libinput_event_get_type(ev1),
+ libinput_event_get_type(ev2));
+
+ ck_assert_int_eq(libinput_event_pointer_get_dx(p1),
+ libinput_event_pointer_get_dx(p2));
+ ck_assert_int_eq(libinput_event_pointer_get_dy(p1),
+ libinput_event_pointer_get_dy(p2));
+ libinput_event_destroy(ev1);
+ libinput_event_destroy(ev2);
+ }
+
+ libinput_unref(libinput2);
+}
+END_TEST
+
+void
+litest_setup_tests(void)
+{
+ struct range multitap_range = {3, 8};
+ struct range axis_range = {ABS_X, ABS_Y + 1};
litest_add("touchpad:motion", touchpad_1fg_motion, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:motion", touchpad_2fg_no_motion, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:tap", touchpad_1fg_tap, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add("touchpad:tap", touchpad_1fg_doubletap, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add_ranged("touchpad:tap", touchpad_1fg_multitap, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range);
+ litest_add_ranged("touchpad:tap", touchpad_1fg_multitap_n_drag_timeout, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range);
+ litest_add_ranged("touchpad:tap", touchpad_1fg_multitap_n_drag_tap, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range);
+ litest_add_ranged("touchpad:tap", touchpad_1fg_multitap_n_drag_move, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range);
+ litest_add_ranged("touchpad:tap", touchpad_1fg_multitap_n_drag_2fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &multitap_range);
+ litest_add_ranged("touchpad:tap", touchpad_1fg_multitap_n_drag_click, LITEST_CLICKPAD, LITEST_ANY, &multitap_range);
litest_add("touchpad:tap", touchpad_1fg_tap_n_drag, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add("touchpad:tap", touchpad_1fg_tap_n_drag_tap, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:tap", touchpad_1fg_tap_n_drag_timeout, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:tap", touchpad_2fg_tap_n_drag, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:tap", touchpad_2fg_tap_n_drag_3fg_btntool, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_APPLE_CLICKPAD);
litest_add("touchpad:tap", touchpad_2fg_tap_n_drag_3fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:tap", touchpad_2fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
litest_add("touchpad:tap", touchpad_2fg_tap_inverted, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+ litest_add("touchpad:tap", touchpad_2fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
litest_add("touchpad:tap", touchpad_1fg_tap_click, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_CLICKPAD);
litest_add("touchpad:tap", touchpad_2fg_tap_click, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_SINGLE_TOUCH|LITEST_CLICKPAD);
@@ -3381,11 +4627,18 @@ int main(int argc, char **argv) {
litest_add("touchpad:tap", touchpad_3fg_tap_btntool, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:tap", touchpad_3fg_tap_btntool_inverted, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:tap", touchpad_3fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+ litest_add("touchpad:tap", touchpad_3fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+ litest_add("touchpad:tap", touchpad_4fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
+ litest_add("touchpad:tap", touchpad_4fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
+ litest_add("touchpad:tap", touchpad_5fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
+ litest_add("touchpad:tap", touchpad_5fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
/* Real buttons don't interfere with tapping, so don't run those for
pads with buttons */
litest_add("touchpad:tap", touchpad_1fg_double_tap_click, LITEST_CLICKPAD, LITEST_ANY);
litest_add("touchpad:tap", touchpad_1fg_tap_n_drag_click, LITEST_CLICKPAD, LITEST_ANY);
+ litest_add("touchpad:tap", touchpad_1fg_multitap_n_drag_tap_click, LITEST_CLICKPAD, LITEST_ANY);
+ litest_add("touchpad:tap", touchpad_1fg_tap_n_drag_tap_click, LITEST_CLICKPAD, LITEST_ANY);
litest_add("touchpad:tap", touchpad_tap_default_disabled, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_ANY);
litest_add("touchpad:tap", touchpad_tap_default_enabled, LITEST_TOUCHPAD, LITEST_BUTTON);
@@ -3397,6 +4650,7 @@ int main(int argc, char **argv) {
litest_add("touchpad:tap", clickpad_2fg_tap_click, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH|LITEST_APPLE_CLICKPAD);
litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY);
+ litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger_no_touch, LITEST_CLICKPAD, LITEST_ANY);
litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY);
litest_add("touchpad:clickfinger", touchpad_clickfinger_to_area_method, LITEST_CLICKPAD, LITEST_ANY);
litest_add("touchpad:clickfinger",
@@ -3433,14 +4687,16 @@ int main(int argc, char **argv) {
litest_add("touchpad:scroll", touchpad_2fg_scroll_slow_distance, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:scroll", touchpad_2fg_scroll_return_to_motion, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:scroll", touchpad_2fg_scroll_source, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+ litest_add("touchpad:scroll", touchpad_2fg_scroll_semi_mt, LITEST_SEMI_MT, LITEST_SINGLE_TOUCH);
litest_add("touchpad:scroll", touchpad_scroll_natural_defaults, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:scroll", touchpad_scroll_natural_enable_config, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:scroll", touchpad_scroll_natural, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
- litest_add("touchpad:scroll", touchpad_edge_scroll, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY);
- litest_add("touchpad:scroll", touchpad_edge_scroll_no_motion, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY);
- litest_add("touchpad:scroll", touchpad_edge_scroll_no_edge_after_motion, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY);
- litest_add("touchpad:scroll", touchpad_edge_scroll_timeout, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY);
- litest_add("touchpad:scroll", touchpad_edge_scroll_source, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY);
+ litest_add("touchpad:scroll", touchpad_edge_scroll, LITEST_TOUCHPAD, LITEST_CLICKPAD);
+ litest_add("touchpad:scroll", touchpad_edge_scroll_no_motion, LITEST_TOUCHPAD, LITEST_CLICKPAD);
+ litest_add("touchpad:scroll", touchpad_edge_scroll_no_edge_after_motion, LITEST_TOUCHPAD, LITEST_CLICKPAD);
+ litest_add("touchpad:scroll", touchpad_edge_scroll_timeout, LITEST_TOUCHPAD, LITEST_CLICKPAD);
+ litest_add("touchpad:scroll", touchpad_edge_scroll_source, LITEST_TOUCHPAD, LITEST_CLICKPAD);
+ litest_add("touchpad:scroll", touchpad_edge_scroll_no_2fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_CLICKPAD);
litest_add("touchpad:palm", touchpad_palm_detect_at_edge, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:palm", touchpad_palm_detect_at_bottom_corners, LITEST_TOUCHPAD, LITEST_CLICKPAD);
@@ -3448,6 +4704,7 @@ int main(int argc, char **argv) {
litest_add("touchpad:palm", touchpad_palm_detect_palm_becomes_pointer, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:palm", touchpad_palm_detect_palm_stays_palm, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:palm", touchpad_palm_detect_no_palm_moving_into_edges, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add("touchpad:palm", touchpad_palm_detect_tap, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:left-handed", touchpad_left_handed, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_CLICKPAD);
litest_add("touchpad:left-handed", touchpad_left_handed_clickpad, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD);
@@ -3457,14 +4714,21 @@ int main(int argc, char **argv) {
litest_add("touchpad:left-handed", touchpad_left_handed_delayed, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_CLICKPAD);
litest_add("touchpad:left-handed", touchpad_left_handed_clickpad_delayed, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD);
- /* Hover tests aren't generic, they only work on this device and
+ /* Semi-MT hover tests aren't generic, they only work on this device and
* ignore the semi-mt capability (it doesn't matter for the tests */
- litest_add_for_device("touchpad:hover", touchpad_hover_noevent, LITEST_SYNAPTICS_HOVER_SEMI_MT);
- litest_add_for_device("touchpad:hover", touchpad_hover_down, LITEST_SYNAPTICS_HOVER_SEMI_MT);
- litest_add_for_device("touchpad:hover", touchpad_hover_down_up, LITEST_SYNAPTICS_HOVER_SEMI_MT);
- litest_add_for_device("touchpad:hover", touchpad_hover_down_hover_down, LITEST_SYNAPTICS_HOVER_SEMI_MT);
- litest_add_for_device("touchpad:hover", touchpad_hover_2fg_noevent, LITEST_SYNAPTICS_HOVER_SEMI_MT);
- litest_add_for_device("touchpad:hover", touchpad_hover_2fg_1fg_down, LITEST_SYNAPTICS_HOVER_SEMI_MT);
+ litest_add_for_device("touchpad:semi-mt-hover", touchpad_semi_mt_hover_noevent, LITEST_SYNAPTICS_HOVER_SEMI_MT);
+ litest_add_for_device("touchpad:semi-mt-hover", touchpad_semi_mt_hover_down, LITEST_SYNAPTICS_HOVER_SEMI_MT);
+ litest_add_for_device("touchpad:semi-mt-hover", touchpad_semi_mt_hover_down_up, LITEST_SYNAPTICS_HOVER_SEMI_MT);
+ litest_add_for_device("touchpad:semi-mt-hover", touchpad_semi_mt_hover_down_hover_down, LITEST_SYNAPTICS_HOVER_SEMI_MT);
+ litest_add_for_device("touchpad:semi-mt-hover", touchpad_semi_mt_hover_2fg_noevent, LITEST_SYNAPTICS_HOVER_SEMI_MT);
+ litest_add_for_device("touchpad:semi-mt-hover", touchpad_semi_mt_hover_2fg_1fg_down, LITEST_SYNAPTICS_HOVER_SEMI_MT);
+
+ litest_add("touchpad:hover", touchpad_hover_noevent, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY);
+ litest_add("touchpad:hover", touchpad_hover_down, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY);
+ litest_add("touchpad:hover", touchpad_hover_down_up, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY);
+ litest_add("touchpad:hover", touchpad_hover_down_hover_down, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY);
+ litest_add("touchpad:hover", touchpad_hover_2fg_noevent, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY);
+ litest_add("touchpad:hover", touchpad_hover_2fg_1fg_down, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY);
litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_buttons, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS);
litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_mb_scroll, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS);
@@ -3473,5 +4737,5 @@ int main(int argc, char **argv) {
litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_buttons_2fg_scroll, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS);
litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_no_trackpoint, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS);
- return litest_run(argc, argv);
+ litest_add_ranged("touchpad:state", touchpad_initial_state, LITEST_TOUCHPAD, LITEST_ANY, &axis_range);
}
diff --git a/test/trackpoint.c b/test/trackpoint.c
index 2708bad2..9fcce6f7 100644
--- a/test/trackpoint.c
+++ b/test/trackpoint.c
@@ -130,12 +130,11 @@ START_TEST(trackpoint_scroll_source)
}
END_TEST
-int main(int argc, char **argv) {
-
+void
+litest_setup_tests(void)
+{
litest_add("trackpoint:middlebutton", trackpoint_middlebutton, LITEST_POINTINGSTICK, LITEST_ANY);
litest_add("trackpoint:middlebutton", trackpoint_middlebutton_noscroll, LITEST_POINTINGSTICK, LITEST_ANY);
litest_add("trackpoint:scroll", trackpoint_scroll, LITEST_POINTINGSTICK, LITEST_ANY);
litest_add("trackpoint:scroll", trackpoint_scroll_source, LITEST_POINTINGSTICK, LITEST_ANY);
-
- return litest_run(argc, argv);
}
diff --git a/test/udev.c b/test/udev.c
index c351bed5..4ec956bc 100644
--- a/test/udev.c
+++ b/test/udev.c
@@ -502,8 +502,8 @@ START_TEST(udev_seat_recycle)
}
END_TEST
-int
-main(int argc, char **argv)
+void
+litest_setup_tests(void)
{
litest_add_no_device("udev:create", udev_create_NULL);
litest_add_no_device("udev:create", udev_create_seat0);
@@ -518,6 +518,4 @@ main(int argc, char **argv)
litest_add_for_device("udev:suspend", udev_suspend_resume, LITEST_SYNAPTICS_CLICKPAD);
litest_add_for_device("udev:device events", udev_device_sysname, LITEST_SYNAPTICS_CLICKPAD);
litest_add_for_device("udev:seat", udev_seat_recycle, LITEST_SYNAPTICS_CLICKPAD);
-
- return litest_run(argc, argv);
}
diff --git a/test/valgrind.suppressions b/test/valgrind.suppressions
index 4ad22ab2..b7f43499 100644
--- a/test/valgrind.suppressions
+++ b/test/valgrind.suppressions
@@ -31,3 +31,11 @@
...
fun:g_malloc0
}
+{
+ libunwind:msync_uninitialized_bytes
+ Memcheck:Param
+ msync(start)
+ fun:__msync_nocancel
+ ...
+ fun:litest_backtrace
+}
diff --git a/tools/.gitignore b/tools/.gitignore
index cf348a6f..cb934298 100644
--- a/tools/.gitignore
+++ b/tools/.gitignore
@@ -1,2 +1,5 @@
event-debug
event-gui
+ptraccel-debug
+libinput-list-devices
+libinput-debug-events
diff --git a/tools/Makefile.am b/tools/Makefile.am
index cebcd729..b24c560d 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -1,19 +1,36 @@
-noinst_PROGRAMS = event-debug
+noinst_PROGRAMS = event-debug ptraccel-debug
+bin_PROGRAMS = libinput-list-devices libinput-debug-events
noinst_LTLIBRARIES = libshared.la
AM_CPPFLAGS = -I$(top_srcdir)/include \
- -I$(top_srcdir)/src
-
+ -I$(top_srcdir)/src \
+ -I$(top_builddir)/src # for libinput-version.h
libshared_la_SOURCES = \
shared.c \
shared.h
+libshared_la_CFLAGS = $(LIBEVDEV_CFLAGS)
+libshared_la_LIBADD = $(LIBEVDEV_LIBS)
event_debug_SOURCES = event-debug.c
event_debug_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS)
event_debug_LDFLAGS = -no-install
event_debug_CFLAGS = $(LIBUDEV_CFLAGS)
+ptraccel_debug_SOURCES = ptraccel-debug.c
+ptraccel_debug_LDADD = ../src/libfilter.la
+ptraccel_debug_LDFLAGS = -no-install
+
+libinput_list_devices_SOURCES = libinput-list-devices.c
+libinput_list_devices_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS)
+libinput_list_devices_CFLAGS = $(LIBUDEV_CFLAGS)
+dist_man1_MANS = libinput-list-devices.man
+
+libinput_debug_events_SOURCES = $(event_debug_SOURCES)
+libinput_debug_events_LDADD = $(event_debug_LDADD)
+libinput_debug_events_CFLAGS = $(event_debug_CFLAGS)
+dist_man1_MANS += libinput-debug-events.man
+
if BUILD_EVENTGUI
noinst_PROGRAMS += event-gui
diff --git a/tools/event-gui.c b/tools/event-gui.c
index 370df4a7..cdeabf05 100644
--- a/tools/event-gui.c
+++ b/tools/event-gui.c
@@ -62,8 +62,8 @@ struct window {
int absx, absy;
/* scroll bar positions */
- int vx, vy;
- int hx, hy;
+ double vx, vy;
+ double hx, hy;
/* touch positions */
struct touch touches[32];
@@ -279,14 +279,8 @@ handle_event_device_notify(struct libinput_event *ev)
libinput_device_get_name(dev),
type);
- if (libinput_device_config_tap_get_finger_count(dev) > 0) {
- enum libinput_config_status status;
- status = libinput_device_config_tap_set_enabled(dev,
- LIBINPUT_CONFIG_TAP_ENABLED);
- if (status != LIBINPUT_CONFIG_STATUS_SUCCESS)
- error("%s: Failed to enable tapping\n",
- libinput_device_get_sysname(dev));
- }
+ tools_device_apply_config(libinput_event_get_device(ev),
+ &options);
li = libinput_event_get_context(ev);
w = libinput_get_user_data(li);
@@ -369,7 +363,7 @@ handle_event_axis(struct libinput_event *ev, struct window *w)
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
value = libinput_event_pointer_get_axis_value(p,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
- w->vy += (int)value;
+ w->vy += value;
w->vy = clip(w->vy, 0, w->height);
}
@@ -377,7 +371,7 @@ handle_event_axis(struct libinput_event *ev, struct window *w)
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) {
value = libinput_event_pointer_get_axis_value(p,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
- w->hx += (int)value;
+ w->hx += value;
w->hx = clip(w->hx, 0, w->width);
}
}
diff --git a/tools/libinput-debug-events.man b/tools/libinput-debug-events.man
new file mode 100644
index 00000000..bb393992
--- /dev/null
+++ b/tools/libinput-debug-events.man
@@ -0,0 +1,31 @@
+.TH LIBINPUT-DEBUG-EVENTS "1"
+.SH NAME
+libinput-debug-events \- debug helper for libinput
+.SH SYNOPSIS
+.B libinput-debug-events [--help]
+.SH DESCRIPTION
+.PP
+The
+.I libinput-debug-events
+tool creates a libinput context and prints all events from these devices.
+.PP
+This is a debugging tool only, its output may change at any time. Do not
+rely on the output.
+.PP
+This tool usually needs to be run as root to have access to the
+/dev/input/eventX nodes.
+.SH OPTIONS
+.TP 8
+.B --help
+Print help
+.PP
+For all other options, see the output from --help. Options may be added or
+removed at any time.
+.SH NOTES
+.PP
+Events shown by this tool may not correspond to the events seen by a
+different user of libinput. This tool initializes a separate context.
+.PP
+Events shown by this tool include key codes in plain text. Anything you type
+while this tool is running will show up in the output, including your
+passwords.
diff --git a/tools/libinput-list-devices.c b/tools/libinput-list-devices.c
new file mode 100644
index 00000000..825969d5
--- /dev/null
+++ b/tools/libinput-list-devices.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright © 2015 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define _GNU_SOURCE
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "shared.h"
+
+static int
+open_restricted(const char *path, int flags, void *user_data)
+{
+ int fd = open(path, flags);
+ if (fd < 0)
+ fprintf(stderr, "Failed to open %s (%s)\n",
+ path, strerror(errno));
+ return fd < 0 ? -errno : fd;
+}
+
+static void
+close_restricted(int fd, void *user_data)
+{
+ close(fd);
+}
+
+static const struct libinput_interface interface = {
+ .open_restricted = open_restricted,
+ .close_restricted = close_restricted,
+};
+
+static inline const char*
+bool_to_str(bool b)
+{
+ if (b)
+ return "yes";
+ else
+ return "no";
+}
+
+static const char *
+tap_default(struct libinput_device *device)
+{
+ if (!libinput_device_config_tap_get_finger_count(device))
+ return "n/a";
+
+ if (libinput_device_config_tap_get_default_enabled(device))
+ return "enabled";
+ else
+ return "disabled";
+}
+
+static const char*
+left_handed_default(struct libinput_device *device)
+{
+ if (!libinput_device_config_left_handed_is_available(device))
+ return "n/a";
+
+ if (libinput_device_config_left_handed_get_default(device))
+ return "enabled";
+ else
+ return "disabled";
+}
+
+static const char *
+nat_scroll_default(struct libinput_device *device)
+{
+ if (!libinput_device_config_scroll_has_natural_scroll(device))
+ return "n/a";
+
+ if (libinput_device_config_scroll_get_default_natural_scroll_enabled(device))
+ return "enabled";
+ else
+ return "disabled";
+}
+
+static const char *
+middle_emulation_default(struct libinput_device *device)
+{
+ if (!libinput_device_config_middle_emulation_is_available(device))
+ return "n/a";
+
+ if (libinput_device_config_middle_emulation_get_default_enabled(device))
+ return "enabled";
+ else
+ return "disabled";
+}
+
+static char *
+calibration_default(struct libinput_device *device)
+{
+ char *str;
+ float calibration[6];
+
+ if (!libinput_device_config_calibration_has_matrix(device)) {
+ asprintf(&str, "n/a");
+ return str;
+ }
+
+ if (libinput_device_config_calibration_get_default_matrix(device,
+ calibration) == 0) {
+ asprintf(&str, "identity matrix");
+ return str;
+ }
+
+ asprintf(&str,
+ "%.2f %.2f %.2f %.2f %.2f %.2f",
+ calibration[0],
+ calibration[1],
+ calibration[2],
+ calibration[3],
+ calibration[4],
+ calibration[5]);
+ return str;
+}
+
+static char *
+scroll_defaults(struct libinput_device *device)
+{
+ uint32_t scroll_methods;
+ char *str;
+ enum libinput_config_scroll_method method;
+
+ scroll_methods = libinput_device_config_scroll_get_methods(device);
+ if (scroll_methods == LIBINPUT_CONFIG_SCROLL_NO_SCROLL) {
+ asprintf(&str, "none");
+ return str;
+ }
+
+ method = libinput_device_config_scroll_get_default_method(device);
+
+ asprintf(&str,
+ "%s%s%s%s%s%s",
+ (method == LIBINPUT_CONFIG_SCROLL_2FG) ? "*" : "",
+ (scroll_methods & LIBINPUT_CONFIG_SCROLL_2FG) ? "two-finger " : "",
+ (method == LIBINPUT_CONFIG_SCROLL_EDGE) ? "*" : "",
+ (scroll_methods & LIBINPUT_CONFIG_SCROLL_EDGE) ? "edge " : "",
+ (method == LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) ? "*" : "",
+ (scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) ? "button" : "");
+ return str;
+}
+
+static char*
+click_defaults(struct libinput_device *device)
+{
+ uint32_t click_methods;
+ char *str;
+ enum libinput_config_click_method method;
+
+ click_methods = libinput_device_config_click_get_methods(device);
+ if (click_methods == LIBINPUT_CONFIG_CLICK_METHOD_NONE) {
+ asprintf(&str, "none");
+ return str;
+ }
+
+ method = libinput_device_config_click_get_default_method(device);
+ asprintf(&str,
+ "%s%s%s%s",
+ (method == LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "*" : "",
+ (click_methods & LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "button-areas " : "",
+ (method == LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) ? "*" : "",
+ (click_methods & LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) ? "clickfinger " : "");
+ return str;
+}
+
+static void
+print_device_notify(struct libinput_event *ev)
+{
+ struct libinput_device *dev = libinput_event_get_device(ev);
+ struct libinput_seat *seat = libinput_device_get_seat(dev);
+ struct libinput_device_group *group;
+ double w, h;
+ static int next_group_id = 0;
+ intptr_t group_id;
+ const char *devnode;
+ char *str;
+
+ group = libinput_device_get_device_group(dev);
+ group_id = (intptr_t)libinput_device_group_get_user_data(group);
+ if (!group_id) {
+ group_id = ++next_group_id;
+ libinput_device_group_set_user_data(group, (void*)group_id);
+ }
+
+ devnode = udev_device_get_devnode(
+ libinput_device_get_udev_device(dev));
+
+ printf("Device: %s\n"
+ "Kernel: %s\n"
+ "Group: %d\n"
+ "Seat: %s, %s\n",
+ libinput_device_get_name(dev),
+ devnode,
+ (int)group_id,
+ libinput_seat_get_physical_name(seat),
+ libinput_seat_get_logical_name(seat));
+
+ if (libinput_device_get_size(dev, &w, &h) == 0)
+ printf("Size: %.2fx%.2fmm\n", w, h);
+ printf("Capabilities: ");
+ if (libinput_device_has_capability(dev,
+ LIBINPUT_DEVICE_CAP_KEYBOARD))
+ printf("keyboard ");
+ if (libinput_device_has_capability(dev,
+ LIBINPUT_DEVICE_CAP_POINTER))
+ printf("pointer ");
+ if (libinput_device_has_capability(dev,
+ LIBINPUT_DEVICE_CAP_TOUCH))
+ printf("touch");
+ printf("\n");
+
+ printf("Tap-to-click: %s\n", tap_default(dev));
+ printf("Left-handed: %s\n", left_handed_default(dev));
+ printf("Nat.scrolling: %s\n", nat_scroll_default(dev));
+ printf("Middle emulation: %s\n", middle_emulation_default(dev));
+ str = calibration_default(dev);
+ printf("Calibration: %s\n", str);
+ free(str);
+
+ str = scroll_defaults(dev);
+ printf("Scroll methods: %s\n", str);
+ free(str);
+
+ str = click_defaults(dev);
+ printf("Click methods: %s\n", str);
+ free(str);
+
+ printf("\n");
+}
+
+static inline void
+usage(void)
+{
+ printf("Usage: %s [--help|--version]\n"
+ "\n"
+ "This tool creates a libinput context on the default seat \"seat0\"\n"
+ "and lists all devices recognized by libinput and the configuration options.\n"
+ "Where multiple options are possible, the default is prefixed with \"*\".\n"
+ "\n"
+ "Options:\n"
+ "--help ...... show this help\n"
+ "--version ... show version information\n"
+ "\n"
+ "This tool requires access to the /dev/input/eventX nodes.\n",
+ program_invocation_short_name);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct libinput *li;
+ struct tools_options options;
+ struct libinput_event *ev;
+
+ if (argc > 1) {
+ if (strcmp(argv[1], "--help") == 0) {
+ usage();
+ return 0;
+ } else if (strcmp(argv[1], "--version") == 0) {
+ printf("%s\n", LIBINPUT_VERSION);
+ return 0;
+ } else {
+ usage();
+ return 1;
+ }
+ }
+
+ tools_init_options(&options);
+
+ li = tools_open_backend(&options, NULL, &interface);
+ if (!li)
+ return 1;
+
+ libinput_dispatch(li);
+ while ((ev = libinput_get_event(li))) {
+
+ if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED)
+ print_device_notify(ev);
+
+ libinput_event_destroy(ev);
+ libinput_dispatch(li);
+ }
+
+ libinput_unref(li);
+
+ return 0;
+}
diff --git a/tools/libinput-list-devices.man b/tools/libinput-list-devices.man
new file mode 100644
index 00000000..cd89283a
--- /dev/null
+++ b/tools/libinput-list-devices.man
@@ -0,0 +1,37 @@
+.TH LIBINPUT-LIST-DEVICES "1"
+.SH NAME
+libinput-list-devices \- list local devices as recognized by libinput
+.SH SYNOPSIS
+.B libinput-list-devices [--help]
+.SH DESCRIPTION
+.PP
+The
+.I libinput-list-devices
+tool creates a libinput context on the default seat "seat0" and lists all
+devices regonized by libinput. Each device shows available configurations
+the respective default configuration setting.
+.PP
+For configuration options that allow multiple different settings (e.g.
+scrolling), all available settings are listed. The default setting is
+prefixed by an asterisk (*).
+.PP
+This tool usually needs to be run as root to have access to the
+/dev/input/eventX nodes.
+.SH OPTIONS
+.TP 8
+.B --help
+Print help
+.SH NOTES
+.PP
+Some specific feature may still be available on a device even when
+no configuration is exposed, a lack of a configuration option does not
+necessarily mean that this feature does not work.
+.PP
+A device may be recognized by libinput but not handled by the X.Org libinput
+driver or the Wayland compositor.
+.PP
+An xorg.conf(5) configuration entry or Wayland compositor setting may have
+changed configurations on a device. The
+.I libinput-list-devices
+tool only shows the device's default configuration, not the current
+configuration.
diff --git a/tools/ptraccel-debug.c b/tools/ptraccel-debug.c
new file mode 100644
index 00000000..fdd8490e
--- /dev/null
+++ b/tools/ptraccel-debug.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright © 2015 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#define _GNU_SOURCE
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static void
+print_ptraccel_deltas(struct motion_filter *filter, double step)
+{
+ struct normalized_coords motion;
+ uint64_t time = 0;
+ double i;
+
+ printf("# gnuplot:\n");
+ printf("# set xlabel dx unaccelerated\n");
+ printf("# set ylabel dx accelerated\n");
+ printf("# set style data lines\n");
+ printf("# plot \"gnuplot.data\" using 1:2 title \"step %.2f\"\n", step);
+ printf("#\n");
+
+ /* Accel flattens out after 15 and becomes linear */
+ for (i = 0.0; i < 15.0; i += step) {
+ motion.x = i;
+ motion.y = 0;
+ time += 12; /* pretend 80Hz data */
+
+ motion = filter_dispatch(filter, &motion, NULL, time);
+
+ printf("%.2f %.3f\n", i, motion.x);
+ }
+}
+
+static void
+print_ptraccel_movement(struct motion_filter *filter,
+ int nevents,
+ double max_dx,
+ double step)
+{
+ struct normalized_coords motion;
+ uint64_t time = 0;
+ double dx;
+ int i;
+
+ printf("# gnuplot:\n");
+ printf("# set xlabel \"event number\"\n");
+ printf("# set ylabel \"delta motion\"\n");
+ printf("# set style data lines\n");
+ printf("# plot \"gnuplot.data\" using 1:2 title \"dx out\", \\\n");
+ printf("# \"gnuplot.data\" using 1:3 title \"dx in\"\n");
+ printf("#\n");
+
+ if (nevents == 0) {
+ if (step > 1.0)
+ nevents = max_dx;
+ else
+ nevents = 1.0 * max_dx/step + 0.5;
+
+ /* Print more events than needed so we see the curve
+ * flattening out */
+ nevents *= 1.5;
+ }
+
+ dx = 0;
+
+ for (i = 0; i < nevents; i++) {
+ motion.x = dx;
+ motion.y = 0;
+ time += 12; /* pretend 80Hz data */
+
+ filter_dispatch(filter, &motion, NULL, time);
+
+ printf("%d %.3f %.3f\n", i, motion.x, dx);
+
+ if (dx < max_dx)
+ dx += step;
+ }
+}
+
+static void
+print_ptraccel_sequence(struct motion_filter *filter,
+ int nevents,
+ double *deltas)
+{
+ struct normalized_coords motion;
+ uint64_t time = 0;
+ double *dx;
+ int i;
+
+ printf("# gnuplot:\n");
+ printf("# set xlabel \"event number\"\n");
+ printf("# set ylabel \"delta motion\"\n");
+ printf("# set style data lines\n");
+ printf("# plot \"gnuplot.data\" using 1:2 title \"dx out\", \\\n");
+ printf("# \"gnuplot.data\" using 1:3 title \"dx in\"\n");
+ printf("#\n");
+
+ dx = deltas;
+
+ for (i = 0; i < nevents; i++, dx++) {
+ motion.x = *dx;
+ motion.y = 0;
+ time += 12; /* pretend 80Hz data */
+
+ filter_dispatch(filter, &motion, NULL, time);
+
+ printf("%d %.3f %.3f\n", i, motion.x, *dx);
+ }
+}
+
+static void
+print_accel_func(struct motion_filter *filter)
+{
+ double vel;
+
+ printf("# gnuplot:\n");
+ printf("# set xlabel \"speed\"\n");
+ printf("# set ylabel \"raw accel factor\"\n");
+ printf("# set style data lines\n");
+ printf("# plot \"gnuplot.data\" using 1:2\n");
+ for (vel = 0.0; vel < 3.0; vel += .0001) {
+ double result = pointer_accel_profile_linear(filter,
+ NULL,
+ vel,
+ 0 /* time */);
+ printf("%.4f\t%.4f\n", vel, result);
+ }
+}
+
+static void
+usage(void)
+{
+ printf("Usage: %s [options] [dx1] [dx2] [...] > gnuplot.data\n", program_invocation_short_name);
+ printf("\n"
+ "Options:\n"
+ "--mode= \n"
+ " motion ... print motion to accelerated motion (default)\n"
+ " delta ... print delta to accelerated delta\n"
+ " accel ... print accel factor\n"
+ " sequence ... print motion for custom delta sequence\n"
+ "--maxdx=\n ... in motion mode only. Stop increasing dx at maxdx\n"
+ "--steps=\n ... in motion and delta modes only. Increase dx by step each round\n"
+ "--speed=\n ... accel speed [-1, 1], default 0\n"
+ "\n"
+ "If extra arguments are present and mode is not given, mode defaults to 'sequence'\n"
+ "and the arguments are interpreted as sequence of delta x coordinates\n"
+ "\n"
+ "If stdin is a pipe, mode defaults to 'sequence' and the pipe is read \n"
+ "for delta coordinates\n"
+ "\n"
+ "Output best viewed with gnuplot. See output for gnuplot commands\n");
+}
+
+int
+main(int argc, char **argv)
+{
+ struct motion_filter *filter;
+ double step = 0.1,
+ max_dx = 10;
+ int nevents = 0;
+ bool print_accel = false,
+ print_motion = true,
+ print_delta = false,
+ print_sequence = false;
+ double custom_deltas[1024];
+ double speed = 0.0;
+ enum {
+ OPT_MODE = 1,
+ OPT_NEVENTS,
+ OPT_MAXDX,
+ OPT_STEP,
+ OPT_SPEED,
+ };
+
+ filter = create_pointer_accelerator_filter(pointer_accel_profile_linear);
+ assert(filter != NULL);
+
+ while (1) {
+ int c;
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"mode", 1, 0, OPT_MODE },
+ {"nevents", 1, 0, OPT_NEVENTS },
+ {"maxdx", 1, 0, OPT_MAXDX },
+ {"step", 1, 0, OPT_STEP },
+ {"speed", 1, 0, OPT_SPEED },
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case OPT_MODE:
+ if (strcmp(optarg, "accel") == 0)
+ print_accel = true;
+ else if (strcmp(optarg, "motion") == 0)
+ print_motion = true;
+ else if (strcmp(optarg, "delta") == 0)
+ print_delta = true;
+ else if (strcmp(optarg, "sequence") == 0)
+ print_sequence = true;
+ else {
+ usage();
+ return 1;
+ }
+ break;
+ case OPT_NEVENTS:
+ nevents = atoi(optarg);
+ if (nevents == 0) {
+ usage();
+ return 1;
+ }
+ break;
+ case OPT_MAXDX:
+ max_dx = strtod(optarg, NULL);
+ if (max_dx == 0.0) {
+ usage();
+ return 1;
+ }
+ break;
+ case OPT_STEP:
+ step = strtod(optarg, NULL);
+ if (step == 0.0) {
+ usage();
+ return 1;
+ }
+ break;
+ case OPT_SPEED:
+ speed = strtod(optarg, NULL);
+ break;
+ default:
+ usage();
+ exit(1);
+ break;
+ }
+ }
+
+ filter_set_speed(filter, speed);
+
+ if (!isatty(STDIN_FILENO)) {
+ char buf[12];
+ print_sequence = true;
+ print_motion = false;
+ nevents = 0;
+ memset(custom_deltas, 0, sizeof(custom_deltas));
+
+ while(fgets(buf, sizeof(buf), stdin) && nevents < 1024) {
+ custom_deltas[nevents++] = strtod(buf, NULL);
+ }
+ } else if (optind < argc) {
+ print_sequence = true;
+ print_motion = false;
+ nevents = 0;
+ memset(custom_deltas, 0, sizeof(custom_deltas));
+ while (optind < argc)
+ custom_deltas[nevents++] = strtod(argv[optind++], NULL);
+ }
+
+ if (print_accel)
+ print_accel_func(filter);
+ else if (print_delta)
+ print_ptraccel_deltas(filter, step);
+ else if (print_motion)
+ print_ptraccel_movement(filter, nevents, max_dx, step);
+ else if (print_sequence)
+ print_ptraccel_sequence(filter, nevents, custom_deltas);
+
+ filter_destroy(filter);
+
+ return 0;
+}
diff --git a/tools/shared.c b/tools/shared.c
index 2cff52c4..9ccd5dda 100644
--- a/tools/shared.c
+++ b/tools/shared.c
@@ -30,6 +30,8 @@
#include
#include
+#include
+
#include "shared.h"
enum options {
@@ -43,7 +45,12 @@ enum options {
OPT_NATURAL_SCROLL_DISABLE,
OPT_LEFT_HANDED_ENABLE,
OPT_LEFT_HANDED_DISABLE,
+ OPT_MIDDLEBUTTON_ENABLE,
+ OPT_MIDDLEBUTTON_DISABLE,
OPT_CLICK_METHOD,
+ OPT_SCROLL_METHOD,
+ OPT_SCROLL_BUTTON,
+ OPT_SPEED,
};
static void
@@ -70,7 +77,12 @@ tools_usage()
"--disable-natural-scrolling.... enable/disable natural scrolling\n"
"--enable-left-handed\n"
"--disable-left-handed.... enable/disable left-handed button configuration\n"
+ "--enable-middlebutton\n"
+ "--disable-middlebutton.... enable/disable middle button emulation\n"
"--set-click-method=[none|clickfinger|buttonareas] .... set the desired click method\n"
+ "--set-scroll-method=[none|twofinger|edge|button] ... set the desired scroll method\n"
+ "--set-scroll-button=BTN_MIDDLE ... set the button to the given button code\n"
+ "--set-speed=.... set pointer acceleration speed\n"
"\n"
"These options apply to all applicable devices, if a feature\n"
"is not explicitly specified it is left at each device's default.\n"
@@ -88,9 +100,13 @@ tools_init_options(struct tools_options *options)
options->tapping = -1;
options->natural_scroll = -1;
options->left_handed = -1;
+ options->middlebutton = -1;
options->click_method = -1;
+ options->scroll_method = -1;
+ options->scroll_button = -1;
options->backend = BACKEND_UDEV;
options->seat = "seat0";
+ options->speed = 0.0;
}
int
@@ -110,7 +126,12 @@ tools_parse_args(int argc, char **argv, struct tools_options *options)
{ "disable-natural-scrolling", 0, 0, OPT_NATURAL_SCROLL_DISABLE },
{ "enable-left-handed", 0, 0, OPT_LEFT_HANDED_ENABLE },
{ "disable-left-handed", 0, 0, OPT_LEFT_HANDED_DISABLE },
+ { "enable-middlebutton", 0, 0, OPT_MIDDLEBUTTON_ENABLE },
+ { "disable-middlebutton", 0, 0, OPT_MIDDLEBUTTON_DISABLE },
{ "set-click-method", 1, 0, OPT_CLICK_METHOD },
+ { "set-scroll-method", 1, 0, OPT_SCROLL_METHOD },
+ { "set-scroll-button", 1, 0, OPT_SCROLL_BUTTON },
+ { "speed", 1, 0, OPT_SPEED },
{ 0, 0, 0, 0}
};
@@ -157,6 +178,12 @@ tools_parse_args(int argc, char **argv, struct tools_options *options)
case OPT_LEFT_HANDED_DISABLE:
options->left_handed = 0;
break;
+ case OPT_MIDDLEBUTTON_ENABLE:
+ options->middlebutton = 1;
+ break;
+ case OPT_MIDDLEBUTTON_DISABLE:
+ options->middlebutton = 0;
+ break;
case OPT_CLICK_METHOD:
if (!optarg) {
tools_usage();
@@ -176,6 +203,50 @@ tools_parse_args(int argc, char **argv, struct tools_options *options)
return 1;
}
break;
+ case OPT_SCROLL_METHOD:
+ if (!optarg) {
+ tools_usage();
+ return 1;
+ }
+ if (strcmp(optarg, "none") == 0) {
+ options->scroll_method =
+ LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
+ } else if (strcmp(optarg, "twofinger") == 0) {
+ options->scroll_method =
+ LIBINPUT_CONFIG_SCROLL_2FG;
+ } else if (strcmp(optarg, "edge") == 0) {
+ options->scroll_method =
+ LIBINPUT_CONFIG_SCROLL_EDGE;
+ } else if (strcmp(optarg, "button") == 0) {
+ options->scroll_method =
+ LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
+ } else {
+ tools_usage();
+ return 1;
+ }
+ break;
+ case OPT_SCROLL_BUTTON:
+ if (!optarg) {
+ tools_usage();
+ return 1;
+ }
+ options->scroll_button =
+ libevdev_event_code_from_name(EV_KEY,
+ optarg);
+ if (options->scroll_button == -1) {
+ fprintf(stderr,
+ "Invalid button %s\n",
+ optarg);
+ return 1;
+ }
+ break;
+ case OPT_SPEED:
+ if (!optarg) {
+ tools_usage();
+ return 1;
+ }
+ options->speed = atof(optarg);
+ break;
default:
tools_usage();
return 1;
@@ -286,7 +357,21 @@ tools_device_apply_config(struct libinput_device *device,
options->natural_scroll);
if (options->left_handed != -1)
libinput_device_config_left_handed_set(device, options->left_handed);
+ if (options->middlebutton != -1)
+ libinput_device_config_middle_emulation_set_enabled(device,
+ options->middlebutton);
if (options->click_method != -1)
libinput_device_config_click_set_method(device, options->click_method);
+
+ if (options->scroll_method != -1)
+ libinput_device_config_scroll_set_method(device,
+ options->scroll_method);
+ if (options->scroll_button != -1)
+ libinput_device_config_scroll_set_button(device,
+ options->scroll_button);
+
+ if (libinput_device_config_accel_is_available(device))
+ libinput_device_config_accel_set_speed(device,
+ options->speed);
}
diff --git a/tools/shared.h b/tools/shared.h
index fcf748fd..a1aec462 100644
--- a/tools/shared.h
+++ b/tools/shared.h
@@ -39,7 +39,11 @@ struct tools_options {
int tapping;
int natural_scroll;
int left_handed;
+ int middlebutton;
enum libinput_config_click_method click_method;
+ enum libinput_config_scroll_method scroll_method;
+ int scroll_button;
+ double speed;
};
void tools_init_options(struct tools_options *options);
diff --git a/udev/90-libinput-model-quirks.hwdb b/udev/90-libinput-model-quirks.hwdb
new file mode 100644
index 00000000..048e5cc6
--- /dev/null
+++ b/udev/90-libinput-model-quirks.hwdb
@@ -0,0 +1,66 @@
+# Do not edit this file, it will be overwritten on update
+#
+# This file contains hwdb matches for libinput model-specific quirks.
+# The contents of this file are a contract between libinput, udev rules and
+# the hwdb.
+# IT IS NOT A STABLE API AND SUBJECT TO CHANGE AT ANY TIME
+
+# The lookup keys are composed in:
+# 90-libinput-model-quirks.rules
+#
+# Match string formats:
+# libinput:
+# libinput:name::dmi:
+
+#
+# Sort by brand, model
+
+##########################################
+# Google
+##########################################
+
+# The various chromebooks, info from modinfo chromeos_laptop, touchpad names
+# extrapolated from the chromiumos touchad-tests repo
+# https://chromium.googlesource.com/chromiumos/platform/touchpad-tests
+libinput:name:Cypress APA Trackpad (cyapa):dmi:*pnFalco:pvr*
+libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pn*Mario*:
+libinput:name:Cypress APA Trackpad (cyapa):dmi:*pn*Butterfly*:
+libinput:name:Cypress APA Trackpad (cyapa):dmi:*pn*Peppy*:
+libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pn*ZGB*:
+libinput:name:Cypress APA Trackpad (cyapa):dmi:*pn*Parrot*:
+libinput:name:Cypress APA Trackpad (cyapa):dmi:*bvn*coreboot*:pn*Leon*:
+libinput:name:Cypress APA Trackpad (cyapa):dmi:*bvn*coreboot*:pn*Falco*:
+libinput:name:Cypress APA Trackpad (cyapa):dmi:*bvn*coreboot*:pn*Wolf*:
+libinput:name:Cypress APA Trackpad (cyapa):dmi:*svn*GOOGLE*:pn*Link*:
+libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pn*Alex*:
+libinput:name:Cypress APA Trackpad (cyapa):dmi:*svn*SAMSUNG*:pn*Lumpy*:
+libinput:name:Atmel maXTouch Touchpad:dmi:*svn*GOOGLE*:pn*Samus*:
+ LIBINPUT_MODEL_CHROMEBOOK=1
+
+##########################################
+# LENOVO
+##########################################
+
+# X230 (Tablet)
+libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO:*:pvrThinkPadX230*
+ LIBINPUT_MODEL_LENOVO_X230=1
+
+##########################################
+# System76
+##########################################
+
+# Bonobo Professional
+libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnSystem76*pvrbonp5*
+ LIBINPUT_MODEL_SYSTEM76_BONOBO=1
+
+# Clevo
+libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pnW740SU*rnW740SU*
+ LIBINPUT_MODEL_CLEVO_W740SU=1
+
+# Galago Ultra Pro
+libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnSystem76*pvrgalu1*
+ LIBINPUT_MODEL_SYSTEM76_GALAGO=1
+
+# Kudu Professional
+libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnSystem76*pvrkudp1*
+ LIBINPUT_MODEL_SYSTEM76_KUDU=1
diff --git a/udev/90-libinput-model-quirks.rules b/udev/90-libinput-model-quirks.rules
new file mode 100644
index 00000000..4b988748
--- /dev/null
+++ b/udev/90-libinput-model-quirks.rules
@@ -0,0 +1,25 @@
+# Do not edit this file, it will be overwritten on update
+#
+# This file contains lookup rules for libinput model-specific quirks.
+# The contents of this file are a contract between libinput, udev rules and
+# the hwdb.
+# IT IS NOT A STABLE API AND SUBJECT TO CHANGE AT ANY TIME
+#
+# The hwdb database is in:
+# 90-libinput-model-quirks.hwdb
+
+ACTION!="add|change", GOTO="libinput_model_quirks_end"
+KERNEL!="event*", GOTO="libinput_model_quirks_end"
+
+# hwdb matches:
+#
+# libinput:
+IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=libinput:", \
+ GOTO="libinput_model_quirks_end"
+
+# libinput:name::dmi:
+KERNELS=="input*", \
+ IMPORT{builtin}="hwdb 'libinput:name:$attr{name}:$attr{[dmi/id]modalias}'", \
+ GOTO="libinput_model_quirks_end"
+
+LABEL="libinput_model_quirks_end"
diff --git a/udev/Makefile.am b/udev/Makefile.am
index 3691172c..7d19809d 100644
--- a/udev/Makefile.am
+++ b/udev/Makefile.am
@@ -6,4 +6,10 @@ libinput_device_group_CFLAGS = $(LIBUDEV_CFLAGS) $(GCC_CFLAGS)
libinput_device_group_LDADD = $(LIBUDEV_LIBS)
udev_rulesdir=$(UDEV_DIR)/rules.d
-dist_udev_rules_DATA = 80-libinput-device-groups.rules
+dist_udev_rules_DATA = \
+ 80-libinput-device-groups.rules \
+ 90-libinput-model-quirks.rules
+
+udev_hwdbdir=$(UDEV_DIR)/hwdb.d
+dist_udev_hwdb_DATA = \
+ 90-libinput-model-quirks.hwdb