summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2015-05-22 14:21:21 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2015-05-22 14:21:21 +1000
commit087d25a54e5c507325a678f01fec4e5909d478e1 (patch)
tree4db61dd4d45005e48561891f667e519823650413
parent29ba32b330e390db2b79bf6982d1376906392370 (diff)
parenta6be43990cb31c004697bfa3c433d4c9bb9a5d3d (diff)
Merge branch 'master' into tablet-support
-rw-r--r--COPYING1
-rw-r--r--configure.ac19
-rw-r--r--doc/Makefile.am8
-rw-r--r--doc/device-configuration-via-udev.dox24
-rw-r--r--doc/middle-button-emulation.svg1315
-rw-r--r--doc/palm-detection.dox30
-rw-r--r--doc/scrolling.dox34
-rw-r--r--doc/svg/palm-detection.svg213
-rw-r--r--doc/tapping.dox4
-rw-r--r--doc/test-suite.dox92
-rw-r--r--doc/touchpad-tap-state-machine.svg1639
-rw-r--r--include/linux/input.h28
-rw-r--r--src/Makefile.am15
-rw-r--r--src/evdev-middle-button.c716
-rw-r--r--src/evdev-mt-touchpad-buttons.c127
-rw-r--r--src/evdev-mt-touchpad-edge-scroll.c60
-rw-r--r--src/evdev-mt-touchpad-gestures.c33
-rw-r--r--src/evdev-mt-touchpad-tap.c168
-rw-r--r--src/evdev-mt-touchpad.c405
-rw-r--r--src/evdev-mt-touchpad.h45
-rw-r--r--src/evdev-tablet.c1
-rw-r--r--src/evdev.c402
-rw-r--r--src/evdev.h77
-rw-r--r--src/filter.c52
-rw-r--r--src/filter.h5
-rw-r--r--src/libinput-private.h102
-rw-r--r--src/libinput-util.c31
-rw-r--r--src/libinput-util.h58
-rw-r--r--src/libinput.c129
-rw-r--r--src/libinput.h140
-rw-r--r--src/libinput.sym15
-rw-r--r--src/path.c2
-rw-r--r--src/timer.c12
-rw-r--r--test/Makefile.am34
-rw-r--r--test/build-pedantic.c3
-rw-r--r--test/device.c143
-rw-r--r--test/keyboard.c90
-rw-r--r--test/litest-atmel-hover.c149
-rw-r--r--test/litest-bcm5974.c2
-rw-r--r--test/litest-keyboard-razer-blackwidow.c350
-rw-r--r--test/litest-logitech-trackball.c64
-rw-r--r--test/litest-mouse-roccat.c205
-rw-r--r--test/litest-ms-surface-cover.c11
-rw-r--r--test/litest-selftest.c369
-rw-r--r--test/litest-wheel-only.c68
-rw-r--r--test/litest.c941
-rw-r--r--test/litest.h215
-rw-r--r--test/log.c11
-rw-r--r--test/misc.c37
-rw-r--r--test/path.c7
-rw-r--r--test/pointer.c579
-rw-r--r--test/tablet.c6
-rw-r--r--test/touch.c98
-rw-r--r--test/touchpad.c1332
-rw-r--r--test/trackpoint.c7
-rw-r--r--test/udev.c6
-rw-r--r--test/valgrind.suppressions8
-rw-r--r--tools/.gitignore3
-rw-r--r--tools/Makefile.am23
-rw-r--r--tools/event-gui.c18
-rw-r--r--tools/libinput-debug-events.man31
-rw-r--r--tools/libinput-list-devices.c313
-rw-r--r--tools/libinput-list-devices.man37
-rw-r--r--tools/ptraccel-debug.c300
-rw-r--r--tools/shared.c85
-rw-r--r--tools/shared.h4
-rw-r--r--udev/90-libinput-model-quirks.hwdb66
-rw-r--r--udev/90-libinput-model-quirks.rules25
-rw-r--r--udev/Makefile.am8
69 files changed, 10178 insertions, 1472 deletions
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.</dt>
ID_INPUT_TOUCHSCREEN, ID_INPUT_TABLET, ID_INPUT_JOYSTICK,
ID_INPUT_ACCELEROMETER</dt>
<dd>If any of the above is set, libinput initializes the device as the given
-type, see @ref udev_device_type.</dd>
+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.</dd>
<dt>WL_SEAT</dt>
<dd>Assigns the logical seat for this device. See
libinput_seat_get_logical_name()
@@ -57,6 +60,13 @@ See @ref motion_normalization for details.
<dd>The angle in degrees for each click on a mouse wheel. See
libinput_pointer_get_axis_source() for details.
</dd>
+<dt>POINTINGSTICK_CONST_ACCEL</dt>
+<dd>A constant (linear) acceleration factor to apply to pointingstick deltas
+to normalize them.
+<dt>LIBINPUT_MODEL_*</dt>
+<dd><b>This prefix is reserved as private API, do not use.</b>. See @ref
+model_specific_configuration for details.
+</dd>
</dl>
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 <b>LIBINPUT_MODEL_</b> for
+model-specific configuration. <b>This prefix is reserved as private API, do
+not use.</b>
+
+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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1674px" height="2151px" version="1.1">
+ <defs/>
+ <g transform="translate(0.5,0.5)">
+ <ellipse cx="748" cy="32" rx="50" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g transform="translate(729,25)">
+ <switch>
+ <foreignObject pointer-events="all" width="38" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 38px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ IDLE<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="19" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 429.17 99 C 432.06 95.86 436.17 94.05 440.5 94 L 475.51 94 C 479.84 94.05 483.95 95.86 486.84 99 L 507.43 122 C 508.01 123.28 508.01 124.72 507.43 126 L 486.84 149 C 483.95 152.14 479.84 153.95 475.51 154 L 440.5 154 C 436.17 153.95 432.06 152.14 429.17 149 L 408.58 126 C 408 124.72 408 123.28 408.58 122 L 429.17 99 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(425,110)">
+ <switch>
+ <foreignObject pointer-events="all" width="65" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 65px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ left button<br />
+ press<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="33" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1009.17 99 C 1012.06 95.86 1016.17 94.05 1020.5 94 L 1055.51 94 C 1059.84 94.05 1063.95 95.86 1066.84 99 L 1087.43 122 C 1088.01 123.28 1088.01 124.72 1087.43 126 L 1066.84 149 C 1063.95 152.14 1059.84 153.95 1055.51 154 L 1020.5 154 C 1016.17 153.95 1012.06 152.14 1009.17 149 L 988.58 126 C 988 124.72 988 123.28 988.58 122 L 1009.17 99 Z" fill="#9ac7bf" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1002,110)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ right button<br />
+ press<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="36" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 729.17 239 C 732.06 235.86 736.17 234.05 740.5 234 L 775.51 234 C 779.84 234.05 783.95 235.86 786.84 239 L 807.43 262 C 808.01 263.28 808.01 264.72 807.43 266 L 786.84 289 C 783.95 292.14 779.84 293.95 775.51 294 L 740.5 294 C 736.17 293.95 732.06 292.14 729.17 289 L 708.58 266 C 708 264.72 708 263.28 708.58 262 L 729.17 239 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(720,250)">
+ <switch>
+ <foreignObject pointer-events="all" width="75" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 75px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ other button<br />
+ press<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="38" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 749 62 L 756.7 227.64" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 756.95 232.88 L 753.13 226.05 L 756.7 227.64 L 760.12 225.73 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 698 48 L 514.07 106.08" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 509.07 107.66 L 514.69 102.22 L 514.07 106.08 L 516.8 108.89 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 798 48 L 981.93 106.08" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 986.93 107.66 L 979.2 108.89 L 981.93 106.08 L 981.31 102.22 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <ellipse cx="398" cy="224" rx="50" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g transform="translate(355,217)">
+ <switch>
+ <foreignObject pointer-events="all" width="85" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 85px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ LEFT_DOWN</div>
+ </div>
+ </foreignObject>
+ <text x="43" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 440 154 L 419.28 188.54" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 416.58 193.04 L 417.18 185.24 L 419.28 188.54 L 423.18 188.84 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <ellipse cx="1068" cy="224" rx="50" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g transform="translate(1021,217)">
+ <switch>
+ <foreignObject pointer-events="all" width="93" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 93px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ RIGHT_DOWN</div>
+ </div>
+ </foreignObject>
+ <text x="47" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <ellipse cx="748" cy="604" rx="50" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g transform="translate(697,597)">
+ <switch>
+ <foreignObject pointer-events="all" width="102" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 102px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ PASSTHROUGH</div>
+ </div>
+ </foreignObject>
+ <text x="51" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1047 154 L 1057.17 187.9" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1058.68 192.93 L 1053.31 187.23 L 1057.17 187.9 L 1060.02 185.22 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 209.17 329 C 212.06 325.86 216.17 324.05 220.5 324 L 255.51 324 C 259.84 324.05 263.95 325.86 266.84 329 L 287.43 352 C 288.01 353.28 288.01 354.72 287.43 356 L 266.84 379 C 263.95 382.14 259.84 383.95 255.51 384 L 220.5 384 C 216.17 383.95 212.06 382.14 209.17 379 L 188.58 356 C 188 354.72 188 353.28 188.58 352 L 209.17 329 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(205,340)">
+ <switch>
+ <foreignObject pointer-events="all" width="65" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 65px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ left button<br />
+ release<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="33" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 361 254 L 279.94 319.98" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 275.87 323.29 L 279.09 316.16 L 279.94 319.98 L 283.51 321.59 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 329.17 339 C 332.06 335.86 336.17 334.05 340.5 334 L 375.51 334 C 379.84 334.05 383.95 335.86 386.84 339 L 407.43 362 C 408.01 363.28 408.01 364.72 407.43 366 L 386.84 389 C 383.95 392.14 379.84 393.95 375.51 394 L 340.5 394 C 336.17 393.95 332.06 392.14 329.17 389 L 308.58 366 C 308 364.72 308 363.28 308.58 362 L 329.17 339 Z" fill="#9ac7bf" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(322,350)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ right button<br />
+ press<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="36" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 390 254 L 368.76 327.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 367.31 332.93 L 365.88 325.23 L 368.76 327.88 L 372.61 327.17 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 479.17 339 C 482.06 335.86 486.17 334.05 490.5 334 L 525.51 334 C 529.84 334.05 533.95 335.86 536.84 339 L 557.43 362 C 558.01 363.28 558.01 364.72 557.43 366 L 536.84 389 C 533.95 392.14 529.84 393.95 525.51 394 L 490.5 394 C 486.17 393.95 482.06 392.14 479.17 389 L 458.58 366 C 458 364.72 458 363.28 458.58 362 L 479.17 339 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(482,357)">
+ <switch>
+ <foreignObject pointer-events="all" width="51" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 51px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ timeout</div>
+ </div>
+ </foreignObject>
+ <text x="26" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 421 254 L 480.06 329" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 483.31 333.12 L 476.23 329.79 L 480.06 329 L 481.73 325.46 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 448 236 L 551.8 260.54" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 556.91 261.74 L 549.29 263.54 L 551.8 260.54 L 550.9 256.73 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 147.61 409.1 C 148.78 405.9 150.67 404.04 152.67 404.12 L 222.53 404.12 C 224.13 404.08 225.65 404.63 226.65 405.61 C 227.64 406.59 227.99 407.88 227.6 409.1 L 208.36 458.9 C 207.19 462.1 205.3 463.96 203.3 463.88 L 132.42 463.88 C 131.01 463.69 129.77 463.05 128.99 462.1 C 128.22 461.15 128 459.99 128.37 458.9 Z" fill="#ea6b66" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(142,420)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN_LEFT<br />
+ PRESS<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="36" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 47.61 499.1 C 48.78 495.9 50.67 494.04 52.67 494.12 L 122.53 494.12 C 124.13 494.08 125.65 494.63 126.65 495.61 C 127.64 496.59 127.99 497.88 127.6 499.1 L 108.36 548.9 C 107.19 552.1 105.3 553.96 103.3 553.88 L 32.42 553.88 C 31.01 553.69 29.77 553.05 28.99 552.1 C 28.22 551.15 28 549.99 28.37 548.9 Z" fill="#ea6b66" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(42,510)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN_LEFT<br />
+ RELEASE<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="36" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 216 384 L 204.82 398.91" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 201.67 403.11 L 203.07 395.41 L 204.82 398.91 L 208.67 399.61 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 145 464 L 115.77 489.79" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 111.84 493.26 L 114.77 486 L 115.77 489.79 L 119.4 491.25 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 517.61 429.1 C 518.78 425.9 520.67 424.04 522.67 424.12 L 592.53 424.12 C 594.13 424.08 595.65 424.63 596.65 425.61 C 597.64 426.59 597.99 427.88 597.6 429.1 L 578.36 478.9 C 577.19 482.1 575.3 483.96 573.3 483.88 L 502.42 483.88 C 501.01 483.69 499.77 483.05 498.99 482.1 C 498.22 481.15 498 479.99 498.37 478.9 Z" fill="#ea6b66" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(512,440)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN_LEFT<br />
+ PRESS<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="36" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 528 394 L 544.47 418.7" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 547.38 423.07 L 540.58 419.19 L 544.47 418.7 L 546.41 415.3 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <ellipse cx="808" cy="894" rx="50" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g transform="translate(779,887)">
+ <switch>
+ <foreignObject pointer-events="all" width="57" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 57px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ MIDDLE</div>
+ </div>
+ </foreignObject>
+ <text x="29" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 767.61 719.1 C 768.78 715.9 770.67 714.04 772.67 714.12 L 842.53 714.12 C 844.13 714.08 845.65 714.63 846.65 715.61 C 847.64 716.59 847.99 717.88 847.6 719.1 L 828.36 768.9 C 827.19 772.1 825.3 773.96 823.3 773.88 L 752.42 773.88 C 751.01 773.69 749.77 773.05 748.99 772.1 C 748.22 771.15 748 769.99 748.37 768.9 Z" fill="#ea6b66" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(754,730)">
+ <switch>
+ <foreignObject pointer-events="all" width="87" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 87px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN_MIDDLE<br />
+ PRESS<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="44" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 393 394 L 758.18 709.83" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 762.15 713.27 L 754.57 711.34 L 758.18 709.83 L 759.15 706.04 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 800 774 L 805.58 857.65" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 805.93 862.88 L 801.97 856.13 L 805.58 857.65 L 808.95 855.67 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 709.17 989 C 712.06 985.86 716.17 984.05 720.5 984 L 755.51 984 C 759.84 984.05 763.95 985.86 766.84 989 L 787.43 1012 C 788.01 1013.28 788.01 1014.72 787.43 1016 L 766.84 1039 C 763.95 1042.14 759.84 1043.95 755.51 1044 L 720.5 1044 C 716.17 1043.95 712.06 1042.14 709.17 1039 L 688.58 1016 C 688 1014.72 688 1013.28 688.58 1012 L 709.17 989 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(705,1000)">
+ <switch>
+ <foreignObject pointer-events="all" width="65" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 65px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ left button<br />
+ release<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="33" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 849.17 989 C 852.06 985.86 856.17 984.05 860.5 984 L 895.51 984 C 899.84 984.05 903.95 985.86 906.84 989 L 927.43 1012 C 928.01 1013.28 928.01 1014.72 927.43 1016 L 906.84 1039 C 903.95 1042.14 899.84 1043.95 895.51 1044 L 860.5 1044 C 856.17 1043.95 852.06 1042.14 849.17 1039 L 828.58 1016 C 828 1014.72 828 1013.28 828.58 1012 L 849.17 989 Z" fill="#9ac7bf" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(842,1000)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ right button<br />
+ release<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="36" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 826 924 L 857.79 978.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 860.44 983.03 L 853.89 978.75 L 857.79 978.5 L 859.93 975.22 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 791 924 L 759.21 978.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 756.56 983.03 L 757.07 975.22 L 759.21 978.5 L 763.11 978.75 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 857.61 1109.1 C 858.78 1105.9 860.67 1104.04 862.67 1104.12 L 932.53 1104.12 C 934.13 1104.08 935.65 1104.63 936.65 1105.61 C 937.64 1106.59 937.99 1107.88 937.6 1109.1 L 918.36 1158.9 C 917.19 1162.1 915.3 1163.96 913.3 1163.88 L 842.42 1163.88 C 841.01 1163.69 839.77 1163.05 838.99 1162.1 C 838.22 1161.15 838 1159.99 838.37 1158.9 Z" fill="#ea6b66" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(844,1120)">
+ <switch>
+ <foreignObject pointer-events="all" width="87" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 87px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN_MIDDLE<br />
+ RELEASE<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="44" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 707.61 1109.1 C 708.78 1105.9 710.67 1104.04 712.67 1104.12 L 782.53 1104.12 C 784.13 1104.08 785.65 1104.63 786.65 1105.61 C 787.64 1106.59 787.99 1107.88 787.6 1109.1 L 768.36 1158.9 C 767.19 1162.1 765.3 1163.96 763.3 1163.88 L 692.42 1163.88 C 691.01 1163.69 689.77 1163.05 688.99 1162.1 C 688.22 1161.15 688 1159.99 688.37 1158.9 Z" fill="#ea6b66" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(694,1120)">
+ <switch>
+ <foreignObject pointer-events="all" width="87" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 87px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN_MIDDLE<br />
+ RELEASE<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="44" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 738 1044 L 738 1097.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 738 1102.88 L 734.5 1095.88 L 738 1097.63 L 741.5 1095.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 881 1044 L 885.47 1097.65" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 885.91 1102.89 L 881.84 1096.2 L 885.47 1097.65 L 888.81 1095.62 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <ellipse cx="888" cy="1224" rx="50" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g transform="translate(840,1217)">
+ <switch>
+ <foreignObject pointer-events="all" width="95" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 95px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ LUP_PENDING</div>
+ </div>
+ </foreignObject>
+ <text x="48" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <ellipse cx="738" cy="1224" rx="50" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g transform="translate(689,1217)">
+ <switch>
+ <foreignObject pointer-events="all" width="97" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 97px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ RUP_PENDING</div>
+ </div>
+ </foreignObject>
+ <text x="49" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 738 1164 L 738 1187.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 738 1192.88 L 734.5 1185.88 L 738 1187.63 L 741.5 1185.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 888 1164 L 888 1187.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 888 1192.88 L 884.5 1185.88 L 888 1187.63 L 891.5 1185.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 829.17 1319 C 832.06 1315.86 836.17 1314.05 840.5 1314 L 875.51 1314 C 879.84 1314.05 883.95 1315.86 886.84 1319 L 907.43 1342 C 908.01 1343.28 908.01 1344.72 907.43 1346 L 886.84 1369 C 883.95 1372.14 879.84 1373.95 875.51 1374 L 840.5 1374 C 836.17 1373.95 832.06 1372.14 829.17 1369 L 808.58 1346 C 808 1344.72 808 1343.28 808.58 1342 L 829.17 1319 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(825,1330)">
+ <switch>
+ <foreignObject pointer-events="all" width="65" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 65px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ left button<br />
+ release<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="33" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 719.17 1319 C 722.06 1315.86 726.17 1314.05 730.5 1314 L 765.51 1314 C 769.84 1314.05 773.95 1315.86 776.84 1319 L 797.43 1342 C 798.01 1343.28 798.01 1344.72 797.43 1346 L 776.84 1369 C 773.95 1372.14 769.84 1373.95 765.51 1374 L 730.5 1374 C 726.17 1373.95 722.06 1372.14 719.17 1369 L 698.58 1346 C 698 1344.72 698 1343.28 698.58 1342 L 719.17 1319 Z" fill="#9ac7bf" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(712,1330)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ right button<br />
+ release<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="36" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 741 1254 L 745.47 1307.65" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 745.91 1312.89 L 741.84 1306.2 L 745.47 1307.65 L 748.81 1305.62 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 881 1254 L 867.54 1307.82" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 866.27 1312.92 L 864.57 1305.28 L 867.54 1307.82 L 871.36 1306.97 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <ellipse cx="808" cy="1454" rx="50" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g transform="translate(789,1447)">
+ <switch>
+ <foreignObject pointer-events="all" width="38" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 38px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ IDLE<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="19" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 765 1374 L 788.97 1418.4" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 791.47 1423.02 L 785.06 1418.52 L 788.97 1418.4 L 791.22 1415.19 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 845 1374 L 824.66 1418.21" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 822.47 1422.98 L 822.21 1415.16 L 824.66 1418.21 L 828.57 1418.09 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1009.17 1089 C 1012.06 1085.86 1016.17 1084.05 1020.5 1084 L 1055.51 1084 C 1059.84 1084.05 1063.95 1085.86 1066.84 1089 L 1087.43 1112 C 1088.01 1113.28 1088.01 1114.72 1087.43 1116 L 1066.84 1139 C 1063.95 1142.14 1059.84 1143.95 1055.51 1144 L 1020.5 1144 C 1016.17 1143.95 1012.06 1142.14 1009.17 1139 L 988.58 1116 C 988 1114.72 988 1113.28 988.58 1112 L 1009.17 1089 Z" fill="#9ac7bf" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1002,1100)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ right button<br />
+ press<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="36" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 929 1194 L 991.87 1147.77" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 996.1 1144.66 L 992.53 1151.63 L 991.87 1147.77 L 988.39 1145.99 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1019 1084 L 820.48 779.34" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 817.61 774.94 L 824.36 778.89 L 820.48 779.34 L 818.5 782.71 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 545.17 1089 C 548.06 1085.86 552.17 1084.05 556.5 1084 L 591.51 1084 C 595.84 1084.05 599.95 1085.86 602.84 1089 L 623.43 1112 C 624.01 1113.28 624.01 1114.72 623.43 1116 L 602.84 1139 C 599.95 1142.14 595.84 1143.95 591.51 1144 L 556.5 1144 C 552.17 1143.95 548.06 1142.14 545.17 1139 L 524.58 1116 C 524 1114.72 524 1113.28 524.58 1112 L 545.17 1089 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(541,1100)">
+ <switch>
+ <foreignObject pointer-events="all" width="65" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 65px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ left button<br />
+ press<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="33" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 693 1194 L 624.28 1147.57" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 619.93 1144.63 L 627.69 1145.64 L 624.28 1147.57 L 623.77 1151.44 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 592 1084 L 776.7 779.44" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 779.42 774.96 L 778.78 782.76 L 776.7 779.44 L 772.8 779.13 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 78 494 L 78 42 Q 78 32 88 32 L 691.63 32" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 696.88 32 L 689.88 35.5 L 691.63 32 L 689.88 28.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 579.17 249 C 582.06 245.86 586.17 244.05 590.5 244 L 625.51 244 C 629.84 244.05 633.95 245.86 636.84 249 L 657.43 272 C 658.01 273.28 658.01 274.72 657.43 276 L 636.84 299 C 633.95 302.14 629.84 303.95 625.51 304 L 590.5 304 C 586.17 303.95 582.06 302.14 579.17 299 L 558.58 276 C 558 274.72 558 273.28 558.58 272 L 579.17 249 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(570,260)">
+ <switch>
+ <foreignObject pointer-events="all" width="75" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 75px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ other button<br />
+ press<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="38" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 588 484 L 702.91 570.18" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 707.11 573.33 L 699.41 571.93 L 702.91 570.18 L 703.61 566.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1109.17 349 C 1112.06 345.86 1116.17 344.05 1120.5 344 L 1155.51 344 C 1159.84 344.05 1163.95 345.86 1166.84 349 L 1187.43 372 C 1188.01 373.28 1188.01 374.72 1187.43 376 L 1166.84 399 C 1163.95 402.14 1159.84 403.95 1155.51 404 L 1120.5 404 C 1116.17 403.95 1112.06 402.14 1109.17 399 L 1088.58 376 C 1088 374.72 1088 373.28 1088.58 372 L 1109.17 349 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1105,360)">
+ <switch>
+ <foreignObject pointer-events="all" width="65" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 65px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ left button<br />
+ press<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="33" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1249.17 349 C 1252.06 345.86 1256.17 344.05 1260.5 344 L 1295.51 344 C 1299.84 344.05 1303.95 345.86 1306.84 349 L 1327.43 372 C 1328.01 373.28 1328.01 374.72 1327.43 376 L 1306.84 399 C 1303.95 402.14 1299.84 403.95 1295.51 404 L 1260.5 404 C 1256.17 403.95 1252.06 402.14 1249.17 399 L 1228.58 376 C 1228 374.72 1228 373.28 1228.58 372 L 1249.17 349 Z" fill="#9ac7bf" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1242,360)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ right button<br />
+ release<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="36" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 979.17 349 C 982.06 345.86 986.17 344.05 990.5 344 L 1025.51 344 C 1029.84 344.05 1033.95 345.86 1036.84 349 L 1057.43 372 C 1058.01 373.28 1058.01 374.72 1057.43 376 L 1036.84 399 C 1033.95 402.14 1029.84 403.95 1025.51 404 L 990.5 404 C 986.17 403.95 982.06 402.14 979.17 399 L 958.58 376 C 958 374.72 958 373.28 958.58 372 L 979.17 349 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(982,367)">
+ <switch>
+ <foreignObject pointer-events="all" width="51" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 51px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ timeout</div>
+ </div>
+ </foreignObject>
+ <text x="26" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 869.17 249 C 872.06 245.86 876.17 244.05 880.5 244 L 915.51 244 C 919.84 244.05 923.95 245.86 926.84 249 L 947.43 272 C 948.01 273.28 948.01 274.72 947.43 276 L 926.84 299 C 923.95 302.14 919.84 303.95 915.51 304 L 880.5 304 C 876.17 303.95 872.06 302.14 869.17 299 L 848.58 276 C 848 274.72 848 273.28 848.58 272 L 869.17 249 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(860,260)">
+ <switch>
+ <foreignObject pointer-events="all" width="75" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 75px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ other button<br />
+ press<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="38" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1018 239 L 954.12 257.25" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 949.07 258.69 L 954.84 253.4 L 954.12 257.25 L 956.77 260.14 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 827.61 449.1 C 828.78 445.9 830.67 444.04 832.67 444.12 L 902.53 444.12 C 904.13 444.08 905.65 444.63 906.65 445.61 C 907.64 446.59 907.99 447.88 907.6 449.1 L 888.36 498.9 C 887.19 502.1 885.3 503.96 883.3 503.88 L 812.42 503.88 C 811.01 503.69 809.77 503.05 808.99 502.1 C 808.22 501.15 808 499.99 808.37 498.9 Z" fill="#ea6b66" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(818,460)">
+ <switch>
+ <foreignObject pointer-events="all" width="80" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 80px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN_RIGHT<br />
+ PRESS<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="40" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 963 404 L 908.3 440.47" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 903.93 443.38 L 907.81 436.58 L 908.3 440.47 L 911.7 442.41 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 833 504 L 777.14 569.17" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 773.73 573.15 L 775.63 565.56 L 777.14 569.17 L 780.94 570.11 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1056 254 L 1022.37 338.09" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1020.42 342.96 L 1019.77 335.16 L 1022.37 338.09 L 1026.26 337.76 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1110 254 L 1230.82 340.3" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1235.09 343.35 L 1227.36 342.13 L 1230.82 340.3 L 1231.43 336.43 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1307.61 449.1 C 1308.78 445.9 1310.67 444.04 1312.67 444.12 L 1382.53 444.12 C 1384.13 444.08 1385.65 444.63 1386.65 445.61 C 1387.64 446.59 1387.99 447.88 1387.6 449.1 L 1368.36 498.9 C 1367.19 502.1 1365.3 503.96 1363.3 503.88 L 1292.42 503.88 C 1291.01 503.69 1289.77 503.05 1288.99 502.1 C 1288.22 501.15 1288 499.99 1288.37 498.9 Z" fill="#ea6b66" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1298,460)">
+ <switch>
+ <foreignObject pointer-events="all" width="80" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 80px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN_RIGHT<br />
+ PRESS<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="40" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1377.61 539.1 C 1378.78 535.9 1380.67 534.04 1382.67 534.12 L 1452.53 534.12 C 1454.13 534.08 1455.65 534.63 1456.65 535.61 C 1457.64 536.59 1457.99 537.88 1457.6 539.1 L 1438.36 588.9 C 1437.19 592.1 1435.3 593.96 1433.3 593.88 L 1362.42 593.88 C 1361.01 593.69 1359.77 593.05 1358.99 592.1 C 1358.22 591.15 1358 589.99 1358.37 588.9 Z" fill="#ea6b66" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1368,550)">
+ <switch>
+ <foreignObject pointer-events="all" width="80" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 80px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN_RIGHT<br />
+ RELEASE<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="40" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1296 404 L 1316.72 438.54" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1319.42 443.04 L 1312.82 438.84 L 1316.72 438.54 L 1318.82 435.24 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1362 504 L 1381.13 528.95" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1384.32 533.11 L 1377.28 529.69 L 1381.13 528.95 L 1382.84 525.43 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1458 564 L 1488 564 Q 1498 564 1498 554 L 1498 42 Q 1498 32 1488 32 L 804.37 32" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 799.12 32 L 806.12 28.5 L 804.37 32 L 806.12 35.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1082 254 L 1121.31 338.23" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1123.53 342.99 L 1117.4 338.12 L 1121.31 338.23 L 1123.74 335.16 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1110 404 L 830.3 709.3" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 826.76 713.18 L 828.9 705.65 L 830.3 709.3 L 834.06 710.38 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1049.17 909 C 1052.06 905.86 1056.17 904.05 1060.5 904 L 1095.51 904 C 1099.84 904.05 1103.95 905.86 1106.84 909 L 1127.43 932 C 1128.01 933.28 1128.01 934.72 1127.43 936 L 1106.84 959 C 1103.95 962.14 1099.84 963.95 1095.51 964 L 1060.5 964 C 1056.17 963.95 1052.06 962.14 1049.17 959 L 1028.58 936 C 1028 934.72 1028 933.28 1028.58 932 L 1049.17 909 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1040,920)">
+ <switch>
+ <foreignObject pointer-events="all" width="75" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 75px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ other button<br />
+ press<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="38" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 858 902 L 1021.7 926.07" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1026.89 926.84 L 1019.46 929.28 L 1021.7 926.07 L 1020.48 922.36 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1217.61 969.1 C 1218.78 965.9 1220.67 964.04 1222.67 964.12 L 1292.53 964.12 C 1294.13 964.08 1295.65 964.63 1296.65 965.61 C 1297.64 966.59 1297.99 967.88 1297.6 969.1 L 1278.36 1018.9 C 1277.19 1022.1 1275.3 1023.96 1273.3 1023.88 L 1202.42 1023.88 C 1201.01 1023.69 1199.77 1023.05 1198.99 1022.1 C 1198.22 1021.15 1198 1019.99 1198.37 1018.9 Z" fill="#ea6b66" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1204,980)">
+ <switch>
+ <foreignObject pointer-events="all" width="87" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 87px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN_MIDDLE<br />
+ RELEASE<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="44" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1128 952 L 1191.98 973.93" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1196.94 975.64 L 1189.19 976.68 L 1191.98 973.93 L 1191.46 970.06 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 408 674 L 18 674 Q 8 674 8 664 L 8 42 Q 8 32 18 32 L 691.63 32" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 696.88 32 L 689.88 35.5 L 691.63 32 L 689.88 28.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <ellipse cx="1248" cy="1174" rx="50" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g transform="translate(1208,1167)">
+ <switch>
+ <foreignObject pointer-events="all" width="80" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 80px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ IGNORE_LR</div>
+ </div>
+ </foreignObject>
+ <text x="40" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1127.17 1384 C 1130.06 1380.86 1134.17 1379.05 1138.5 1379 L 1173.51 1379 C 1177.84 1379.05 1181.95 1380.86 1184.84 1384 L 1205.43 1407 C 1206.01 1408.28 1206.01 1409.72 1205.43 1411 L 1184.84 1434 C 1181.95 1437.14 1177.84 1438.95 1173.51 1439 L 1138.5 1439 C 1134.17 1438.95 1130.06 1437.14 1127.17 1434 L 1106.58 1411 C 1106 1409.72 1106 1408.28 1106.58 1407 L 1127.17 1384 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1123,1395)">
+ <switch>
+ <foreignObject pointer-events="all" width="65" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 65px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ left button<br />
+ release<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="33" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1277.17 1384 C 1280.06 1380.86 1284.17 1379.05 1288.5 1379 L 1323.51 1379 C 1327.84 1379.05 1331.95 1380.86 1334.84 1384 L 1355.43 1407 C 1356.01 1408.28 1356.01 1409.72 1355.43 1411 L 1334.84 1434 C 1331.95 1437.14 1327.84 1438.95 1323.51 1439 L 1288.5 1439 C 1284.17 1438.95 1280.06 1437.14 1277.17 1434 L 1256.58 1411 C 1256 1409.72 1256 1408.28 1256.58 1407 L 1277.17 1384 Z" fill="#9ac7bf" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1270,1395)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ right button<br />
+ release<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="36" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1449.17 1249 C 1452.06 1245.86 1456.17 1244.05 1460.5 1244 L 1495.51 1244 C 1499.84 1244.05 1503.95 1245.86 1506.84 1249 L 1527.43 1272 C 1528.01 1273.28 1528.01 1274.72 1527.43 1276 L 1506.84 1299 C 1503.95 1302.14 1499.84 1303.95 1495.51 1304 L 1460.5 1304 C 1456.17 1303.95 1452.06 1302.14 1449.17 1299 L 1428.58 1276 C 1428 1274.72 1428 1273.28 1428.58 1272 L 1449.17 1249 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1440,1260)">
+ <switch>
+ <foreignObject pointer-events="all" width="75" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 75px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ other button<br />
+ event<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="38" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1298 1196 L 1422.15 1249.48" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1426.97 1251.56 L 1419.16 1252 L 1422.15 1249.48 L 1421.93 1245.57 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1255 1204 L 1297.45 1372.82" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1298.73 1377.92 L 1293.63 1371.98 L 1297.45 1372.82 L 1300.41 1370.27 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1236 1204 L 1170.31 1373.06" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1168.4 1377.96 L 1167.68 1370.17 L 1170.31 1373.06 L 1174.2 1372.7 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <ellipse cx="1236" cy="1959" rx="50" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g transform="translate(1185,1952)">
+ <switch>
+ <foreignObject pointer-events="all" width="102" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 102px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ PASSTHROUGH</div>
+ </div>
+ </foreignObject>
+ <text x="51" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <ellipse cx="1136" cy="1579" rx="50" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g transform="translate(1099,1572)">
+ <switch>
+ <foreignObject pointer-events="all" width="73" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 73px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ IGNORE_R</div>
+ </div>
+ </foreignObject>
+ <text x="37" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <ellipse cx="1306" cy="1579" rx="50" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g transform="translate(1270,1572)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ IGNORE_L</div>
+ </div>
+ </foreignObject>
+ <text x="36" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1306 1439 L 1306 1542.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1306 1547.88 L 1302.5 1540.88 L 1306 1542.63 L 1309.5 1540.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1153 1439 L 1140.75 1542.68" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1140.13 1547.89 L 1137.48 1540.53 L 1140.75 1542.68 L 1144.43 1541.35 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1017.17 1744 C 1020.06 1740.86 1024.17 1739.05 1028.5 1739 L 1063.51 1739 C 1067.84 1739.05 1071.95 1740.86 1074.84 1744 L 1095.43 1767 C 1096.01 1768.28 1096.01 1769.72 1095.43 1771 L 1074.84 1794 C 1071.95 1797.14 1067.84 1798.95 1063.51 1799 L 1028.5 1799 C 1024.17 1798.95 1020.06 1797.14 1017.17 1794 L 996.58 1771 C 996 1769.72 996 1768.28 996.58 1767 L 1017.17 1744 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1013,1755)">
+ <switch>
+ <foreignObject pointer-events="all" width="65" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 65px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ left button<br />
+ press<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="33" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 885.61 1694.1 C 886.78 1690.9 888.67 1689.04 890.67 1689.12 L 960.53 1689.12 C 962.13 1689.08 963.65 1689.63 964.65 1690.61 C 965.64 1691.59 965.99 1692.88 965.6 1694.1 L 946.36 1743.9 C 945.19 1747.1 943.3 1748.96 941.3 1748.88 L 870.42 1748.88 C 869.01 1748.69 867.77 1748.05 866.99 1747.1 C 866.22 1746.15 866 1744.99 866.37 1743.9 Z" fill="#ea6b66" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(880,1705)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN_LEFT<br />
+ PRESS<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="36" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 996 1750 L 971.91 1740.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 967.04 1738.42 L 974.84 1737.77 L 971.91 1740.37 L 972.24 1744.26 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 963 1689 L 1083.62 1612.41" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1088.06 1609.6 L 1084.02 1616.31 L 1083.62 1612.41 L 1080.27 1610.4 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1122 1609 L 1062.74 1733.25" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1060.48 1737.99 L 1060.34 1730.17 L 1062.74 1733.25 L 1066.65 1733.18 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1177.17 1664 C 1180.06 1660.86 1184.17 1659.05 1188.5 1659 L 1223.51 1659 C 1227.84 1659.05 1231.95 1660.86 1234.84 1664 L 1255.43 1687 C 1256.01 1688.28 1256.01 1689.72 1255.43 1691 L 1234.84 1714 C 1231.95 1717.14 1227.84 1718.95 1223.51 1719 L 1188.5 1719 C 1184.17 1718.95 1180.06 1717.14 1177.17 1714 L 1156.58 1691 C 1156 1689.72 1156 1688.28 1156.58 1687 L 1177.17 1664 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1173,1675)">
+ <switch>
+ <foreignObject pointer-events="all" width="65" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 65px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ left button<br />
+ release<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="33" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1155 1609 L 1183.57 1653.64" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1186.4 1658.06 L 1179.68 1654.05 L 1183.57 1653.64 L 1185.57 1650.28 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 885.61 1614.1 C 886.78 1610.9 888.67 1609.04 890.67 1609.12 L 960.53 1609.12 C 962.13 1609.08 963.65 1609.63 964.65 1610.61 C 965.64 1611.59 965.99 1612.88 965.6 1614.1 L 946.36 1663.9 C 945.19 1667.1 943.3 1668.96 941.3 1668.88 L 870.42 1668.88 C 869.01 1668.69 867.77 1668.05 866.99 1667.1 C 866.22 1666.15 866 1664.99 866.37 1663.9 Z" fill="#ea6b66" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(880,1625)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN_LEFT<br />
+ RELEASE<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="36" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1156 1680 L 972.28 1649.06" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 967.1 1648.19 L 974.59 1645.9 L 972.28 1649.06 L 973.42 1652.8 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 966 1625 L 1079.85 1594.64" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1084.92 1593.29 L 1079.06 1598.47 L 1079.85 1594.64 L 1077.25 1591.71 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1127.17 1794 C 1130.06 1790.86 1134.17 1789.05 1138.5 1789 L 1173.51 1789 C 1177.84 1789.05 1181.95 1790.86 1184.84 1794 L 1205.43 1817 C 1206.01 1818.28 1206.01 1819.72 1205.43 1821 L 1184.84 1844 C 1181.95 1847.14 1177.84 1848.95 1173.51 1849 L 1138.5 1849 C 1134.17 1848.95 1130.06 1847.14 1127.17 1844 L 1106.58 1821 C 1106 1819.72 1106 1818.28 1106.58 1817 L 1127.17 1794 Z" fill="#9ac7bf" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1120,1805)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ right button<br />
+ release<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="36" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1139 1609 L 1153.47 1782.65" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1153.91 1787.89 L 1149.84 1781.2 L 1153.47 1782.65 L 1156.81 1780.62 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1173 1849 L 1215.83 1923.48" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1218.44 1928.03 L 1211.92 1923.71 L 1215.83 1923.48 L 1217.99 1920.22 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1367.17 1684 C 1370.06 1680.86 1374.17 1679.05 1378.5 1679 L 1413.51 1679 C 1417.84 1679.05 1421.95 1680.86 1424.84 1684 L 1445.43 1707 C 1446.01 1708.28 1446.01 1709.72 1445.43 1711 L 1424.84 1734 C 1421.95 1737.14 1417.84 1738.95 1413.51 1739 L 1378.5 1739 C 1374.17 1738.95 1370.06 1737.14 1367.17 1734 L 1346.58 1711 C 1346 1709.72 1346 1708.28 1346.58 1707 L 1367.17 1684 Z" fill="#9ac7bf" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1360,1695)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ right button<br />
+ release<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="36" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1517.17 1714 C 1520.06 1710.86 1524.17 1709.05 1528.5 1709 L 1563.51 1709 C 1567.84 1709.05 1571.95 1710.86 1574.84 1714 L 1595.43 1737 C 1596.01 1738.28 1596.01 1739.72 1595.43 1741 L 1574.84 1764 C 1571.95 1767.14 1567.84 1768.95 1563.51 1769 L 1528.5 1769 C 1524.17 1768.95 1520.06 1767.14 1517.17 1764 L 1496.58 1741 C 1496 1739.72 1496 1738.28 1496.58 1737 L 1517.17 1714 Z" fill="#9ac7bf" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1510,1725)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ right button<br />
+ press<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="36" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1590.61 1624.1 C 1591.78 1620.9 1593.67 1619.04 1595.67 1619.12 L 1665.53 1619.12 C 1667.13 1619.08 1668.65 1619.63 1669.65 1620.61 C 1670.64 1621.59 1670.99 1622.88 1670.6 1624.1 L 1651.36 1673.9 C 1650.19 1677.1 1648.3 1678.96 1646.3 1678.88 L 1575.42 1678.88 C 1574.01 1678.69 1572.77 1678.05 1571.99 1677.1 C 1571.22 1676.15 1571 1674.99 1571.37 1673.9 Z" fill="#ea6b66" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1581,1635)">
+ <switch>
+ <foreignObject pointer-events="all" width="80" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 80px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN_RIGHT<br />
+ PRESS<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="40" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1590.61 1544.1 C 1591.78 1540.9 1593.67 1539.04 1595.67 1539.12 L 1665.53 1539.12 C 1667.13 1539.08 1668.65 1539.63 1669.65 1540.61 C 1670.64 1541.59 1670.99 1542.88 1670.6 1544.1 L 1651.36 1593.9 C 1650.19 1597.1 1648.3 1598.96 1646.3 1598.88 L 1575.42 1598.88 C 1574.01 1598.69 1572.77 1598.05 1571.99 1597.1 C 1571.22 1596.15 1571 1594.99 1571.37 1593.9 Z" fill="#ea6b66" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1581,1555)">
+ <switch>
+ <foreignObject pointer-events="all" width="80" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 80px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN_RIGHT<br />
+ RELEASE<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="40" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1444 1679 L 1567.59 1602.36" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1572.05 1599.59 L 1567.95 1606.25 L 1567.59 1602.36 L 1564.26 1600.3 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1571 1571 L 1362.37 1576.82" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1357.12 1576.97 L 1364.02 1573.27 L 1362.37 1576.82 L 1364.21 1580.27 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1327 1609 L 1371.4 1673.75" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1374.37 1678.08 L 1367.52 1674.28 L 1371.4 1673.75 L 1373.3 1670.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1571 1709 L 1591.92 1683.89" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1595.28 1679.86 L 1593.49 1687.48 L 1591.92 1683.89 L 1588.11 1683 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1571 1638 L 1362.21 1591.39" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1357.09 1590.24 L 1364.69 1588.35 L 1362.21 1591.39 L 1363.16 1595.18 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1351 1609 L 1495.7 1705.47" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1500.07 1708.38 L 1492.3 1707.41 L 1495.7 1705.47 L 1496.19 1701.58 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1307.17 1794 C 1310.06 1790.86 1314.17 1789.05 1318.5 1789 L 1353.51 1789 C 1357.84 1789.05 1361.95 1790.86 1364.84 1794 L 1385.43 1817 C 1386.01 1818.28 1386.01 1819.72 1385.43 1821 L 1364.84 1844 C 1361.95 1847.14 1357.84 1848.95 1353.51 1849 L 1318.5 1849 C 1314.17 1848.95 1310.06 1847.14 1307.17 1844 L 1286.58 1821 C 1286 1819.72 1286 1818.28 1286.58 1817 L 1307.17 1794 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1303,1805)">
+ <switch>
+ <foreignObject pointer-events="all" width="65" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 65px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ left button<br />
+ release<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="33" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1310 1609 L 1331.23 1782.68" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1331.86 1787.89 L 1327.54 1781.37 L 1331.23 1782.68 L 1334.49 1780.52 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1314 1849 L 1260.7 1923.81" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1257.65 1928.09 L 1258.86 1920.36 L 1260.7 1923.81 L 1264.56 1924.42 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 979.17 1239 C 982.06 1235.86 986.17 1234.05 990.5 1234 L 1025.51 1234 C 1029.84 1234.05 1033.95 1235.86 1036.84 1239 L 1057.43 1262 C 1058.01 1263.28 1058.01 1264.72 1057.43 1266 L 1036.84 1289 C 1033.95 1292.14 1029.84 1293.95 1025.51 1294 L 990.5 1294 C 986.17 1293.95 982.06 1292.14 979.17 1289 L 958.58 1266 C 958 1264.72 958 1263.28 958.58 1262 L 979.17 1239 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(970,1250)">
+ <switch>
+ <foreignObject pointer-events="all" width="75" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 75px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ other button<br />
+ press<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="38" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 938 1240 L 951.99 1244.9" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 956.94 1246.63 L 949.18 1247.62 L 951.99 1244.9 L 951.49 1241.01 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 959.17 1319 C 962.06 1315.86 966.17 1314.05 970.5 1314 L 1005.51 1314 C 1009.84 1314.05 1013.95 1315.86 1016.84 1319 L 1037.43 1342 C 1038.01 1343.28 1038.01 1344.72 1037.43 1346 L 1016.84 1369 C 1013.95 1372.14 1009.84 1373.95 1005.51 1374 L 970.5 1374 C 966.17 1373.95 962.06 1372.14 959.17 1369 L 938.58 1346 C 938 1344.72 938 1343.28 938.58 1342 L 959.17 1319 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(950,1330)">
+ <switch>
+ <foreignObject pointer-events="all" width="75" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 75px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ other button<br />
+ press<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="38" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 788 1248 L 932.26 1317.24" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 936.99 1319.52 L 929.17 1319.64 L 932.26 1317.24 L 932.2 1313.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1427.61 1149.1 C 1428.78 1145.9 1430.67 1144.04 1432.67 1144.12 L 1502.53 1144.12 C 1504.13 1144.08 1505.65 1144.63 1506.65 1145.61 C 1507.64 1146.59 1507.99 1147.88 1507.6 1149.1 L 1488.36 1198.9 C 1487.19 1202.1 1485.3 1203.96 1483.3 1203.88 L 1412.42 1203.88 C 1411.01 1203.69 1409.77 1203.05 1408.99 1202.1 C 1408.22 1201.15 1408 1199.99 1408.37 1198.9 Z" fill="#f8cecc" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1437,1160)">
+ <switch>
+ <foreignObject pointer-events="all" width="41" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 41px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN*<br />
+ event<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="21" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1472 1244 L 1465.25 1210.24" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1464.22 1205.1 L 1469.02 1211.27 L 1465.25 1210.24 L 1462.16 1212.65 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1408 1174 L 1304.37 1174" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1299.12 1174 L 1306.12 1170.5 L 1304.37 1174 L 1306.12 1177.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1307.61 1049.1 C 1308.78 1045.9 1310.67 1044.04 1312.67 1044.12 L 1382.53 1044.12 C 1384.13 1044.08 1385.65 1044.63 1386.65 1045.61 C 1387.64 1046.59 1387.99 1047.88 1387.6 1049.1 L 1368.36 1098.9 C 1367.19 1102.1 1365.3 1103.96 1363.3 1103.88 L 1292.42 1103.88 C 1291.01 1103.69 1289.77 1103.05 1288.99 1102.1 C 1288.22 1101.15 1288 1099.99 1288.37 1098.9 Z" fill="#f8cecc" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1317,1060)">
+ <switch>
+ <foreignObject pointer-events="all" width="41" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 41px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN*<br />
+ event<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="21" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1282 1024 L 1299.29 1039.72" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1303.17 1043.25 L 1295.64 1041.13 L 1299.29 1039.72 L 1300.35 1035.95 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1311 1104 L 1279.26 1139.27" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1275.75 1143.17 L 1277.83 1135.62 L 1279.26 1139.27 L 1283.03 1140.31 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1125.61 1249.1 C 1126.78 1245.9 1128.67 1244.04 1130.67 1244.12 L 1200.53 1244.12 C 1202.13 1244.08 1203.65 1244.63 1204.65 1245.61 C 1205.64 1246.59 1205.99 1247.88 1205.6 1249.1 L 1186.36 1298.9 C 1185.19 1302.1 1183.3 1303.96 1181.3 1303.88 L 1110.42 1303.88 C 1109.01 1303.69 1107.77 1303.05 1106.99 1302.1 C 1106.22 1301.15 1106 1299.99 1106.37 1298.9 Z" fill="#f8cecc" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1135,1260)">
+ <switch>
+ <foreignObject pointer-events="all" width="41" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 41px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN*<br />
+ event<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="21" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1058 1268 L 1099.64 1270.6" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1104.88 1270.93 L 1097.68 1273.99 L 1099.64 1270.6 L 1098.12 1267 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1171 1304 L 1288.2 1543.28" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1290.51 1548 L 1284.29 1543.25 L 1288.2 1543.28 L 1290.57 1540.17 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1015.61 1399.1 C 1016.78 1395.9 1018.67 1394.04 1020.67 1394.12 L 1090.53 1394.12 C 1092.13 1394.08 1093.65 1394.63 1094.65 1395.61 C 1095.64 1396.59 1095.99 1397.88 1095.6 1399.1 L 1076.36 1448.9 C 1075.19 1452.1 1073.3 1453.96 1071.3 1453.88 L 1000.42 1453.88 C 999.01 1453.69 997.77 1453.05 996.99 1452.1 C 996.22 1451.15 996 1449.99 996.37 1448.9 Z" fill="#f8cecc" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1025,1410)">
+ <switch>
+ <foreignObject pointer-events="all" width="41" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 41px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN*<br />
+ event<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="21" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1010 1374 L 1020.35 1388.78" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1023.36 1393.08 L 1016.48 1389.36 L 1020.35 1388.78 L 1022.21 1385.34 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1064 1454 L 1115.81 1543.49" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1118.44 1548.03 L 1111.9 1543.73 L 1115.81 1543.49 L 1117.96 1540.22 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 717.61 459.1 C 718.78 455.9 720.67 454.04 722.67 454.12 L 792.53 454.12 C 794.13 454.08 795.65 454.63 796.65 455.61 C 797.64 456.59 797.99 457.88 797.6 459.1 L 778.36 508.9 C 777.19 512.1 775.3 513.96 773.3 513.88 L 702.42 513.88 C 701.01 513.69 699.77 513.05 698.99 512.1 C 698.22 511.15 698 509.99 698.37 508.9 Z" fill="#f8cecc" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(727,470)">
+ <switch>
+ <foreignObject pointer-events="all" width="41" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 41px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN*<br />
+ event<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="21" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 757 294 L 749.32 447.64" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 749.06 452.88 L 745.91 445.72 L 749.32 447.64 L 752.9 446.07 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 748 514 L 748 567.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 748 572.88 L 744.5 565.88 L 748 567.63 L 751.5 565.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 643.61 369.1 C 644.78 365.9 646.67 364.04 648.67 364.12 L 718.53 364.12 C 720.13 364.08 721.65 364.63 722.65 365.61 C 723.64 366.59 723.99 367.88 723.6 369.1 L 704.36 418.9 C 703.19 422.1 701.3 423.96 699.3 423.88 L 628.42 423.88 C 627.01 423.69 625.77 423.05 624.99 422.1 C 624.22 421.15 624 419.99 624.37 418.9 Z" fill="#ea6b66" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(638,380)">
+ <switch>
+ <foreignObject pointer-events="all" width="71" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 71px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN_LEFT<br />
+ PRESS<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="36" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 625 304 L 654.93 358.42" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 657.46 363.02 L 651.02 358.57 L 654.93 358.42 L 657.15 355.2 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 699 424 L 719.02 449.03" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 722.3 453.13 L 715.2 449.85 L 719.02 449.03 L 720.66 445.47 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 807.61 349.1 C 808.78 345.9 810.67 344.04 812.67 344.12 L 882.53 344.12 C 884.13 344.08 885.65 344.63 886.65 345.61 C 887.64 346.59 887.99 347.88 887.6 349.1 L 868.36 398.9 C 867.19 402.1 865.3 403.96 863.3 403.88 L 792.42 403.88 C 791.01 403.69 789.77 403.05 788.99 402.1 C 788.22 401.15 788 399.99 788.37 398.9 Z" fill="#ea6b66" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(798,360)">
+ <switch>
+ <foreignObject pointer-events="all" width="80" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 80px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN_RIGHT<br />
+ PRESS<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="40" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 880 304 L 859.28 338.54" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 856.58 343.04 L 857.18 335.24 L 859.28 338.54 L 863.18 338.84 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 814 404 L 777.04 449.08" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 773.71 453.14 L 775.44 445.5 L 777.04 449.08 L 780.85 449.94 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 579.17 696 C 582.06 692.86 586.17 691.05 590.5 691 L 625.51 691 C 629.84 691.05 633.95 692.86 636.84 696 L 657.43 719 C 658.01 720.28 658.01 721.72 657.43 723 L 636.84 746 C 633.95 749.14 629.84 750.95 625.51 751 L 590.5 751 C 586.17 750.95 582.06 749.14 579.17 746 L 558.58 723 C 558 721.72 558 720.28 558.58 719 L 579.17 696 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(574,707)">
+ <switch>
+ <foreignObject pointer-events="all" width="67" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 67px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ any button<br />
+ event<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="34" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 437.61 779.1 C 438.78 775.9 440.67 774.04 442.67 774.12 L 512.53 774.12 C 514.13 774.08 515.65 774.63 516.65 775.61 C 517.64 776.59 517.99 777.88 517.6 779.1 L 498.36 828.9 C 497.19 832.1 495.3 833.96 493.3 833.88 L 422.42 833.88 C 421.01 833.69 419.77 833.05 418.99 832.1 C 418.22 831.15 418 829.99 418.37 828.9 Z" fill="#f8cecc" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(447,790)">
+ <switch>
+ <foreignObject pointer-events="all" width="41" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 41px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN*<br />
+ event<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="21" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 712 634 L 648.88 686.91" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 644.86 690.28 L 647.97 683.1 L 648.88 686.91 L 652.47 688.47 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 558 750 L 523.46 770.72" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 518.96 773.42 L 523.16 766.82 L 523.46 770.72 L 526.76 772.82 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 458 624 L 508 674 L 458 724 L 408 674 Z" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(425,660)">
+ <switch>
+ <foreignObject pointer-events="all" width="66" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 66px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ all buttons<br />
+ up?<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="33" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 466 774 L 462.51 730.35" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 462.09 725.11 L 466.14 731.81 L 462.51 730.35 L 459.16 732.37 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 508 662 L 691.81 617.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 696.91 616.26 L 690.93 621.31 L 691.81 617.5 L 689.29 614.51 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <rect x="593" y="637" width="30" height="20" fill="none" stroke="none" pointer-events="none"/>
+ <g transform="translate(595,640)">
+ <switch>
+ <foreignObject pointer-events="all" width="15" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; white-space: nowrap;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ no</div>
+ </div>
+ </foreignObject>
+ <text x="8" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <rect x="245" y="671" width="30" height="20" fill="none" stroke="none" pointer-events="none"/>
+ <g transform="translate(247,674)">
+ <switch>
+ <foreignObject pointer-events="all" width="21" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; white-space: nowrap;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ yes</div>
+ </div>
+ </foreignObject>
+ <text x="11" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1592.17 1459 C 1595.06 1455.86 1599.17 1454.05 1603.5 1454 L 1638.51 1454 C 1642.84 1454.05 1646.95 1455.86 1649.84 1459 L 1670.43 1482 C 1671.01 1483.28 1671.01 1484.72 1670.43 1486 L 1649.84 1509 C 1646.95 1512.14 1642.84 1513.95 1638.51 1514 L 1603.5 1514 C 1599.17 1513.95 1595.06 1512.14 1592.17 1509 L 1571.58 1486 C 1571 1484.72 1571 1483.28 1571.58 1482 L 1592.17 1459 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1583,1470)">
+ <switch>
+ <foreignObject pointer-events="all" width="75" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 75px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ other button<br />
+ event<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="38" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1557.61 1369.1 C 1558.78 1365.9 1560.67 1364.04 1562.67 1364.12 L 1632.53 1364.12 C 1634.13 1364.08 1635.65 1364.63 1636.65 1365.61 C 1637.64 1366.59 1637.99 1367.88 1637.6 1369.1 L 1618.36 1418.9 C 1617.19 1422.1 1615.3 1423.96 1613.3 1423.88 L 1542.42 1423.88 C 1541.01 1423.69 1539.77 1423.05 1538.99 1422.1 C 1538.22 1421.15 1538 1419.99 1538.37 1418.9 Z" fill="#f8cecc" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1567,1380)">
+ <switch>
+ <foreignObject pointer-events="all" width="41" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 41px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN*<br />
+ event<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="21" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1356 1564 L 1564.9 1500.84" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1569.93 1499.32 L 1564.24 1504.7 L 1564.9 1500.84 L 1562.22 1498 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1605 1454 L 1591.14 1429.54" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1588.55 1424.97 L 1595.05 1429.34 L 1591.14 1429.54 L 1588.96 1432.79 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1542 1424 L 1357.32 1545.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1352.93 1548.39 L 1356.86 1541.61 L 1357.32 1545.5 L 1360.71 1547.46 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 899.17 1539 C 902.06 1535.86 906.17 1534.05 910.5 1534 L 945.51 1534 C 949.84 1534.05 953.95 1535.86 956.84 1539 L 977.43 1562 C 978.01 1563.28 978.01 1564.72 977.43 1566 L 956.84 1589 C 953.95 1592.14 949.84 1593.95 945.51 1594 L 910.5 1594 C 906.17 1593.95 902.06 1592.14 899.17 1589 L 878.58 1566 C 878 1564.72 878 1563.28 878.58 1562 L 899.17 1539 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(890,1550)">
+ <switch>
+ <foreignObject pointer-events="all" width="75" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 75px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ other button<br />
+ event<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="38" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 917.61 1459.1 C 918.78 1455.9 920.67 1454.04 922.67 1454.12 L 992.53 1454.12 C 994.13 1454.08 995.65 1454.63 996.65 1455.61 C 997.64 1456.59 997.99 1457.88 997.6 1459.1 L 978.36 1508.9 C 977.19 1512.1 975.3 1513.96 973.3 1513.88 L 902.42 1513.88 C 901.01 1513.69 899.77 1513.05 898.99 1512.1 C 898.22 1511.15 898 1509.99 898.37 1508.9 Z" fill="#f8cecc" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(927,1470)">
+ <switch>
+ <foreignObject pointer-events="all" width="41" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 41px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN*<br />
+ event<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="21" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1086 1576 L 984.35 1568.47" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 979.11 1568.08 L 986.35 1565.11 L 984.35 1568.47 L 985.84 1572.09 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 936 1534 L 939.46 1520.18" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 940.73 1515.08 L 942.43 1522.72 L 939.46 1520.18 L 935.64 1521.03 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 998 1509 L 1080.33 1551.1" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1085 1553.49 L 1077.18 1553.42 L 1080.33 1551.1 L 1080.37 1547.19 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1068.17 1995 C 1071.06 1991.86 1075.17 1990.05 1079.5 1990 L 1114.51 1990 C 1118.84 1990.05 1122.95 1991.86 1125.84 1995 L 1146.43 2018 C 1147.01 2019.28 1147.01 2020.72 1146.43 2022 L 1125.84 2045 C 1122.95 2048.14 1118.84 2049.95 1114.51 2050 L 1079.5 2050 C 1075.17 2049.95 1071.06 2048.14 1068.17 2045 L 1047.58 2022 C 1047 2020.72 1047 2019.28 1047.58 2018 L 1068.17 1995 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(1063,2006)">
+ <switch>
+ <foreignObject pointer-events="all" width="67" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 67px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ any button<br />
+ event<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="34" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 899.61 2094.1 C 900.78 2090.9 902.67 2089.04 904.67 2089.12 L 974.53 2089.12 C 976.13 2089.08 977.65 2089.63 978.65 2090.61 C 979.64 2091.59 979.99 2092.88 979.6 2094.1 L 960.36 2143.9 C 959.19 2147.1 957.3 2148.96 955.3 2148.88 L 884.42 2148.88 C 883.01 2148.69 881.77 2148.05 880.99 2147.1 C 880.22 2146.15 880 2144.99 880.37 2143.9 Z" fill="#f8cecc" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(909,2105)">
+ <switch>
+ <foreignObject pointer-events="all" width="41" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 41px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ BTN*<br />
+ event<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="21" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 1186 1981 L 1152.84 1995.46" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1148.02 1997.55 L 1153.04 1991.55 L 1152.84 1995.46 L 1155.84 1997.96 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1047 2049 L 985.47 2085.74" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 980.96 2088.43 L 985.18 2081.83 L 985.47 2085.74 L 988.76 2087.84 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 930 1944 L 980 1994 L 930 2044 L 880 1994 Z" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(897,1980)">
+ <switch>
+ <foreignObject pointer-events="all" width="66" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 66px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ all buttons<br />
+ up?<br />
+ </div>
+ </div>
+ </foreignObject>
+ <text x="33" y="22" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 930 2089 L 930 2050.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 930 2045.12 L 933.5 2052.12 L 930 2050.37 L 926.5 2052.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 980 1988 L 1179.67 1965.71" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1184.89 1965.12 L 1178.32 1969.38 L 1179.67 1965.71 L 1177.54 1962.42 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <rect x="1067" y="1929" width="30" height="20" fill="none" stroke="none" pointer-events="none"/>
+ <g transform="translate(1069,1932)">
+ <switch>
+ <foreignObject pointer-events="all" width="15" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; white-space: nowrap;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ no</div>
+ </div>
+ </foreignObject>
+ <text x="8" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <ellipse cx="738" cy="1989" rx="50" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g transform="translate(719,1982)">
+ <switch>
+ <foreignObject pointer-events="all" width="38" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 38px; white-space: normal; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;" xmlns="http://www.w3.org/1999/xhtml">
+ IDLE</div>
+ </div>
+ </foreignObject>
+ <text x="19" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ <path d="M 880 1993 L 794.36 1990.21" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 789.12 1990.04 L 796.23 1986.77 L 794.36 1990.21 L 796 1993.76 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <g transform="translate(827,1997)">
+ <switch>
+ <foreignObject pointer-events="all" width="21" height="17" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; white-space: nowrap; text-align: center;">
+ <div style="display:inline-block;text-align:inherit;text-decoration:inherit;background-color:#ffffff;" xmlns="http://www.w3.org/1999/xhtml">
+ yes</div>
+ </div>
+ </foreignObject>
+ <text x="11" y="15" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">
+ [Not supported by viewer]</text>
+ </switch>
+ </g>
+ </g>
+</svg>
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
<a href="http://en.wikipedia.org/wiki/Pointing_stick">pointing stick</a> 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,15 +2,67 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="393.62857"
height="268.62857"
- id="svg2">
+ id="svg2"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="palm-detection.svg">
+ <metadata
+ id="metadata3479">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1136"
+ id="namedview3477"
+ showgrid="false"
+ inkscape:zoom="3.5662625"
+ inkscape:cx="199.35048"
+ inkscape:cy="156.74673"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
<defs
id="defs4">
<marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="marker4663"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4407"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
+ transform="scale(0.8) translate(12.5,0)" />
+ </marker>
+ <marker
refX="0"
refY="0"
orient="auto"
@@ -59,64 +111,111 @@
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
</defs>
- <g
- transform="translate(343.95712,-527.33359)"
- id="layer3"
- style="display:inline">
- <rect
- width="386.42856"
- height="261.42856"
- x="-340.35712"
- y="530.93359"
- id="rect2858-0"
- style="color:#000000;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:7.20000076;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
- <rect
- width="65.281105"
- height="254.3844"
- x="-336.88608"
- y="534.46918"
- id="rect12924"
- style="color:#000000;fill:#ff0000;fill-opacity:0.32017547;fill-rule:nonzero;stroke:none;stroke-width:3.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
- <rect
- width="65.281105"
- height="254.3844"
- x="-22.72864"
- y="534.21661"
- id="rect13482"
- style="color:#000000;fill:#ff0000;fill-opacity:0.32017547;fill-rule:nonzero;stroke:none;stroke-width:3.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
- <path
- d="m 38.928571,67.914286 c 0,0 3.508205,24.810617 9.642857,57.857144 6.134651,33.04652 23.277202,79.68584 89.642852,90.35714"
- transform="translate(-343.95712,527.33359)"
- id="path13492"
- style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 1;stroke-dashoffset:0;marker-mid:none;marker-end:url(#Arrow1Lend-2)" />
- <text
- x="-310.74283"
- y="590.96222"
- id="text13874"
- xml:space="preserve"
- style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Utopia;-inkscape-font-specification:Utopia"><tspan
- x="-310.74283"
- y="590.96222"
- id="tspan13876"
- style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial">A</tspan></text>
- <text
- x="7.8971062"
- y="626.08258"
- id="text13874-8"
- xml:space="preserve"
- style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Utopia;-inkscape-font-specification:Utopia"><tspan
- x="7.8971062"
- y="626.08258"
- id="tspan13876-7"
- style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial">B</tspan></text>
- <path
- d="m 347.5,90.414286 c 0,0 -28.20972,-6.408104 -85,-6.071429 -22.06971,0.130838 -66.07143,4.285715 -66.07143,4.285715"
- transform="translate(-343.95712,527.33359)"
- id="path13903"
- style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow1Lend-2)" />
- </g>
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:7.20000076;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect2858-0"
+ y="3.6000037"
+ x="3.6000032"
+ height="261.42856"
+ width="386.42856" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ff0000;fill-opacity:0.32017547;fill-rule:nonzero;stroke:none;stroke-width:3.5;marker:none;enable-background:accumulate"
+ id="rect12924"
+ y="7.1355872"
+ x="7.0710421"
+ height="254.3844"
+ width="65.281105" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ff0000;fill-opacity:0.32017547;fill-rule:nonzero;stroke:none;stroke-width:3.5;marker:none;enable-background:accumulate"
+ id="rect13482"
+ y="6.8830237"
+ x="321.22849"
+ height="254.3844"
+ width="65.281105" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:6, 1;stroke-dashoffset:0;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow1Lend-2)"
+ id="path13492"
+ d="m 38.928571,67.914286 c 0,0 3.508205,24.810617 9.642857,57.857144 6.134651,33.04652 23.277202,79.68584 89.642852,90.35714" />
+ <rect
+ style="fill:#000000;fill-opacity:0.3559322;fill-rule:evenodd;stroke:none;stroke-width:3.30527353px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect3490"
+ width="65.272476"
+ height="136.21509"
+ x="7.0411549"
+ y="7.0411549" />
+ <text
+ sodipodi:linespacing="100%"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:100%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
+ xml:space="preserve"
+ id="text13874"
+ y="63.628628"
+ x="33.214291"><tspan
+ style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial"
+ id="tspan13876"
+ y="63.628628"
+ x="33.214291">A</tspan></text>
+ <rect
+ style="fill:#000000;fill-opacity:0.3559322;fill-rule:evenodd;stroke:none;stroke-width:3.30527353px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect3490-2"
+ width="65.272476"
+ height="136.21509"
+ x="321.23563"
+ y="6.7607527" />
+ <text
+ sodipodi:linespacing="100%"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:100%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none"
+ xml:space="preserve"
+ id="text13874-8"
+ y="98.748993"
+ x="351.85422"><tspan
+ style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial"
+ id="tspan13876-7"
+ y="98.748993"
+ x="351.85422">B</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow1Lend-2)"
+ id="path13903"
+ d="m 347.5,90.414286 c 0,0 -28.20972,-6.408104 -85,-6.071429 -22.06971,0.130838 -66.07143,4.285715 -66.07143,4.285715" />
<g
transform="translate(343.95712,-527.33359)"
id="layer1"
style="display:inline" />
+ <text
+ sodipodi:linespacing="100%"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:100%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none"
+ xml:space="preserve"
+ id="text13874-8-1"
+ y="46.009491"
+ x="342.27759"><tspan
+ style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial"
+ id="tspan13876-7-9"
+ y="46.009491"
+ x="342.27759">C</tspan></text>
+ <text
+ sodipodi:linespacing="100%"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:100%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none"
+ xml:space="preserve"
+ id="text13874-8-1-4"
+ y="215.65927"
+ x="37.970726"><tspan
+ style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial"
+ id="tspan13876-7-9-5"
+ y="215.65927"
+ x="37.970726">D</tspan></text>
+ <circle
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-opacity:1"
+ id="path4401"
+ cx="-360.181"
+ cy="24.53549"
+ r="4.0658817"
+ transform="scale(-1,1)" />
+ <circle
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-opacity:1"
+ id="path4401-9"
+ cx="-36.452721"
+ cy="194.8819"
+ r="4.0658817"
+ transform="scale(-1,1)" />
</svg>
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.
+<b>This usually requires the tests to be run as root</b>.
+
+@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 @@
-<?xml version="1.0"?>
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2302px" height="3014px" version="1.1">
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2722px" height="3014px" version="1.1">
<defs/>
<g transform="translate(0.5,0.5)">
- <path d="M 1466 1350 L 2199 893" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
- <path d="M 2203 890 L 2199 897 L 2199 893 L 2196 891 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1353 335 L 2199 828" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
- <path d="M 2203 831 L 2195 830 L 2199 828 L 2199 824 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 903 1880 L 931 1929" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 934 1934 L 927 1929 L 931 1929 L 933 1926 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <ellipse cx="1312" cy="35" rx="49.5" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <path d="M 1884 1347 L 2616.6 890.37" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
+ <path d="M 2621.05 887.59 L 2616.96 894.26 L 2616.6 890.37 L 2613.26 888.32 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1771 332 L 2616.5 825.79" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
+ <path d="M 2621.03 828.44 L 2613.22 827.93 L 2616.5 825.79 L 2616.76 821.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1318 2071 L 1342.18 2091.84" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1346.15 2095.27 L 1338.57 2093.35 L 1342.18 2091.84 L 1343.14 2088.05 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <ellipse cx="1730" cy="32" rx="49.5" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1312" y="39">IDLE</text>
+ <text x="1729.75" y="35.75">
+ IDLE</text>
</g>
- <rect x="1254" y="295" width="130" height="40" rx="2" ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <rect x="1672" y="292" width="130" height="40" rx="2.4" ry="2.4" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1319" y="319">TOUCH</text>
+ <text x="1736.75" y="315.75">
+ TOUCH</text>
</g>
- <path d="M 1288 110 C 1291 106 1295 105 1299 105 L 1333 105 C 1337 105 1341 106 1344 110 L 1364 133 C 1365 134 1365 135 1364 137 L 1344 160 C 1341 163 1337 165 1333 165 L 1299 165 C 1295 165 1291 163 1288 160 L 1268 137 C 1268 135 1268 134 1268 133 L 1288 110 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1706.04 107 C 1708.84 103.86 1712.82 102.05 1717.02 102 L 1750.99 102 C 1755.19 102.05 1759.17 103.86 1761.97 107 L 1781.95 130 C 1782.51 131.28 1782.51 132.72 1781.95 134 L 1761.97 157 C 1759.17 160.14 1755.19 161.95 1750.99 162 L 1717.02 162 C 1712.82 161.95 1708.84 160.14 1706.04 157 L 1686.06 134 C 1685.5 132.72 1685.5 131.28 1686.06 130 L 1706.04 107 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1316" y="132">first</text>
- <text x="1316" y="146">finger down</text>
+ <text x="1733.75" y="128.75">
+ first</text>
+ <text x="1733.75" y="142.75">
+ finger down</text>
</g>
- <path d="M 1313 65 L 1315 98" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1315 103 L 1311 97 L 1315 98 L 1318 96 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1018 410 C 1021 406 1025 405 1029 405 L 1063 405 C 1067 405 1071 406 1074 410 L 1094 433 C 1095 434 1095 435 1094 437 L 1074 460 C 1071 463 1067 465 1063 465 L 1029 465 C 1025 465 1021 463 1018 460 L 998 437 C 998 435 998 434 998 433 L 1018 410 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1731 62 L 1732.68 95.64" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1732.94 100.88 L 1729.1 94.07 L 1732.68 95.64 L 1736.09 93.72 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1436.04 407 C 1438.84 403.86 1442.82 402.05 1447.02 402 L 1480.99 402 C 1485.19 402.05 1489.17 403.86 1491.97 407 L 1511.95 430 C 1512.51 431.28 1512.51 432.72 1511.95 434 L 1491.97 457 C 1489.17 460.14 1485.19 461.95 1480.99 462 L 1447.02 462 C 1442.82 461.95 1438.84 460.14 1436.04 457 L 1416.06 434 C 1415.5 432.72 1415.5 431.28 1416.06 430 L 1436.04 407 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1046" y="439">finger up</text>
+ <text x="1463.75" y="435.75">
+ finger up</text>
</g>
- <path d="M 1274 335 L 1100 411" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1096 413 L 1101 407 L 1100 411 L 1103 413 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1016 725 C 1017 723 1017 722 1018 721 C 1019 720 1020 720 1021 720 L 1090 720 C 1091 720 1093 720 1094 721 C 1095 722 1095 723 1095 725 L 1076 774 C 1075 776 1074 777 1074 778 C 1073 779 1072 780 1071 779 L 1001 779 C 1000 779 999 779 998 778 C 997 777 997 776 997 774 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1692 332 L 1518.83 408.43" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1514.02 410.55 L 1519.01 404.52 L 1518.83 408.43 L 1521.84 410.92 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1434.22 722.1 C 1435.36 718.9 1437.22 717.04 1439.18 717.12 L 1507.64 717.12 C 1509.2 717.08 1510.7 717.63 1511.67 718.61 C 1512.65 719.59 1512.99 720.88 1512.6 722.1 L 1493.75 771.9 C 1492.61 775.1 1490.75 776.96 1488.79 776.88 L 1419.34 776.88 C 1417.95 776.69 1416.73 776.05 1415.97 775.1 C 1415.22 774.15 1415 772.99 1415.37 771.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1046" y="747">button 1</text>
- <text x="1046" y="761">press</text>
+ <text x="1463.75" y="743.75">
+ button 1</text>
+ <text x="1463.75" y="757.75">
+ press</text>
</g>
- <path d="M 1566 410 C 1568 406 1572 405 1577 405 L 1610 405 C 1615 405 1619 406 1621 410 L 1641 433 C 1642 434 1642 435 1641 437 L 1621 460 C 1619 463 1615 465 1610 465 L 1577 465 C 1572 465 1568 463 1566 460 L 1546 437 C 1545 435 1545 434 1546 433 L 1566 410 Z" fill="#99ffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1983.54 407 C 1986.34 403.86 1990.32 402.05 1994.52 402 L 2028.49 402 C 2032.69 402.05 2036.67 403.86 2039.47 407 L 2059.45 430 C 2060.01 431.28 2060.01 432.72 2059.45 434 L 2039.47 457 C 2036.67 460.14 2032.69 461.95 2028.49 462 L 1994.52 462 C 1990.32 461.95 1986.34 460.14 1983.54 457 L 1963.56 434 C 1963 432.72 1963 431.28 1963.56 430 L 1983.54 407 Z" fill="#99ffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1593" y="439">timeout</text>
+ <text x="2011.25" y="435.75">
+ timeout</text>
</g>
- <path d="M 1365 335 L 1539 411" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1544 413 L 1536 413 L 1539 411 L 1539 407 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1385 410 C 1387 406 1391 405 1396 405 L 1429 405 C 1434 405 1438 406 1440 410 L 1460 433 C 1461 434 1461 435 1460 437 L 1440 460 C 1438 463 1434 465 1429 465 L 1396 465 C 1391 465 1387 463 1385 460 L 1365 437 C 1364 435 1364 434 1365 433 L 1385 410 Z" fill="#67ab9f" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1783 332 L 1957.17 408.44" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1961.98 410.55 L 1954.16 410.94 L 1957.17 408.44 L 1956.97 404.53 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1802.54 407 C 1805.34 403.86 1809.32 402.05 1813.52 402 L 1847.49 402 C 1851.69 402.05 1855.67 403.86 1858.47 407 L 1878.45 430 C 1879.01 431.28 1879.01 432.72 1878.45 434 L 1858.47 457 C 1855.67 460.14 1851.69 461.95 1847.49 462 L 1813.52 462 C 1809.32 461.95 1805.34 460.14 1802.54 457 L 1782.56 434 C 1782 432.72 1782 431.28 1782.56 430 L 1802.54 407 Z" fill="#67ab9f" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1413" y="432">move &gt; </text>
- <text x="1413" y="446">threshold</text>
+ <text x="1830.25" y="428.75">
+ move &gt; </text>
+ <text x="1830.25" y="442.75">
+ threshold</text>
</g>
- <path d="M 1335 335 L 1385 400" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1388 404 L 1381 400 L 1385 400 L 1387 396 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1196 410 C 1198 406 1202 405 1207 405 L 1240 405 C 1245 405 1249 406 1251 410 L 1271 433 C 1272 434 1272 435 1271 437 L 1251 460 C 1249 463 1245 465 1240 465 L 1207 465 C 1202 465 1198 463 1196 460 L 1176 437 C 1175 435 1175 434 1176 433 L 1196 410 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1753 332 L 1803.11 396.96" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1806.32 401.11 L 1799.27 397.71 L 1803.11 396.96 L 1804.81 393.43 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1613.54 407 C 1616.34 403.86 1620.32 402.05 1624.52 402 L 1658.49 402 C 1662.69 402.05 1666.67 403.86 1669.47 407 L 1689.45 430 C 1690.01 431.28 1690.01 432.72 1689.45 434 L 1669.47 457 C 1666.67 460.14 1662.69 461.95 1658.49 462 L 1624.52 462 C 1620.32 461.95 1616.34 460.14 1613.54 457 L 1593.56 434 C 1593 432.72 1593 431.28 1593.56 430 L 1613.54 407 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1224" y="432">second</text>
- <text x="1224" y="446">finger down</text>
+ <text x="1641.25" y="428.75">
+ second</text>
+ <text x="1641.25" y="442.75">
+ finger down</text>
</g>
- <path d="M 1303 335 L 1251 400" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1248 404 L 1250 396 L 1251 400 L 1255 400 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <rect x="1399" y="850" width="130" height="40" rx="2" ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <path d="M 1721 332 L 1668.98 397.03" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1665.7 401.13 L 1667.34 393.47 L 1668.98 397.03 L 1672.8 397.85 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <rect x="1817" y="847" width="130" height="40" rx="2.4" ry="2.4" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1464" y="874">TOUCH_2</text>
+ <text x="1881.75" y="870.75">
+ TOUCH_2</text>
</g>
- <path d="M 1309 985 C 1312 981 1316 980 1320 980 L 1354 980 C 1358 980 1362 981 1365 985 L 1385 1008 C 1386 1009 1386 1010 1385 1012 L 1365 1035 C 1362 1038 1358 1040 1354 1040 L 1320 1040 C 1316 1040 1312 1038 1309 1035 L 1289 1012 C 1289 1010 1289 1009 1289 1008 L 1309 985 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1727.04 982 C 1729.84 978.86 1733.82 977.05 1738.02 977 L 1771.99 977 C 1776.19 977.05 1780.17 978.86 1782.97 982 L 1802.95 1005 C 1803.51 1006.28 1803.51 1007.72 1802.95 1009 L 1782.97 1032 C 1780.17 1035.14 1776.19 1036.95 1771.99 1037 L 1738.02 1037 C 1733.82 1036.95 1729.84 1035.14 1727.04 1032 L 1707.06 1009 C 1706.5 1007.72 1706.5 1006.28 1707.06 1005 L 1727.04 982 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1337" y="1007">second</text>
- <text x="1337" y="1021">finger up</text>
+ <text x="1754.75" y="1003.75">
+ second</text>
+ <text x="1754.75" y="1017.75">
+ finger up</text>
</g>
- <path d="M 1446 890 L 1368 975" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1365 979 L 1367 971 L 1368 975 L 1372 976 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1157 645 C 1158 643 1158 642 1159 641 C 1160 640 1161 640 1162 640 L 1231 640 C 1232 640 1234 640 1235 641 C 1236 642 1236 643 1236 645 L 1217 694 C 1216 696 1215 697 1215 698 C 1214 699 1213 700 1212 699 L 1142 699 C 1141 699 1140 699 1139 698 C 1138 697 1138 696 1138 694 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1864 887 L 1786.29 972.29" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1782.75 976.17 L 1784.88 968.64 L 1786.29 972.29 L 1790.05 973.36 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1575.22 642.1 C 1576.36 638.9 1578.22 637.04 1580.18 637.12 L 1648.64 637.12 C 1650.2 637.08 1651.7 637.63 1652.67 638.61 C 1653.65 639.59 1653.99 640.88 1653.6 642.1 L 1634.75 691.9 C 1633.61 695.1 1631.75 696.96 1629.79 696.88 L 1560.34 696.88 C 1558.95 696.69 1557.73 696.05 1556.97 695.1 C 1556.22 694.15 1556 692.99 1556.37 691.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1187" y="667">button 2</text>
- <text x="1187" y="681">press</text>
+ <text x="1604.75" y="663.75">
+ button 2</text>
+ <text x="1604.75" y="677.75">
+ press</text>
</g>
- <path d="M 1559 965 C 1561 961 1565 960 1570 960 L 1603 960 C 1608 960 1612 961 1614 965 L 1634 988 C 1635 989 1635 990 1634 992 L 1614 1015 C 1612 1018 1608 1020 1603 1020 L 1570 1020 C 1565 1020 1561 1018 1559 1015 L 1539 992 C 1538 990 1538 989 1539 988 L 1559 965 Z" fill="#67ab9f" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1976.54 962 C 1979.34 958.86 1983.32 957.05 1987.52 957 L 2021.49 957 C 2025.69 957.05 2029.67 958.86 2032.47 962 L 2052.45 985 C 2053.01 986.28 2053.01 987.72 2052.45 989 L 2032.47 1012 C 2029.67 1015.14 2025.69 1016.95 2021.49 1017 L 1987.52 1017 C 1983.32 1016.95 1979.34 1015.14 1976.54 1012 L 1956.56 989 C 1956 987.72 1956 986.28 1956.56 985 L 1976.54 962 Z" fill="#67ab9f" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1586" y="987">move &gt; </text>
- <text x="1586" y="1001">threshold</text>
+ <text x="2004.25" y="983.75">
+ move &gt; </text>
+ <text x="2004.25" y="997.75">
+ threshold</text>
</g>
- <path d="M 1685 905 C 1687 901 1691 900 1696 900 L 1729 900 C 1734 900 1738 901 1740 905 L 1760 928 C 1761 929 1761 930 1760 932 L 1740 955 C 1738 958 1734 960 1729 960 L 1696 960 C 1691 960 1687 958 1685 955 L 1665 932 C 1664 930 1664 929 1665 928 L 1685 905 Z" fill="#99ffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2102.54 902 C 2105.34 898.86 2109.32 897.05 2113.52 897 L 2147.49 897 C 2151.69 897.05 2155.67 898.86 2158.47 902 L 2178.45 925 C 2179.01 926.28 2179.01 927.72 2178.45 929 L 2158.47 952 C 2155.67 955.14 2151.69 956.95 2147.49 957 L 2113.52 957 C 2109.32 956.95 2105.34 955.14 2102.54 952 L 2082.56 929 C 2082 927.72 2082 926.28 2082.56 925 L 2102.54 902 Z" fill="#99ffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1713" y="934">timeout</text>
+ <text x="2130.25" y="930.75">
+ timeout</text>
</g>
- <path d="M 1529 885 L 1658 916" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1663 918 L 1655 919 L 1658 916 L 1657 913 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1484 890 L 1551 955" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1555 959 L 1548 956 L 1551 955 L 1553 951 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1108 1145 C 1108 1143 1109 1142 1110 1141 C 1111 1140 1112 1140 1113 1140 L 1181 1140 C 1183 1140 1184 1140 1185 1141 C 1186 1142 1186 1143 1186 1145 L 1167 1194 C 1167 1196 1166 1197 1165 1198 C 1164 1199 1163 1200 1162 1199 L 1093 1199 C 1091 1199 1090 1199 1089 1198 C 1089 1197 1088 1196 1089 1194 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1947 883 L 2075.8 913.53" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2080.91 914.74 L 2073.29 916.53 L 2075.8 913.53 L 2074.91 909.72 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1902 887 L 1969.43 952.56" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1973.2 956.22 L 1965.74 953.85 L 1969.43 952.56 L 1970.62 948.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1638.72 1112.1 C 1639.86 1108.9 1641.72 1107.04 1643.68 1107.12 L 1712.14 1107.12 C 1713.7 1107.08 1715.2 1107.63 1716.17 1108.61 C 1717.15 1109.59 1717.49 1110.88 1717.1 1112.1 L 1698.25 1161.9 C 1697.11 1165.1 1695.25 1166.96 1693.29 1166.88 L 1623.84 1166.88 C 1622.45 1166.69 1621.23 1166.05 1620.47 1165.1 C 1619.72 1164.15 1619.5 1162.99 1619.87 1161.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1138" y="1167">button 1</text>
- <text x="1138" y="1181">release</text>
+ <text x="1668.25" y="1133.75">
+ button 1</text>
+ <text x="1668.25" y="1147.75">
+ release</text>
</g>
- <path d="M 1167 550 C 1167 548 1168 547 1169 546 C 1170 545 1171 545 1172 545 L 1240 545 C 1242 545 1243 545 1244 546 C 1245 547 1245 548 1245 550 L 1226 599 C 1226 601 1225 602 1224 603 C 1223 604 1222 605 1221 604 L 1152 604 C 1150 604 1149 604 1148 603 C 1148 602 1147 601 1148 599 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1584.72 547.1 C 1585.86 543.9 1587.72 542.04 1589.68 542.12 L 1658.14 542.12 C 1659.7 542.08 1661.2 542.63 1662.17 543.61 C 1663.15 544.59 1663.49 545.88 1663.1 547.1 L 1644.25 596.9 C 1643.11 600.1 1641.25 601.96 1639.29 601.88 L 1569.84 601.88 C 1568.45 601.69 1567.23 601.05 1566.47 600.1 C 1565.72 599.15 1565.5 597.99 1565.87 596.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1197" y="572">button 2</text>
- <text x="1197" y="586">release</text>
+ <text x="1614.25" y="568.75">
+ button 2</text>
+ <text x="1614.25" y="582.75">
+ release</text>
</g>
- <path d="M 1301 780 L 1223 704" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1219 700 L 1226 703 L 1223 704 L 1221 708 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1190 640 L 1193 611" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1193 606 L 1196 613 L 1193 611 L 1189 612 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <rect x="981" y="920" width="130" height="40" rx="2" ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <path d="M 1719 778 L 1640.56 701.45" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1636.8 697.78 L 1644.25 700.16 L 1640.56 701.45 L 1639.37 705.17 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1608 637 L 1610.46 608.34" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1610.9 603.11 L 1613.79 610.39 L 1610.46 608.34 L 1606.82 609.79 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <rect x="1399" y="917" width="130" height="40" rx="2.4" ry="2.4" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1046" y="944">TAPPED</text>
+ <text x="1463.75" y="940.75">
+ TAPPED</text>
</g>
- <path d="M 1110 1045 C 1113 1041 1117 1040 1121 1040 L 1155 1040 C 1159 1040 1163 1041 1166 1045 L 1186 1068 C 1187 1069 1187 1070 1186 1072 L 1166 1095 C 1163 1098 1159 1100 1155 1100 L 1121 1100 C 1117 1100 1113 1098 1110 1095 L 1090 1072 C 1090 1070 1090 1069 1090 1068 L 1110 1045 Z" fill="#99ffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1528.04 1042 C 1530.84 1038.86 1534.82 1037.05 1539.02 1037 L 1572.99 1037 C 1577.19 1037.05 1581.17 1038.86 1583.97 1042 L 1603.95 1065 C 1604.51 1066.28 1604.51 1067.72 1603.95 1069 L 1583.97 1092 C 1581.17 1095.14 1577.19 1096.95 1572.99 1097 L 1539.02 1097 C 1534.82 1096.95 1530.84 1095.14 1528.04 1092 L 1508.06 1069 C 1507.5 1067.72 1507.5 1066.28 1508.06 1065 L 1528.04 1042 Z" fill="#99ffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1138" y="1074">timeout</text>
+ <text x="1555.75" y="1070.75">
+ timeout</text>
</g>
- <path d="M 1060 960 L 1113 1034" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1116 1039 L 1109 1035 L 1113 1034 L 1115 1031 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 955 1015 C 957 1011 961 1010 966 1010 L 999 1010 C 1004 1010 1008 1011 1010 1015 L 1030 1038 C 1031 1039 1031 1040 1030 1042 L 1010 1065 C 1008 1068 1004 1070 999 1070 L 966 1070 C 961 1070 957 1068 955 1065 L 935 1042 C 934 1040 934 1039 935 1038 L 955 1015 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1478 957 L 1531.3 1031.81" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1534.35 1036.09 L 1527.44 1032.42 L 1531.3 1031.81 L 1533.14 1028.36 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1372.54 1012 C 1375.34 1008.86 1379.32 1007.05 1383.52 1007 L 1417.49 1007 C 1421.69 1007.05 1425.67 1008.86 1428.47 1012 L 1448.45 1035 C 1449.01 1036.28 1449.01 1037.72 1448.45 1039 L 1428.47 1062 C 1425.67 1065.14 1421.69 1066.95 1417.49 1067 L 1383.52 1067 C 1379.32 1066.95 1375.34 1065.14 1372.54 1062 L 1352.56 1039 C 1352 1037.72 1352 1036.28 1352.56 1035 L 1372.54 1012 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="983" y="1037">first</text>
- <text x="983" y="1051">finger down</text>
+ <text x="1400.25" y="1033.75">
+ first</text>
+ <text x="1400.25" y="1047.75">
+ finger down</text>
</g>
- <path d="M 1033 960 L 1005 1004" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1002 1009 L 1003 1001 L 1005 1004 L 1009 1005 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <rect x="412" y="1490" width="130" height="40" rx="2" ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <path d="M 1451 957 L 1423.36 1001.59" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1420.59 1006.05 L 1421.3 998.26 L 1423.36 1001.59 L 1427.25 1001.94 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <rect x="830" y="1487" width="130" height="40" rx="2.4" ry="2.4" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="477" y="1514">DRAGGING</text>
+ <text x="894.75" y="1510.75">
+ DRAGGING</text>
</g>
- <path d="M 306 1620 C 309 1616 313 1615 317 1615 L 351 1615 C 355 1615 359 1616 362 1620 L 382 1643 C 383 1644 383 1645 382 1647 L 362 1670 C 359 1673 355 1675 351 1675 L 317 1675 C 313 1675 309 1673 306 1670 L 286 1647 C 286 1645 286 1644 286 1643 L 306 1620 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 724.04 1617 C 726.84 1613.86 730.82 1612.05 735.02 1612 L 768.99 1612 C 773.19 1612.05 777.17 1613.86 779.97 1617 L 799.95 1640 C 800.51 1641.28 800.51 1642.72 799.95 1644 L 779.97 1667 C 777.17 1670.14 773.19 1671.95 768.99 1672 L 735.02 1672 C 730.82 1671.95 726.84 1670.14 724.04 1667 L 704.06 1644 C 703.5 1642.72 703.5 1641.28 704.06 1640 L 724.04 1617 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="334" y="1642">first</text>
- <text x="334" y="1656">finger up</text>
+ <text x="751.75" y="1638.75">
+ first</text>
+ <text x="751.75" y="1652.75">
+ finger up</text>
</g>
- <path d="M 485 1962 C 486 1960 486 1959 487 1958 C 488 1957 489 1957 490 1957 L 559 1957 C 560 1957 562 1957 563 1958 C 564 1959 564 1960 564 1962 L 545 2011 C 544 2013 543 2014 543 2015 C 542 2016 541 2017 540 2016 L 470 2016 C 469 2016 468 2016 467 2015 C 466 2014 466 2013 466 2011 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 903.22 1959.1 C 904.36 1955.9 906.22 1954.04 908.18 1954.12 L 976.64 1954.12 C 978.2 1954.08 979.7 1954.63 980.67 1955.61 C 981.65 1956.59 981.99 1957.88 981.6 1959.1 L 962.75 2008.9 C 961.61 2012.1 959.75 2013.96 957.79 2013.88 L 888.34 2013.88 C 886.95 2013.69 885.73 2013.05 884.97 2012.1 C 884.22 2011.15 884 2009.99 884.37 2008.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="515" y="1984">btn1</text>
- <text x="515" y="1998">release</text>
+ <text x="932.75" y="1980.75">
+ btn1</text>
+ <text x="932.75" y="1994.75">
+ release</text>
</g>
- <path d="M 456 1530 L 370 1610" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 367 1614 L 369 1606 L 370 1610 L 374 1612 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1046 780 L 1046 913" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1046 918 L 1043 911 L 1046 913 L 1050 911 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1138 1100 L 1138 1133" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1138 1138 L 1134 1131 L 1138 1133 L 1141 1131 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <ellipse cx="1923" cy="813" rx="49.5" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <path d="M 874 1527 L 788.63 1607.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 784.81 1611.23 L 787.5 1603.88 L 788.63 1607.63 L 792.31 1608.97 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1464 777 L 1464 910.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1464 915.88 L 1460.5 908.88 L 1464 910.63 L 1467.5 908.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1604 1097 L 1614.6 1103.62" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1619.05 1106.41 L 1611.26 1105.67 L 1614.6 1103.62 L 1614.97 1099.73 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <ellipse cx="2341" cy="810" rx="49.5" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1923" y="817">IDLE</text>
+ <text x="2341.25" y="813.75">
+ IDLE</text>
</g>
- <path d="M 1443 1001 C 1445 997 1449 996 1454 996 L 1487 996 C 1492 996 1496 997 1498 1001 L 1518 1024 C 1519 1025 1519 1026 1518 1028 L 1498 1051 C 1496 1054 1492 1056 1487 1056 L 1454 1056 C 1449 1056 1445 1054 1443 1051 L 1423 1028 C 1422 1026 1422 1025 1423 1024 L 1443 1001 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1860.54 998 C 1863.34 994.86 1867.32 993.05 1871.52 993 L 1905.49 993 C 1909.69 993.05 1913.67 994.86 1916.47 998 L 1936.45 1021 C 1937.01 1022.28 1937.01 1023.72 1936.45 1025 L 1916.47 1048 C 1913.67 1051.14 1909.69 1052.95 1905.49 1053 L 1871.52 1053 C 1867.32 1052.95 1863.34 1051.14 1860.54 1048 L 1840.56 1025 C 1840 1023.72 1840 1022.28 1840.56 1021 L 1860.54 998 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1471" y="1023">third</text>
- <text x="1471" y="1037">finger down</text>
+ <text x="1888.25" y="1019.75">
+ third</text>
+ <text x="1888.25" y="1033.75">
+ finger down</text>
</g>
- <path d="M 1465 890 L 1469 989" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1469 994 L 1465 988 L 1469 989 L 1472 987 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <rect x="1368" y="1350" width="130" height="40" rx="2" ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <path d="M 1883 887 L 1886.76 986.64" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1886.96 991.88 L 1883.2 985.02 L 1886.76 986.64 L 1890.19 984.76 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <rect x="1786.5" y="1347" width="130" height="40" rx="2.4" ry="2.4" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1433" y="1374">TOUCH_3</text>
+ <text x="1851.25" y="1370.75">
+ TOUCH_3</text>
</g>
- <path d="M 1441 1252 L 1435 1343" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1435 1348 L 1432 1341 L 1435 1343 L 1439 1342 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1289 1856 C 1290 1854 1290 1853 1291 1852 C 1292 1851 1293 1851 1294 1851 L 1363 1851 C 1364 1851 1366 1851 1367 1852 C 1368 1853 1368 1854 1368 1856 L 1349 1905 C 1348 1907 1347 1908 1347 1909 C 1346 1910 1345 1911 1344 1910 L 1274 1910 C 1273 1910 1272 1910 1271 1909 C 1270 1908 1270 1907 1270 1905 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1859 1250 L 1853.39 1340.64" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1853.07 1345.88 L 1850.01 1338.68 L 1853.39 1340.64 L 1856.99 1339.11 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1707.22 1853.1 C 1708.36 1849.9 1710.22 1848.04 1712.18 1848.12 L 1780.64 1848.12 C 1782.2 1848.08 1783.7 1848.63 1784.67 1849.61 C 1785.65 1850.59 1785.99 1851.88 1785.6 1853.1 L 1766.75 1902.9 C 1765.61 1906.1 1763.75 1907.96 1761.79 1907.88 L 1692.34 1907.88 C 1690.95 1907.69 1689.73 1907.05 1688.97 1906.1 C 1688.22 1905.15 1688 1903.99 1688.37 1902.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1319" y="1878">button 3</text>
- <text x="1319" y="1892">press</text>
+ <text x="1736.75" y="1874.75">
+ button 3</text>
+ <text x="1736.75" y="1888.75">
+ press</text>
</g>
- <path d="M 1289 1953 C 1290 1951 1290 1950 1291 1949 C 1292 1948 1293 1948 1294 1948 L 1363 1948 C 1364 1948 1366 1948 1367 1949 C 1368 1950 1368 1951 1368 1953 L 1349 2002 C 1348 2004 1347 2005 1347 2006 C 1346 2007 1345 2008 1344 2007 L 1274 2007 C 1273 2007 1272 2007 1271 2006 C 1270 2005 1270 2004 1270 2002 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1707.22 1950.1 C 1708.36 1946.9 1710.22 1945.04 1712.18 1945.12 L 1780.64 1945.12 C 1782.2 1945.08 1783.7 1945.63 1784.67 1946.61 C 1785.65 1947.59 1785.99 1948.88 1785.6 1950.1 L 1766.75 1999.9 C 1765.61 2003.1 1763.75 2004.96 1761.79 2004.88 L 1692.34 2004.88 C 1690.95 2004.69 1689.73 2004.05 1688.97 2003.1 C 1688.22 2002.15 1688 2000.99 1688.37 1999.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1319" y="1975">button 3</text>
- <text x="1319" y="1989">release</text>
+ <text x="1736.75" y="1971.75">
+ button 3</text>
+ <text x="1736.75" y="1985.75">
+ release</text>
</g>
- <path d="M 1319 1911 L 1319 1941" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1319 1946 L 1316 1939 L 1319 1941 L 1323 1939 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1492 1540 C 1495 1536 1499 1535 1503 1535 L 1537 1535 C 1541 1535 1545 1536 1548 1540 L 1568 1563 C 1569 1564 1569 1565 1568 1567 L 1548 1590 C 1545 1593 1541 1595 1537 1595 L 1503 1595 C 1499 1595 1495 1593 1492 1590 L 1472 1567 C 1472 1565 1472 1564 1472 1563 L 1492 1540 Z" fill="#67ab9f" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1737 1908 L 1737 1938.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1737 1943.88 L 1733.5 1936.88 L 1737 1938.63 L 1740.5 1936.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1910.04 1537 C 1912.84 1533.86 1916.82 1532.05 1921.02 1532 L 1954.99 1532 C 1959.19 1532.05 1963.17 1533.86 1965.97 1537 L 1985.95 1560 C 1986.51 1561.28 1986.51 1562.72 1985.95 1564 L 1965.97 1587 C 1963.17 1590.14 1959.19 1591.95 1954.99 1592 L 1921.02 1592 C 1916.82 1591.95 1912.84 1590.14 1910.04 1587 L 1890.06 1564 C 1889.5 1562.72 1889.5 1561.28 1890.06 1560 L 1910.04 1537 Z" fill="#67ab9f" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1520" y="1562">move &gt; </text>
- <text x="1520" y="1576">threshold</text>
+ <text x="1937.75" y="1558.75">
+ move &gt; </text>
+ <text x="1937.75" y="1572.75">
+ threshold</text>
</g>
- <path d="M 1442 1390 L 1504 1529" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1506 1534 L 1500 1529 L 1504 1529 L 1507 1526 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <ellipse cx="667" cy="2002" rx="49.5" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <path d="M 1860 1387 L 1922.4 1526.19" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1924.54 1530.98 L 1918.49 1526.02 L 1922.4 1526.19 L 1924.87 1523.16 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <ellipse cx="1085" cy="1999" rx="49.5" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="667" y="2006">IDLE</text>
+ <text x="1084.25" y="2002.75">
+ IDLE</text>
</g>
- <path d="M 1621 1530 C 1623 1526 1627 1525 1632 1525 L 1665 1525 C 1670 1525 1674 1526 1676 1530 L 1696 1553 C 1697 1554 1697 1555 1696 1557 L 1676 1580 C 1674 1583 1670 1585 1665 1585 L 1632 1585 C 1627 1585 1623 1583 1621 1580 L 1601 1557 C 1600 1555 1600 1554 1601 1553 L 1621 1530 Z" fill="#99ffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2038.54 1527 C 2041.34 1523.86 2045.32 1522.05 2049.52 1522 L 2083.49 1522 C 2087.69 1522.05 2091.67 1523.86 2094.47 1527 L 2114.45 1550 C 2115.01 1551.28 2115.01 1552.72 2114.45 1554 L 2094.47 1577 C 2091.67 1580.14 2087.69 1581.95 2083.49 1582 L 2049.52 1582 C 2045.32 1581.95 2041.34 1580.14 2038.54 1577 L 2018.56 1554 C 2018 1552.72 2018 1551.28 2018.56 1550 L 2038.54 1527 Z" fill="#99ffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1649" y="1559">timeout</text>
+ <text x="2066.25" y="1555.75">
+ timeout</text>
</g>
- <path d="M 1457 1390 L 1609 1520" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1613 1524 L 1605 1522 L 1609 1520 L 1610 1517 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1178 965 C 1180 961 1184 960 1189 960 L 1222 960 C 1227 960 1231 961 1233 965 L 1253 988 C 1254 989 1254 990 1253 992 L 1233 1015 C 1231 1018 1227 1020 1222 1020 L 1189 1020 C 1184 1020 1180 1018 1178 1015 L 1158 992 C 1157 990 1157 989 1158 988 L 1178 965 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1875 1387 L 2027.17 1517.85" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2031.15 1521.27 L 2023.56 1519.36 L 2027.17 1517.85 L 2028.13 1514.05 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1595.54 962 C 1598.34 958.86 1602.32 957.05 1606.52 957 L 1640.49 957 C 1644.69 957.05 1648.67 958.86 1651.47 962 L 1671.45 985 C 1672.01 986.28 1672.01 987.72 1671.45 989 L 1651.47 1012 C 1648.67 1015.14 1644.69 1016.95 1640.49 1017 L 1606.52 1017 C 1602.32 1016.95 1598.34 1015.14 1595.54 1012 L 1575.56 989 C 1575 987.72 1575 986.28 1575.56 985 L 1595.54 962 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1206" y="987">first</text>
- <text x="1206" y="1001">finger up</text>
+ <text x="1623.25" y="983.75">
+ first</text>
+ <text x="1623.25" y="997.75">
+ finger up</text>
</g>
- <path d="M 1421 890 L 1260 964" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1255 967 L 1260 960 L 1260 964 L 1263 967 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <ellipse cx="1138" cy="1310" rx="49.5" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <path d="M 1839 887 L 1677.78 961.33" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1673.02 963.53 L 1677.91 957.42 L 1677.78 961.33 L 1680.84 963.78 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <ellipse cx="1673" cy="1272" rx="49.5" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1138" y="1314">IDLE</text>
+ <text x="1672.75" y="1275.75">
+ IDLE</text>
</g>
- <path d="M 1205 1535 C 1208 1531 1212 1530 1216 1530 L 1250 1530 C 1254 1530 1258 1531 1261 1535 L 1281 1558 C 1282 1559 1282 1560 1281 1562 L 1261 1585 C 1258 1588 1254 1590 1250 1590 L 1216 1590 C 1212 1590 1208 1588 1205 1585 L 1185 1562 C 1185 1560 1185 1559 1185 1558 L 1205 1535 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1623.04 1532 C 1625.84 1528.86 1629.82 1527.05 1634.02 1527 L 1667.99 1527 C 1672.19 1527.05 1676.17 1528.86 1678.97 1532 L 1698.95 1555 C 1699.51 1556.28 1699.51 1557.72 1698.95 1559 L 1678.97 1582 C 1676.17 1585.14 1672.19 1586.95 1667.99 1587 L 1634.02 1587 C 1629.82 1586.95 1625.84 1585.14 1623.04 1582 L 1603.06 1559 C 1602.5 1557.72 1602.5 1556.28 1603.06 1555 L 1623.04 1532 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1233" y="1557">fourth</text>
- <text x="1233" y="1571">finger down</text>
- </g>
- <path d="M 1412 1390 L 1269 1525" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1265 1529 L 1268 1521 L 1269 1525 L 1273 1527 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1138 1200 L 1138 1273" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1138 1278 L 1134 1271 L 1138 1273 L 1141 1271 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <rect x="653" y="1170" width="209" height="40" rx="2" ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="757" y="1194">DRAGGING_OR_DOUBLETAP</text>
- </g>
- <path d="M 835 1147 L 799 1167" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 794 1169 L 799 1163 L 799 1167 L 802 1169 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 530 1255 C 532 1251 536 1250 541 1250 L 574 1250 C 579 1250 583 1251 585 1255 L 605 1278 C 606 1279 606 1280 605 1282 L 585 1305 C 583 1308 579 1310 574 1310 L 541 1310 C 536 1310 532 1308 530 1305 L 510 1282 C 509 1280 509 1279 510 1278 L 530 1255 Z" fill="#99ffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="558" y="1284">timeout</text>
- </g>
- <path d="M 713 1210 L 612 1255" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 607 1257 L 612 1251 L 612 1255 L 615 1258 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 843 1255 C 846 1251 850 1250 854 1250 L 888 1250 C 892 1250 896 1251 899 1255 L 919 1278 C 920 1279 920 1280 919 1282 L 899 1305 C 896 1308 892 1310 888 1310 L 854 1310 C 850 1310 846 1308 843 1305 L 823 1282 C 823 1280 823 1279 823 1278 L 843 1255 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="871" y="1277">first</text>
- <text x="871" y="1291">finger up</text>
- </g>
- <path d="M 782 1210 L 828 1246" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 832 1249 L 824 1247 L 828 1246 L 829 1242 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 883 1475 C 884 1473 884 1472 885 1471 C 886 1470 887 1470 888 1470 L 957 1470 C 958 1470 960 1470 961 1471 C 962 1472 962 1473 962 1475 L 943 1524 C 942 1526 941 1527 941 1528 C 940 1529 939 1530 938 1529 L 868 1529 C 867 1529 866 1529 865 1528 C 864 1527 864 1526 864 1524 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="913" y="1497">button 1</text>
- <text x="913" y="1511">release</text>
- </g>
- <path d="M 883 1555 C 884 1553 884 1552 885 1551 C 886 1550 887 1550 888 1550 L 957 1550 C 958 1550 960 1550 961 1551 C 962 1552 962 1553 962 1555 L 943 1604 C 942 1606 941 1607 941 1608 C 940 1609 939 1610 938 1609 L 868 1609 C 867 1609 866 1609 865 1608 C 864 1607 864 1606 864 1604 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="913" y="1577">button 1</text>
- <text x="913" y="1591">press</text>
- </g>
- <path d="M 890 1645 C 890 1643 891 1642 892 1641 C 893 1640 894 1640 895 1640 L 963 1640 C 965 1640 966 1640 967 1641 C 968 1642 968 1643 968 1645 L 949 1694 C 949 1696 948 1697 947 1698 C 946 1699 945 1700 944 1699 L 875 1699 C 873 1699 872 1699 871 1698 C 871 1697 870 1696 871 1694 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="920" y="1667">btn1</text>
- <text x="920" y="1681">release</text>
- </g>
- <path d="M 902 1423 L 912 1463" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 913 1468 L 908 1462 L 912 1463 L 915 1461 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 913 1530 L 913 1543" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 913 1548 L 910 1541 L 913 1543 L 917 1541 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 915 1610 L 917 1633" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 917 1638 L 913 1632 L 917 1633 L 920 1631 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 897 1700 L 693 1967" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 690 1971 L 691 1963 L 693 1967 L 697 1967 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 685 1255 C 687 1251 691 1250 696 1250 L 729 1250 C 734 1250 738 1251 740 1255 L 760 1278 C 761 1279 761 1280 760 1282 L 740 1305 C 738 1308 734 1310 729 1310 L 696 1310 C 691 1310 687 1308 685 1305 L 665 1282 C 664 1280 664 1279 665 1278 L 685 1255 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="712" y="1277">second</text>
- <text x="712" y="1291">finger down</text>
- </g>
- <path d="M 747 1210 L 730 1244" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 728 1249 L 728 1241 L 730 1244 L 734 1244 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 355 1255 C 357 1251 361 1250 366 1250 L 399 1250 C 404 1250 408 1251 410 1255 L 430 1278 C 431 1279 431 1280 430 1282 L 410 1305 C 408 1308 404 1310 399 1310 L 366 1310 C 361 1310 357 1308 355 1305 L 335 1282 C 334 1280 334 1279 335 1278 L 355 1255 Z" fill="#67ab9f" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <text x="1650.75" y="1553.75">
+ fourth</text>
+ <text x="1650.75" y="1567.75">
+ finger down</text>
+ </g>
+ <path d="M 1830 1387 L 1687.61 1522.61" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1683.81 1526.23 L 1686.46 1518.87 L 1687.61 1522.61 L 1691.29 1523.94 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1670 1167 L 1671.83 1235.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1671.97 1240.88 L 1668.28 1233.98 L 1671.83 1235.63 L 1675.28 1233.79 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <rect x="1070.5" y="1167" width="209" height="40" rx="2.4" ry="2.4" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="383" y="1277">move &gt; </text>
- <text x="383" y="1291">threshold</text>
- </g>
- <path d="M 674 1210 L 437 1266" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 432 1268 L 438 1263 L 437 1266 L 440 1269 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 489 1415 L 480 1483" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 480 1488 L 477 1481 L 480 1483 L 484 1482 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <rect x="1454" y="580" width="130" height="40" rx="2" ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <text x="1174.75" y="1190.75">
+ DRAGGING_OR_DOUBLETAP</text>
+ </g>
+ <path d="M 1253 1144 L 1216.59 1163.94" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1211.98 1166.46 L 1216.44 1160.03 L 1216.59 1163.94 L 1219.8 1166.17 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 947.54 1252 C 950.34 1248.86 954.32 1247.05 958.52 1247 L 992.49 1247 C 996.69 1247.05 1000.67 1248.86 1003.47 1252 L 1023.45 1275 C 1024.01 1276.28 1024.01 1277.72 1023.45 1279 L 1003.47 1302 C 1000.67 1305.14 996.69 1306.95 992.49 1307 L 958.52 1307 C 954.32 1306.95 950.34 1305.14 947.54 1302 L 927.56 1279 C 927 1277.72 927 1276.28 927.56 1275 L 947.54 1252 Z" fill="#99ffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="975.25" y="1280.75">
+ timeout</text>
+ </g>
+ <path d="M 1131 1207 L 1029.81 1252.39" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1025.02 1254.54 L 1029.97 1248.48 L 1029.81 1252.39 L 1032.84 1254.87 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 784.04 1018 C 786.84 1014.86 790.82 1013.05 795.02 1013 L 828.99 1013 C 833.19 1013.05 837.17 1014.86 839.97 1018 L 859.95 1041 C 860.51 1042.28 860.51 1043.72 859.95 1045 L 839.97 1068 C 837.17 1071.14 833.19 1072.95 828.99 1073 L 795.02 1073 C 790.82 1072.95 786.84 1071.14 784.04 1068 L 764.06 1045 C 763.5 1043.72 763.5 1042.28 764.06 1041 L 784.04 1018 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1519" y="604">HOLD</text>
- </g>
- <path d="M 1677 708 C 1680 704 1684 703 1688 703 L 1722 703 C 1726 703 1730 704 1733 708 L 1753 731 C 1754 732 1754 733 1753 735 L 1733 758 C 1730 761 1726 763 1722 763 L 1688 763 C 1684 763 1680 761 1677 758 L 1657 735 C 1657 733 1657 732 1657 731 L 1677 708 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1705" y="730">first</text>
- <text x="1705" y="744">finger up</text>
- </g>
- <path d="M 1547 620 L 1658 699" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1662 702 L 1654 701 L 1658 699 L 1658 695 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1846 810 L 1868 810" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1873 811 L 1866 814 L 1868 810 L 1866 807 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1462 675 C 1464 671 1468 670 1473 670 L 1506 670 C 1511 670 1515 671 1517 675 L 1537 698 C 1538 699 1538 700 1537 702 L 1517 725 C 1515 728 1511 730 1506 730 L 1473 730 C 1468 730 1464 728 1462 725 L 1442 702 C 1441 700 1441 699 1442 698 L 1462 675 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1490" y="697">second</text>
- <text x="1490" y="711">finger down</text>
- </g>
- <path d="M 1513 620 L 1500 663" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1499 668 L 1497 661 L 1500 663 L 1504 663 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1469 820 L 1466 843" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1466 848 L 1463 841 L 1466 843 L 1470 842 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1385 672 L 1477 623" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1481 620 L 1477 627 L 1477 623 L 1473 620 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <rect x="1795" y="1120" width="130" height="40" rx="2" ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1860" y="1144">TOUCH_2_HOLD</text>
- </g>
- <path d="M 1975 1225 C 1978 1221 1982 1220 1986 1220 L 2020 1220 C 2024 1220 2028 1221 2031 1225 L 2051 1248 C 2051 1249 2051 1250 2051 1252 L 2031 1275 C 2028 1278 2024 1280 2020 1280 L 1986 1280 C 1982 1280 1978 1278 1975 1275 L 1955 1252 C 1954 1250 1954 1249 1955 1248 L 1975 1225 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="2003" y="1247">second</text>
- <text x="2003" y="1261">finger up</text>
- </g>
- <path d="M 1886 1160 L 1959 1216" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1963 1219 L 1955 1217 L 1959 1216 L 1960 1212 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1670 1230 C 1673 1226 1677 1225 1681 1225 L 1715 1225 C 1719 1225 1723 1226 1726 1230 L 1746 1253 C 1747 1254 1747 1255 1746 1257 L 1726 1280 C 1723 1283 1719 1285 1715 1285 L 1681 1285 C 1677 1285 1673 1283 1670 1280 L 1650 1257 C 1650 1255 1650 1254 1650 1253 L 1670 1230 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1698" y="1252">first</text>
- <text x="1698" y="1266">finger up</text>
- </g>
- <path d="M 1832 1160 L 1746 1221" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1741 1224 L 1745 1217 L 1746 1221 L 1749 1223 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1984 875 L 1984 610 Q 1984 600 1974 600 L 1590 600" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1585 600 L 1592 596 L 1590 600 L 1592 603 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1644 1085 L 1644 610 Q 1644 600 1634 600 L 1590 600" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1585 600 L 1592 596 L 1590 600 L 1592 603 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1816 1235 C 1819 1231 1823 1230 1827 1230 L 1861 1230 C 1865 1230 1869 1231 1872 1235 L 1892 1258 C 1892 1259 1892 1260 1892 1262 L 1872 1285 C 1869 1288 1865 1290 1861 1290 L 1827 1290 C 1823 1290 1819 1288 1816 1285 L 1796 1262 C 1795 1260 1795 1259 1796 1258 L 1816 1235 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1844" y="1257">third</text>
- <text x="1844" y="1271">finger down</text>
- </g>
- <path d="M 1858 1160 L 1849 1223" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1848 1228 L 1846 1221 L 1849 1223 L 1852 1222 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1569 1355 L 1505 1362" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1500 1362 L 1506 1358 L 1505 1362 L 1507 1365 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1570 1987 L 2064 1987 Q 2074 1987 2074 1977 L 2074 1150 Q 2074 1140 2064 1140 L 1932 1140" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1926 1140 L 1933 1136 L 1932 1140 L 1933 1143 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <rect x="1534" y="2227" width="130" height="40" rx="2" ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1599" y="2251">TOUCH_3_HOLD</text>
- </g>
- <path d="M 1591 1826 L 1599 2220" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1599 2225 L 1595 2219 L 1599 2220 L 1602 2218 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1295 2365 C 1298 2361 1302 2360 1306 2360 L 1340 2360 C 1344 2360 1348 2361 1351 2365 L 1371 2388 C 1372 2389 1372 2390 1371 2392 L 1351 2415 C 1348 2418 1344 2420 1340 2420 L 1306 2420 C 1302 2420 1298 2418 1295 2415 L 1275 2392 C 1275 2390 1275 2389 1275 2388 L 1295 2365 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1323" y="2387">fourth</text>
- <text x="1323" y="2401">finger down</text>
- </g>
- <rect x="1054" y="2485" width="130" height="40" rx="2" ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1119" y="2509">DEAD</text>
- </g>
- <path d="M 1560 2267 L 1377 2362" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1372 2364 L 1377 2358 L 1377 2362 L 1380 2364 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1275 2417 L 1160 2481" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1155 2484 L 1160 2478 L 1160 2481 L 1163 2484 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1229 1590 L 1122 2478" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1122 2483 L 1119 2476 L 1122 2478 L 1126 2477 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1035 2580 C 1038 2576 1042 2575 1046 2575 L 1080 2575 C 1084 2575 1088 2576 1091 2580 L 1111 2603 C 1112 2604 1112 2605 1111 2607 L 1091 2630 C 1088 2633 1084 2635 1080 2635 L 1046 2635 C 1042 2635 1038 2633 1035 2630 L 1015 2607 C 1015 2605 1015 2604 1015 2603 L 1035 2580 Z" fill="#e1d5e7" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1063" y="2609">any finger up</text>
- </g>
- <path d="M 1178 2609 C 1180 2605 1184 2604 1189 2604 L 1222 2604 C 1227 2604 1231 2605 1233 2609 L 1253 2632 C 1254 2633 1254 2634 1253 2636 L 1233 2659 C 1231 2662 1227 2664 1222 2664 L 1189 2664 C 1184 2664 1180 2662 1178 2659 L 1158 2636 C 1157 2634 1157 2633 1158 2632 L 1178 2609 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1206" y="2631">fourth</text>
- <text x="1206" y="2645">finger up</text>
- </g>
- <path d="M 1358 1540 C 1360 1536 1364 1535 1369 1535 L 1402 1535 C 1407 1535 1411 1536 1413 1540 L 1433 1563 C 1434 1564 1434 1565 1433 1567 L 1413 1590 C 1411 1593 1407 1595 1402 1595 L 1369 1595 C 1364 1595 1360 1593 1358 1590 L 1338 1567 C 1337 1565 1337 1564 1338 1563 L 1358 1540 Z" fill="#e1d5e7" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1386" y="1569">any finger up</text>
- </g>
- <path d="M 1429 1390 L 1394 1528" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1393 1533 L 1391 1526 L 1394 1528 L 1398 1528 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1367 1753 L 1332 1845" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1331 1850 L 1330 1842 L 1332 1845 L 1336 1844 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <text x="811.75" y="1039.75">
+ first</text>
+ <text x="811.75" y="1053.75">
+ finger up</text>
+ </g>
+ <path d="M 1125 1167 L 866.92 1064.35" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 862.04 1062.41 L 869.84 1061.75 L 866.92 1064.35 L 867.25 1068.25 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 471.22 968.1 C 472.36 964.9 474.22 963.04 476.18 963.12 L 544.64 963.12 C 546.2 963.08 547.7 963.63 548.67 964.61 C 549.65 965.59 549.99 966.88 549.6 968.1 L 530.75 1017.9 C 529.61 1021.1 527.75 1022.96 525.79 1022.88 L 456.34 1022.88 C 454.95 1022.69 453.73 1022.05 452.97 1021.1 C 452.22 1020.15 452 1018.99 452.37 1017.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="500.75" y="989.75">
+ button 1</text>
+ <text x="500.75" y="1003.75">
+ release</text>
+ </g>
+ <path d="M 222.22 1307.1 C 223.36 1303.9 225.22 1302.04 227.18 1302.12 L 295.64 1302.12 C 297.2 1302.08 298.7 1302.63 299.67 1303.61 C 300.65 1304.59 300.99 1305.88 300.6 1307.1 L 281.75 1356.9 C 280.61 1360.1 278.75 1361.96 276.79 1361.88 L 207.34 1361.88 C 205.95 1361.69 204.73 1361.05 203.97 1360.1 C 203.22 1359.15 203 1357.99 203.37 1356.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="251.75" y="1328.75">
+ button 1</text>
+ <text x="251.75" y="1342.75">
+ press</text>
+ </g>
+ <path d="M 222.72 1447.1 C 223.86 1443.9 225.72 1442.04 227.68 1442.12 L 296.14 1442.12 C 297.7 1442.08 299.2 1442.63 300.17 1443.61 C 301.15 1444.59 301.49 1445.88 301.1 1447.1 L 282.25 1496.9 C 281.11 1500.1 279.25 1501.96 277.29 1501.88 L 207.84 1501.88 C 206.45 1501.69 205.23 1501.05 204.47 1500.1 C 203.72 1499.15 203.5 1497.99 203.87 1496.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="252.25" y="1468.75">
+ btn1</text>
+ <text x="252.25" y="1482.75">
+ release</text>
+ </g>
+ <path d="M 252 1362 L 252 1435.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 252 1440.88 L 248.5 1433.88 L 252 1435.63 L 255.5 1433.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1102.54 1252 C 1105.34 1248.86 1109.32 1247.05 1113.52 1247 L 1147.49 1247 C 1151.69 1247.05 1155.67 1248.86 1158.47 1252 L 1178.45 1275 C 1179.01 1276.28 1179.01 1277.72 1178.45 1279 L 1158.47 1302 C 1155.67 1305.14 1151.69 1306.95 1147.49 1307 L 1113.52 1307 C 1109.32 1306.95 1105.34 1305.14 1102.54 1302 L 1082.56 1279 C 1082 1277.72 1082 1276.28 1082.56 1275 L 1102.54 1252 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1130.25" y="1273.75">
+ second</text>
+ <text x="1130.25" y="1287.75">
+ finger down</text>
+ </g>
+ <path d="M 1165 1207 L 1147.85 1241.3" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1145.5 1246 L 1145.5 1238.17 L 1147.85 1241.3 L 1151.76 1241.3 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 742.54 1257 C 745.34 1253.86 749.32 1252.05 753.52 1252 L 787.49 1252 C 791.69 1252.05 795.67 1253.86 798.47 1257 L 818.45 1280 C 819.01 1281.28 819.01 1282.72 818.45 1284 L 798.47 1307 C 795.67 1310.14 791.69 1311.95 787.49 1312 L 753.52 1312 C 749.32 1311.95 745.34 1310.14 742.54 1307 L 722.56 1284 C 722 1282.72 722 1281.28 722.56 1280 L 742.54 1257 Z" fill="#67ab9f" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="770.25" y="1278.75">
+ move &gt; </text>
+ <text x="770.25" y="1292.75">
+ threshold</text>
+ </g>
+ <path d="M 1090 1207 L 825.2 1269.54" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 820.09 1270.74 L 826.1 1265.73 L 825.2 1269.54 L 827.71 1272.54 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 906 1413 L 897.77 1480.68" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 897.13 1485.89 L 894.51 1478.52 L 897.77 1480.68 L 901.45 1479.36 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <rect x="1872" y="577" width="130" height="40" rx="2.4" ry="2.4" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1936.75" y="600.75">
+ HOLD</text>
+ </g>
+ <path d="M 2095.04 705 C 2097.84 701.86 2101.82 700.05 2106.02 700 L 2139.99 700 C 2144.19 700.05 2148.17 701.86 2150.97 705 L 2170.95 728 C 2171.51 729.28 2171.51 730.72 2170.95 732 L 2150.97 755 C 2148.17 758.14 2144.19 759.95 2139.99 760 L 2106.02 760 C 2101.82 759.95 2097.84 758.14 2095.04 755 L 2075.06 732 C 2074.5 730.72 2074.5 729.28 2075.06 728 L 2095.04 705 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="2122.75" y="726.75">
+ first</text>
+ <text x="2122.75" y="740.75">
+ finger up</text>
+ </g>
+ <path d="M 1965 617 L 2075.82 696.29" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2080.09 699.35 L 2072.36 698.12 L 2075.82 696.29 L 2076.43 692.43 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2264 807 L 2285.64 807.77" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2290.88 807.96 L 2283.76 811.21 L 2285.64 807.77 L 2284.01 804.21 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1879.54 672 C 1882.34 668.86 1886.32 667.05 1890.52 667 L 1924.49 667 C 1928.69 667.05 1932.67 668.86 1935.47 672 L 1955.45 695 C 1956.01 696.28 1956.01 697.72 1955.45 699 L 1935.47 722 C 1932.67 725.14 1928.69 726.95 1924.49 727 L 1890.52 727 C 1886.32 726.95 1882.34 725.14 1879.54 722 L 1859.56 699 C 1859 697.72 1859 696.28 1859.56 695 L 1879.54 672 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1907.25" y="693.75">
+ second</text>
+ <text x="1907.25" y="707.75">
+ finger down</text>
+ </g>
+ <path d="M 1931 617 L 1917.83 660.9" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1916.32 665.93 L 1914.98 658.22 L 1917.83 660.9 L 1921.69 660.23 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1887 818 L 1884.66 840.67" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1884.12 845.89 L 1881.35 838.56 L 1884.66 840.67 L 1888.32 839.29 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1803 670 L 1894.41 620.05" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1899.02 617.54 L 1894.55 623.96 L 1894.41 620.05 L 1891.2 617.82 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <rect x="2213.25" y="1117" width="130" height="40" rx="2.4" ry="2.4" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="2278" y="1140.75">
+ TOUCH_2_HOLD</text>
+ </g>
+ <path d="M 2392.79 1222 C 2395.59 1218.86 2399.57 1217.05 2403.77 1217 L 2437.74 1217 C 2441.94 1217.05 2445.92 1218.86 2448.72 1222 L 2468.7 1245 C 2469.26 1246.28 2469.26 1247.72 2468.7 1249 L 2448.72 1272 C 2445.92 1275.14 2441.94 1276.95 2437.74 1277 L 2403.77 1277 C 2399.57 1276.95 2395.59 1275.14 2392.79 1272 L 2372.81 1249 C 2372.25 1247.72 2372.25 1246.28 2372.81 1245 L 2392.79 1222 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="2420.5" y="1243.75">
+ second</text>
+ <text x="2420.5" y="1257.75">
+ finger up</text>
+ </g>
+ <path d="M 2304 1157 L 2376.95 1213.12" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2381.11 1216.32 L 2373.43 1214.82 L 2376.95 1213.12 L 2377.7 1209.28 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2088.29 1227 C 2091.09 1223.86 2095.07 1222.05 2099.27 1222 L 2133.24 1222 C 2137.44 1222.05 2141.42 1223.86 2144.22 1227 L 2164.2 1250 C 2164.76 1251.28 2164.76 1252.72 2164.2 1254 L 2144.22 1277 C 2141.42 1280.14 2137.44 1281.95 2133.24 1282 L 2099.27 1282 C 2095.07 1281.95 2091.09 1280.14 2088.29 1277 L 2068.31 1254 C 2067.75 1252.72 2067.75 1251.28 2068.31 1250 L 2088.29 1227 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="2116" y="1248.75">
+ first</text>
+ <text x="2116" y="1262.75">
+ finger up</text>
+ </g>
+ <path d="M 2250 1157 L 2164.18 1218.3" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2159.91 1221.35 L 2163.57 1214.43 L 2164.18 1218.3 L 2167.64 1220.13 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2402 873 L 2402 607 Q 2402 597 2392 597 L 2008.37 597" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2003.12 597 L 2010.12 593.5 L 2008.37 597 L 2010.12 600.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2062 1083 L 2062 607 Q 2062 597 2052 597 L 2008.37 597" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2003.12 597 L 2010.12 593.5 L 2008.37 597 L 2010.12 600.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2233.79 1232 C 2236.59 1228.86 2240.57 1227.05 2244.77 1227 L 2278.74 1227 C 2282.94 1227.05 2286.92 1228.86 2289.72 1232 L 2309.7 1255 C 2310.26 1256.28 2310.26 1257.72 2309.7 1259 L 2289.72 1282 C 2286.92 1285.14 2282.94 1286.95 2278.74 1287 L 2244.77 1287 C 2240.57 1286.95 2236.59 1285.14 2233.79 1282 L 2213.81 1259 C 2213.25 1257.72 2213.25 1256.28 2213.81 1255 L 2233.79 1232 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="2261.5" y="1253.75">
+ third</text>
+ <text x="2261.5" y="1267.75">
+ finger down</text>
+ </g>
+ <path d="M 2276 1157 L 2266.9 1220.7" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2266.16 1225.89 L 2263.68 1218.47 L 2266.9 1220.7 L 2270.61 1219.46 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1987 1352 L 1922.33 1359.29" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1917.11 1359.87 L 1923.68 1355.61 L 1922.33 1359.29 L 1924.46 1362.57 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1988 1984 L 2482 1984 Q 2492 1984 2492 1974 L 2492 1147 Q 2492 1137 2482 1137 L 2349.37 1137" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2344.12 1137 L 2351.12 1133.5 L 2349.37 1137 L 2351.12 1140.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <rect x="1952" y="2224" width="130" height="40" rx="2.4" ry="2.4" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="2016.75" y="2247.75">
+ TOUCH_3_HOLD</text>
+ </g>
+ <path d="M 2009 1824 L 2016.87 2217.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2016.98 2222.88 L 2013.34 2215.95 L 2016.87 2217.63 L 2020.34 2215.81 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1713.04 2362 C 1715.84 2358.86 1719.82 2357.05 1724.02 2357 L 1757.99 2357 C 1762.19 2357.05 1766.17 2358.86 1768.97 2362 L 1788.95 2385 C 1789.51 2386.28 1789.51 2387.72 1788.95 2389 L 1768.97 2412 C 1766.17 2415.14 1762.19 2416.95 1757.99 2417 L 1724.02 2417 C 1719.82 2416.95 1715.84 2415.14 1713.04 2412 L 1693.06 2389 C 1692.5 2387.72 1692.5 2386.28 1693.06 2385 L 1713.04 2362 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1740.75" y="2383.75">
+ fourth</text>
+ <text x="1740.75" y="2397.75">
+ finger down</text>
+ </g>
+ <rect x="1472" y="2482" width="130" height="40" rx="2.4" ry="2.4" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1536.75" y="2505.75">
+ DEAD</text>
+ </g>
+ <path d="M 1979 2264 L 1795.65 2359.07" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1790.99 2361.49 L 1795.6 2355.16 L 1795.65 2359.07 L 1798.82 2361.37 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1693 2414 L 1577.55 2478.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1572.97 2481.45 L 1577.36 2474.97 L 1577.55 2478.88 L 1580.79 2481.07 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1647 1587 L 1539.76 2475.68" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1539.13 2480.89 L 1536.5 2473.52 L 1539.76 2475.68 L 1543.45 2474.36 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1453.04 2577 C 1455.84 2573.86 1459.82 2572.05 1464.02 2572 L 1497.99 2572 C 1502.19 2572.05 1506.17 2573.86 1508.97 2577 L 1528.95 2600 C 1529.51 2601.28 1529.51 2602.72 1528.95 2604 L 1508.97 2627 C 1506.17 2630.14 1502.19 2631.95 1497.99 2632 L 1464.02 2632 C 1459.82 2631.95 1455.84 2630.14 1453.04 2627 L 1433.06 2604 C 1432.5 2602.72 1432.5 2601.28 1433.06 2600 L 1453.04 2577 Z" fill="#e1d5e7" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1480.75" y="2605.75">
+ any finger up</text>
+ </g>
+ <path d="M 1595.54 2606 C 1598.34 2602.86 1602.32 2601.05 1606.52 2601 L 1640.49 2601 C 1644.69 2601.05 1648.67 2602.86 1651.47 2606 L 1671.45 2629 C 1672.01 2630.28 1672.01 2631.72 1671.45 2633 L 1651.47 2656 C 1648.67 2659.14 1644.69 2660.95 1640.49 2661 L 1606.52 2661 C 1602.32 2660.95 1598.34 2659.14 1595.54 2656 L 1575.56 2633 C 1575 2631.72 1575 2630.28 1575.56 2629 L 1595.54 2606 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1623.25" y="2627.75">
+ fourth</text>
+ <text x="1623.25" y="2641.75">
+ finger up</text>
+ </g>
+ <path d="M 1775.54 1537 C 1778.34 1533.86 1782.32 1532.05 1786.52 1532 L 1820.49 1532 C 1824.69 1532.05 1828.67 1533.86 1831.47 1537 L 1851.45 1560 C 1852.01 1561.28 1852.01 1562.72 1851.45 1564 L 1831.47 1587 C 1828.67 1590.14 1824.69 1591.95 1820.49 1592 L 1786.52 1592 C 1782.32 1591.95 1778.34 1590.14 1775.54 1587 L 1755.56 1564 C 1755 1562.72 1755 1561.28 1755.56 1560 L 1775.54 1537 Z" fill="#e1d5e7" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1803.25" y="1565.75">
+ any finger up</text>
+ </g>
+ <path d="M 1847 1387 L 1812.53 1525.82" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1811.27 1530.91 L 1809.56 1523.28 L 1812.53 1525.82 L 1816.35 1524.96 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1785 1750 L 1750.25 1842.04" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1748.39 1846.95 L 1747.59 1839.17 L 1750.25 1842.04 L 1754.14 1841.64 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="11px">
- <rect fill="#ffffff" stroke="none" x="1339" y="1789" width="20" height="27" stroke-width="0"/>
- <text x="1349" y="1812">yes</text>
- </g>
- <path d="M 1591 2335 C 1594 2331 1598 2330 1602 2330 L 1636 2330 C 1640 2330 1644 2331 1647 2335 L 1667 2358 C 1668 2359 1668 2360 1667 2362 L 1647 2385 C 1644 2388 1640 2390 1636 2390 L 1602 2390 C 1598 2390 1594 2388 1591 2385 L 1571 2362 C 1571 2360 1571 2359 1571 2358 L 1591 2335 Z" fill="#e1d5e7" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1619" y="2364">any finger up</text>
- </g>
- <path d="M 1603 2267 L 1613 2323" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1613 2328 L 1609 2322 L 1613 2323 L 1616 2321 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1958 2355 L 2064 2355 Q 2074 2355 2074 2345 L 2074 1150 Q 2074 1140 2064 1140 L 1932 1140" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1926 1140 L 1933 1136 L 1932 1140 L 1933 1143 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1108 2525 L 1083 2569" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1080 2574 L 1081 2566 L 1083 2569 L 1087 2569 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1132 2525 L 1182 2598" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1185 2603 L 1178 2599 L 1182 2598 L 1184 2595 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <ellipse cx="1126" cy="2985" rx="49.5" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1126" y="2989">IDLE</text>
- </g>
- <path d="M 1121 2825 L 1173 2870 L 1121 2915 L 1070 2870 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1121" y="2867">if finger</text>
- <text x="1121" y="2881">count == 0</text>
- </g>
- <path d="M 1079 2728 L 1106 2818" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1108 2824 L 1102 2818 L 1106 2818 L 1109 2816 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1195 2664 L 1140 2819" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1138 2824 L 1137 2816 L 1140 2819 L 1143 2818 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1123 2915 L 1125 2948" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1125 2953 L 1121 2947 L 1125 2948 L 1128 2946 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 659 1648 C 661 1644 665 1643 670 1643 L 703 1643 C 708 1643 712 1644 714 1648 L 734 1671 C 735 1672 735 1673 734 1675 L 714 1698 C 712 1701 708 1703 703 1703 L 670 1703 C 665 1703 661 1701 659 1698 L 639 1675 C 638 1673 638 1672 639 1671 L 659 1648 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="686" y="1670">second</text>
- <text x="686" y="1684">finger up</text>
- </g>
- <rect x="599" y="1490" width="130" height="40" rx="2" ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="664" y="1514">DRAGGING_2</text>
- </g>
- <path d="M 678 1435 L 669 1483" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 668 1488 L 666 1481 L 669 1483 L 673 1482 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 667 1530 L 681 1636" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 682 1641 L 678 1635 L 681 1636 L 685 1634 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 486 1712 C 488 1708 492 1707 497 1707 L 530 1707 C 535 1707 539 1708 541 1712 L 561 1735 C 562 1736 562 1737 561 1739 L 541 1762 C 539 1765 535 1767 530 1767 L 497 1767 C 492 1767 488 1765 486 1762 L 466 1739 C 465 1737 465 1736 466 1735 L 486 1712 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="514" y="1734">first</text>
- <text x="514" y="1748">finger up</text>
- </g>
- <path d="M 651 1530 L 537 1701" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 534 1706 L 535 1698 L 537 1701 L 541 1702 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 494 1612 L 481 1536" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 481 1531 L 485 1537 L 481 1536 L 478 1538 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 640 1728 L 496 1535" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 493 1530 L 500 1534 L 496 1535 L 494 1538 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 373 1742 C 375 1738 379 1737 384 1737 L 417 1737 C 422 1737 426 1738 428 1742 L 448 1765 C 449 1766 449 1767 448 1769 L 428 1792 C 426 1795 422 1797 417 1797 L 384 1797 C 379 1797 375 1795 373 1792 L 353 1769 C 352 1767 352 1766 353 1765 L 373 1742 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="401" y="1764">second</text>
- <text x="401" y="1778">finger down</text>
- </g>
- <path d="M 471 1530 L 411 1730" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 410 1735 L 408 1728 L 411 1730 L 415 1730 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 561 1817 L 655 1536" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 657 1531 L 658 1538 L 655 1536 L 651 1536 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 564 1991 L 611 1996" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 616 1997 L 609 1999 L 611 1996 L 609 1992 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 750 1620 C 752 1616 756 1615 761 1615 L 794 1615 C 799 1615 803 1616 805 1620 L 825 1643 C 826 1644 826 1645 825 1647 L 805 1670 C 803 1673 799 1675 794 1675 L 761 1675 C 756 1675 752 1673 750 1670 L 730 1647 C 729 1645 729 1644 730 1643 L 750 1620 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="777" y="1642">third</text>
- <text x="777" y="1656">finger down</text>
- </g>
- <path d="M 681 1530 L 748 1610" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 752 1614 L 744 1611 L 748 1610 L 750 1606 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 922 1940 C 922 1938 923 1937 924 1936 C 925 1935 926 1935 927 1935 L 995 1935 C 997 1935 998 1935 999 1936 C 1000 1937 1000 1938 1000 1940 L 981 1989 C 981 1991 980 1992 979 1993 C 978 1994 977 1995 976 1994 L 907 1994 C 905 1994 904 1994 903 1993 C 903 1992 902 1991 903 1989 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="952" y="1962">btn1</text>
- <text x="952" y="1976">release</text>
- </g>
- <path d="M 961 1995 L 1111 2478" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1112 2484 L 1107 2478 L 1111 2478 L 1114 2476 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 2225 835 C 2227 831 2231 830 2236 830 L 2269 830 C 2274 830 2278 831 2280 835 L 2300 858 C 2301 859 2301 860 2300 862 L 2280 885 C 2278 888 2274 890 2269 890 L 2236 890 C 2231 890 2227 888 2225 885 L 2205 862 C 2204 860 2204 859 2205 858 L 2225 835 Z" fill="#000000" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <rect fill="#ffffff" stroke="none" x="1758" y="1787" width="18" height="27" stroke-width="0"/>
+ <text x="1766.5" y="1809">
+ yes</text>
+ </g>
+ <path d="M 2009.04 2332 C 2011.84 2328.86 2015.82 2327.05 2020.02 2327 L 2053.99 2327 C 2058.19 2327.05 2062.17 2328.86 2064.97 2332 L 2084.95 2355 C 2085.51 2356.28 2085.51 2357.72 2084.95 2359 L 2064.97 2382 C 2062.17 2385.14 2058.19 2386.95 2053.99 2387 L 2020.02 2387 C 2015.82 2386.95 2011.84 2385.14 2009.04 2382 L 1989.06 2359 C 1988.5 2357.72 1988.5 2356.28 1989.06 2355 L 2009.04 2332 Z" fill="#e1d5e7" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="2036.75" y="2360.75">
+ any finger up</text>
+ </g>
+ <path d="M 2021 2264 L 2030.9 2320.73" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2031.81 2325.9 L 2027.16 2319.6 L 2030.9 2320.73 L 2034.05 2318.4 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2376 2352 L 2482 2352 Q 2492 2352 2492 2342 L 2492 1147 Q 2492 1137 2482 1137 L 2349.37 1137" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2344.12 1137 L 2351.12 1133.5 L 2349.37 1137 L 2351.12 1140.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1526 2522 L 1501.11 2566.44" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1498.55 2571.02 L 1498.91 2563.21 L 1501.11 2566.44 L 1505.02 2566.63 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1550 2522 L 1599.45 2595.71" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1602.38 2600.07 L 1595.57 2596.21 L 1599.45 2595.71 L 1601.38 2592.31 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <ellipse cx="1544" cy="2982" rx="49.5" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1543.75" y="2985.75">
+ IDLE</text>
+ </g>
+ <path d="M 1539.4 2822 L 1591 2867 L 1539.4 2912 L 1487.8 2867 Z" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1539.15" y="2863.75">
+ if finger</text>
+ <text x="1539.15" y="2877.75">
+ count == 0</text>
+ </g>
+ <path d="M 1497 2726 L 1524.16 2815.9" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1525.68 2820.93 L 1520.3 2815.24 L 1524.16 2815.9 L 1527 2813.22 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1613 2661 L 1557.16 2816.01" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1555.38 2820.95 L 1554.46 2813.18 L 1557.16 2816.01 L 1561.04 2815.55 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1541 2912 L 1542.68 2945.64" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1542.94 2950.88 L 1539.1 2944.07 L 1542.68 2945.64 L 1546.09 2943.72 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1076.54 1645 C 1079.34 1641.86 1083.32 1640.05 1087.52 1640 L 1121.49 1640 C 1125.69 1640.05 1129.67 1641.86 1132.47 1645 L 1152.45 1668 C 1153.01 1669.28 1153.01 1670.72 1152.45 1672 L 1132.47 1695 C 1129.67 1698.14 1125.69 1699.95 1121.49 1700 L 1087.52 1700 C 1083.32 1699.95 1079.34 1698.14 1076.54 1695 L 1056.56 1672 C 1056 1670.72 1056 1669.28 1056.56 1668 L 1076.54 1645 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1104.25" y="1666.75">
+ second</text>
+ <text x="1104.25" y="1680.75">
+ finger up</text>
+ </g>
+ <rect x="1017" y="1487" width="130" height="40" rx="2.4" ry="2.4" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1081.75" y="1510.75">
+ DRAGGING_2</text>
+ </g>
+ <path d="M 1096 1433 L 1087.16 1480.74" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1086.2 1485.9 L 1084.04 1478.38 L 1087.16 1480.74 L 1090.92 1479.66 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1085 1527 L 1099.16 1633.69" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1099.85 1638.89 L 1095.46 1632.41 L 1099.16 1633.69 L 1102.4 1631.49 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 903.54 1709 C 906.34 1705.86 910.32 1704.05 914.52 1704 L 948.49 1704 C 952.69 1704.05 956.67 1705.86 959.47 1709 L 979.45 1732 C 980.01 1733.28 980.01 1734.72 979.45 1736 L 959.47 1759 C 956.67 1762.14 952.69 1763.95 948.49 1764 L 914.52 1764 C 910.32 1763.95 906.34 1762.14 903.54 1759 L 883.56 1736 C 883 1734.72 883 1733.28 883.56 1732 L 903.54 1709 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="931.25" y="1730.75">
+ first</text>
+ <text x="931.25" y="1744.75">
+ finger up</text>
+ </g>
+ <path d="M 1069 1527 L 954.53 1698.7" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 951.62 1703.07 L 952.59 1695.3 L 954.53 1698.7 L 958.42 1699.19 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 912 1610 L 899.06 1533.28" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 898.19 1528.1 L 902.8 1534.42 L 899.06 1533.28 L 895.9 1535.59 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1058 1726 L 913.8 1532.11" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 910.67 1527.9 L 917.65 1531.43 L 913.8 1532.11 L 912.04 1535.6 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 790.54 1739 C 793.34 1735.86 797.32 1734.05 801.52 1734 L 835.49 1734 C 839.69 1734.05 843.67 1735.86 846.47 1739 L 866.45 1762 C 867.01 1763.28 867.01 1764.72 866.45 1766 L 846.47 1789 C 843.67 1792.14 839.69 1793.95 835.49 1794 L 801.52 1794 C 797.32 1793.95 793.34 1792.14 790.54 1789 L 770.56 1766 C 770 1764.72 770 1763.28 770.56 1762 L 790.54 1739 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="818.25" y="1760.75">
+ second</text>
+ <text x="818.25" y="1774.75">
+ finger down</text>
+ </g>
+ <path d="M 889 1527 L 828.83 1727.9" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 827.32 1732.93 L 825.98 1725.22 L 828.83 1727.9 L 832.68 1727.23 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 979 1815 L 1072.99 1533.04" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1074.65 1528.06 L 1075.75 1535.81 L 1072.99 1533.04 L 1069.11 1533.59 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 982 1989 L 1028.66 1993.4" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1033.89 1993.89 L 1026.59 1996.72 L 1028.66 1993.4 L 1027.25 1989.75 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1167.54 1617 C 1170.34 1613.86 1174.32 1612.05 1178.52 1612 L 1212.49 1612 C 1216.69 1612.05 1220.67 1613.86 1223.47 1617 L 1243.45 1640 C 1244.01 1641.28 1244.01 1642.72 1243.45 1644 L 1223.47 1667 C 1220.67 1670.14 1216.69 1671.95 1212.49 1672 L 1178.52 1672 C 1174.32 1671.95 1170.34 1670.14 1167.54 1667 L 1147.56 1644 C 1147 1642.72 1147 1641.28 1147.56 1640 L 1167.54 1617 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1195.25" y="1638.75">
+ third</text>
+ <text x="1195.25" y="1652.75">
+ finger down</text>
+ </g>
+ <path d="M 1099 1527 L 1165.92 1607.11" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1169.28 1611.14 L 1162.11 1608.01 L 1165.92 1607.11 L 1167.48 1603.53 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1350.72 2101.1 C 1351.86 2097.9 1353.72 2096.04 1355.68 2096.12 L 1424.14 2096.12 C 1425.7 2096.08 1427.2 2096.63 1428.17 2097.61 C 1429.15 2098.59 1429.49 2099.88 1429.1 2101.1 L 1410.25 2150.9 C 1409.11 2154.1 1407.25 2155.96 1405.29 2155.88 L 1335.84 2155.88 C 1334.45 2155.69 1333.23 2155.05 1332.47 2154.1 C 1331.72 2153.15 1331.5 2151.99 1331.87 2150.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1380.25" y="2122.75">
+ btn1</text>
+ <text x="1380.25" y="2136.75">
+ release</text>
+ </g>
+ <path d="M 1393 2156 L 1526.55 2476.12" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1528.57 2480.97 L 1522.64 2475.86 L 1526.55 2476.12 L 1529.1 2473.16 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2642.54 832 C 2645.34 828.86 2649.32 827.05 2653.52 827 L 2687.49 827 C 2691.69 827.05 2695.67 828.86 2698.47 832 L 2718.45 855 C 2719.01 856.28 2719.01 857.72 2718.45 859 L 2698.47 882 C 2695.67 885.14 2691.69 886.95 2687.49 887 L 2653.52 887 C 2649.32 886.95 2645.34 885.14 2642.54 882 L 2622.56 859 C 2622 857.72 2622 856.28 2622.56 855 L 2642.54 832 Z" fill="#000000" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#FFFFFF" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="2252" y="850">clickpad</text>
- <text x="2252" y="864">button</text>
- <text x="2252" y="878">press</text>
- </g>
- <path d="M 1575 620 L 2198 840" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
- <path d="M 2203 842 L 2195 843 L 2198 840 L 2198 836 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1529 869 L 2198 860" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
- <path d="M 2203 860 L 2196 864 L 2198 860 L 2196 857 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1888 1120 L 2205 893" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
- <path d="M 2210 890 L 2206 897 L 2205 893 L 2202 891 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1608 2227 L 2236 895" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
- <path d="M 2238 891 L 2238 898 L 2236 895 L 2232 895 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 2252 2013 L 2252 2495 Q 2252 2505 2242 2505 L 1190 2505" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1185 2505 L 1192 2501 L 1190 2505 L 1192 2508 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1346 65 L 2214 825" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
- <path d="M 2217 829 L 2210 827 L 2214 825 L 2215 822 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 25 1205 C 27 1201 31 1200 36 1200 L 69 1200 C 74 1200 78 1201 80 1205 L 100 1228 C 101 1229 101 1230 100 1232 L 80 1255 C 78 1258 74 1260 69 1260 L 36 1260 C 31 1260 27 1258 25 1255 L 5 1232 C 4 1230 4 1229 5 1228 L 25 1205 Z" fill="#000000" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <text x="2670.25" y="846.75">
+ phys</text>
+ <text x="2670.25" y="860.75">
+ button</text>
+ <text x="2670.25" y="874.75">
+ press</text>
+ </g>
+ <path d="M 1993 617 L 2616 837.87" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
+ <path d="M 2620.95 839.63 L 2613.18 840.59 L 2616 837.87 L 2615.52 833.99 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1947 866 L 2615.63 858.08" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
+ <path d="M 2620.88 858.01 L 2613.92 861.6 L 2615.63 858.08 L 2613.84 854.6 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2306 1117 L 2622.82 890.7" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
+ <path d="M 2627.09 887.65 L 2623.43 894.57 L 2622.82 890.7 L 2619.36 888.87 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2026 2224 L 2653.29 892.76" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
+ <path d="M 2655.52 888.01 L 2655.71 895.84 L 2653.29 892.76 L 2649.37 892.85 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2670 2011 L 2670 2492 Q 2670 2502 2660 2502 L 1608.37 2502" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1603.12 2502 L 1610.12 2498.5 L 1608.37 2502 L 1610.12 2505.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1764 62 L 2631.21 822.8" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
+ <path d="M 2635.16 826.26 L 2627.59 824.28 L 2631.21 822.8 L 2632.21 819.02 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 442.54 1202 C 445.34 1198.86 449.32 1197.05 453.52 1197 L 487.49 1197 C 491.69 1197.05 495.67 1198.86 498.47 1202 L 518.45 1225 C 519.01 1226.28 519.01 1227.72 518.45 1229 L 498.47 1252 C 495.67 1255.14 491.69 1256.95 487.49 1257 L 453.52 1257 C 449.32 1256.95 445.34 1255.14 442.54 1252 L 422.56 1229 C 422 1227.72 422 1226.28 422.56 1225 L 442.54 1202 Z" fill="#000000" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#FFFFFF" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="53" y="1220">clickpad</text>
- <text x="53" y="1234">button</text>
- <text x="53" y="1248">press</text>
- </g>
- <path d="M 981 959 L 107 1214" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
- <path d="M 102 1215 L 108 1210 L 107 1214 L 110 1216 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 43 2030 C 44 2028 44 2027 45 2026 C 46 2025 47 2025 48 2025 L 117 2025 C 118 2025 120 2025 121 2026 C 122 2027 122 2028 122 2030 L 103 2079 C 102 2081 101 2082 101 2083 C 100 2084 99 2085 98 2084 L 28 2084 C 27 2084 26 2084 25 2083 C 24 2082 24 2081 24 2079 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="73" y="2052">button 1</text>
- <text x="73" y="2066">release</text>
- </g>
- <path d="M 53 1260 L 72 2018" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 72 2023 L 69 2017 L 72 2018 L 76 2016 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 653 2283 L 1071 2482" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1076 2484 L 1068 2484 L 1071 2482 L 1071 2478 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 653 1196 L 107 1226" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
- <path d="M 102 1227 L 109 1223 L 107 1226 L 109 1230 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 447 1490 L 103 1263" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
- <path d="M 99 1260 L 107 1261 L 103 1263 L 103 1267 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 620 1490 L 107 1254" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
- <path d="M 102 1252 L 110 1252 L 107 1254 L 107 1258 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <rect x="226" y="1842" width="130" height="40" rx="2" ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="291" y="1866">DRAGGING_WAIT</text>
- </g>
- <path d="M 357 1947 C 360 1943 364 1942 368 1942 L 402 1942 C 406 1942 410 1943 413 1947 L 433 1970 C 434 1971 434 1972 433 1974 L 413 1997 C 410 2000 406 2002 402 2002 L 368 2002 C 364 2002 360 2000 357 1997 L 337 1974 C 337 1972 337 1971 337 1970 L 357 1947 Z" fill="#99ffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="385" y="1976">timeout</text>
- </g>
- <path d="M 280 1787 L 289 1835" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 290 1840 L 286 1834 L 289 1835 L 292 1833 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 308 1882 L 355 1937" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 358 1941 L 351 1938 L 355 1937 L 357 1933 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 434 1977 L 460 1980" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 465 1981 L 458 1983 L 460 1980 L 458 1977 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 178 1947 C 180 1943 184 1942 189 1942 L 222 1942 C 227 1942 231 1943 233 1947 L 253 1970 C 254 1971 254 1972 253 1974 L 233 1997 C 231 2000 227 2002 222 2002 L 189 2002 C 184 2002 180 2000 178 1997 L 158 1974 C 157 1972 157 1971 158 1970 L 178 1947 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="206" y="1969">first</text>
- <text x="206" y="1983">finger down</text>
- </g>
- <path d="M 275 1882 L 233 1937" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 229 1941 L 231 1933 L 233 1937 L 236 1937 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1265 204 C 1281 210 1298 210 1315 204 C 1331 198 1348 198 1365 204 L 1365 257 C 1348 251 1331 251 1315 257 C 1298 263 1281 263 1265 257 L 1265 204 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1315" y="235">TOUCH_TOUCH</text>
- </g>
- <path d="M 998 593 C 1014 599 1031 599 1048 593 C 1064 587 1081 587 1098 593 L 1098 646 C 1081 640 1064 640 1048 646 C 1031 652 1014 652 998 646 L 998 593 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1048" y="624">TOUCH_IDLE</text>
- </g>
- <path d="M 1046 465 L 1047 581" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1047 586 L 1044 579 L 1047 581 L 1051 579 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1047 652 L 1046 713" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1046 718 L 1043 711 L 1046 713 L 1050 711 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1316 165 L 1315 192" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1315 197 L 1312 190 L 1315 192 L 1319 190 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1316 263 L 1318 288" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1318 293 L 1314 287 L 1318 288 L 1321 286 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1462 504 C 1478 510 1495 510 1512 504 C 1528 498 1545 498 1562 504 L 1562 557 C 1545 551 1528 551 1512 557 C 1495 563 1478 563 1462 557 L 1462 504 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1512" y="535">TOUCH_DEAD</text>
- </g>
- <path d="M 1443 465 L 1473 494" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1477 497 L 1470 495 L 1473 494 L 1475 490 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1568 465 L 1543 493" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1540 497 L 1542 490 L 1543 493 L 1547 494 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1515 563 L 1516 573" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1517 578 L 1512 572 L 1516 573 L 1519 571 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1199 960 L 1188 914" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1187 909 L 1192 915 L 1188 914 L 1185 916 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1187 808 L 1187 706" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1187 701 L 1190 708 L 1187 706 L 1183 708 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <text x="470.25" y="1216.75">
+ phys</text>
+ <text x="470.25" y="1230.75">
+ button</text>
+ <text x="470.25" y="1244.75">
+ press</text>
+ </g>
+ <path d="M 1399 956 L 525.11 1211.21" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
+ <path d="M 520.07 1212.69 L 525.81 1207.36 L 525.11 1211.21 L 527.77 1214.08 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 241.22 2117.1 C 242.36 2113.9 244.22 2112.04 246.18 2112.12 L 314.64 2112.12 C 316.2 2112.08 317.7 2112.63 318.67 2113.61 C 319.65 2114.59 319.99 2115.88 319.6 2117.1 L 300.75 2166.9 C 299.61 2170.1 297.75 2171.96 295.79 2171.88 L 226.34 2171.88 C 224.95 2171.69 223.73 2171.05 222.97 2170.1 C 222.22 2169.15 222 2167.99 222.37 2166.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="270.75" y="2138.75">
+ button 1</text>
+ <text x="270.75" y="2152.75">
+ release</text>
+ </g>
+ <path d="M 464 1257 L 279.35 2105.78" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 278.24 2110.91 L 276.31 2103.32 L 279.35 2105.78 L 283.15 2104.81 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1071 2281 L 1489.25 2479.27" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1493.99 2481.52 L 1486.17 2481.69 L 1489.25 2479.27 L 1489.16 2475.36 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1071 1193 L 525.36 1223.64" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
+ <path d="M 520.12 1223.94 L 526.91 1220.05 L 525.36 1223.64 L 527.3 1227.04 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 865 1487 L 521.32 1260.5" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
+ <path d="M 516.93 1257.62 L 524.7 1258.54 L 521.32 1260.5 L 520.85 1264.39 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1038 1487 L 524.79 1251.65" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/>
+ <path d="M 520.02 1249.47 L 527.84 1249.2 L 524.79 1251.65 L 524.92 1255.57 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <rect x="643.5" y="1839" width="130" height="40" rx="2.4" ry="2.4" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="708.25" y="1862.75">
+ DRAGGING_WAIT</text>
+ </g>
+ <path d="M 775.04 1944 C 777.84 1940.86 781.82 1939.05 786.02 1939 L 819.99 1939 C 824.19 1939.05 828.17 1940.86 830.97 1944 L 850.95 1967 C 851.51 1968.28 851.51 1969.72 850.95 1971 L 830.97 1994 C 828.17 1997.14 824.19 1998.95 819.99 1999 L 786.02 1999 C 781.82 1998.95 777.84 1997.14 775.04 1994 L 755.06 1971 C 754.5 1969.72 754.5 1968.28 755.06 1967 L 775.04 1944 Z" fill="#99ffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="802.75" y="1972.75">
+ timeout</text>
+ </g>
+ <path d="M 699 1785 L 707.84 1832.74" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 708.8 1837.9 L 704.08 1831.66 L 707.84 1832.74 L 710.96 1830.38 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 726 1879 L 772.88 1934.15" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 776.28 1938.15 L 769.08 1935.08 L 772.88 1934.15 L 774.41 1930.55 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 852 1974 L 877.68 1977.21" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 882.89 1977.86 L 875.51 1980.47 L 877.68 1977.21 L 876.38 1973.52 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 595.54 1944 C 598.34 1940.86 602.32 1939.05 606.52 1939 L 640.49 1939 C 644.69 1939.05 648.67 1940.86 651.47 1944 L 671.45 1967 C 672.01 1968.28 672.01 1969.72 671.45 1971 L 651.47 1994 C 648.67 1997.14 644.69 1998.95 640.49 1999 L 606.52 1999 C 602.32 1998.95 598.34 1997.14 595.54 1994 L 575.56 1971 C 575 1969.72 575 1968.28 575.56 1967 L 595.54 1944 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="623.25" y="1965.75">
+ first</text>
+ <text x="623.25" y="1979.75">
+ finger down</text>
+ </g>
+ <path d="M 693 1879 L 650.87 1933.95" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 647.68 1938.11 L 649.16 1930.43 L 650.87 1933.95 L 654.72 1934.69 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1682.5 201.7 C 1698.62 207.91 1716.38 207.91 1732.5 201.7 C 1748.62 195.5 1766.38 195.5 1782.5 201.7 L 1782.5 254.28 C 1766.38 248.08 1748.62 248.08 1732.5 254.28 C 1716.38 260.49 1698.62 260.49 1682.5 254.28 L 1682.5 201.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1732.25" y="231.75">
+ TOUCH_TOUCH</text>
+ </g>
+ <path d="M 1415.5 590.7 C 1431.62 596.91 1449.38 596.91 1465.5 590.7 C 1481.62 584.5 1499.38 584.5 1515.5 590.7 L 1515.5 643.28 C 1499.38 637.08 1481.62 637.08 1465.5 643.28 C 1449.38 649.49 1431.62 649.49 1415.5 643.28 L 1415.5 590.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1465.25" y="620.75">
+ TOUCH_IDLE</text>
+ </g>
+ <path d="M 1464 462 L 1464.95 578.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1464.99 583.88 L 1461.43 576.91 L 1464.95 578.63 L 1468.43 576.85 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1465 650 L 1464.1 710.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1464.02 715.88 L 1460.62 708.83 L 1464.1 710.63 L 1467.62 708.94 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1734 162 L 1733.19 189.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1733.03 194.88 L 1729.74 187.78 L 1733.19 189.63 L 1736.74 187.99 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1734 261 L 1735.59 285.65" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1735.93 290.88 L 1731.98 284.12 L 1735.59 285.65 L 1738.97 283.67 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1879.5 501.7 C 1895.62 507.91 1913.38 507.91 1929.5 501.7 C 1945.62 495.5 1963.38 495.5 1979.5 501.7 L 1979.5 554.28 C 1963.38 548.08 1945.62 548.08 1929.5 554.28 C 1913.38 560.49 1895.62 560.49 1879.5 554.28 L 1879.5 501.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1929.25" y="531.75">
+ TOUCH_DEAD</text>
+ </g>
+ <path d="M 1861 462 L 1891.43 491.56" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1895.2 495.22 L 1887.74 492.85 L 1891.43 491.56 L 1892.62 487.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1986 462 L 1961.13 491.16" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1957.73 495.15 L 1959.61 487.55 L 1961.13 491.16 L 1964.93 492.09 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1933 561 L 1934.21 570.68" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1934.86 575.89 L 1930.52 569.38 L 1934.21 570.68 L 1937.47 568.51 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1617 957 L 1606.43 911.2" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1605.25 906.09 L 1610.24 912.12 L 1606.43 911.2 L 1603.42 913.7 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1605 805 L 1605 703.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1605 698.12 L 1608.5 705.12 L 1605 703.37 L 1601.5 705.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="11px">
- <rect fill="#ffffff" stroke="none" x="1177" y="748" width="20" height="14" stroke-width="0"/>
- <text x="1187" y="757">yes</text>
- </g>
- <path d="M 1794 951 C 1810 957 1827 957 1844 951 C 1860 945 1877 945 1894 951 L 1894 1004 C 1877 998 1860 998 1844 1004 C 1827 1010 1810 1010 1794 1004 L 1794 951 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1844" y="982">TOUCH_DEAD</text>
- </g>
- <path d="M 1850 1075 L 1856 1113" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1857 1118 L 1853 1112 L 1856 1113 L 1859 1111 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1761 947 L 1788 957" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1792 959 L 1785 960 L 1788 957 L 1787 953 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1635 987 L 1787 980" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1792 980 L 1786 984 L 1787 980 L 1785 977 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1945 881 C 1961 887 1978 887 1995 881 C 2011 875 2028 875 2045 881 L 2045 934 C 2028 928 2011 928 1995 934 C 1978 940 1961 940 1945 934 L 1945 881 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1995" y="912">TOUCH_IDLE</text>
- </g>
- <path d="M 2002 1220 L 1995 946" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1995 941 L 1999 948 L 1995 946 L 1992 948 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1422 761 C 1438 767 1455 767 1472 761 C 1488 755 1505 755 1522 761 L 1522 814 C 1505 808 1488 808 1472 814 C 1455 820 1438 820 1422 814 L 1422 761 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1472" y="792">TOUCH_TOUCH</text>
- </g>
- <path d="M 1483 730 L 1479 749" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1478 754 L 1476 746 L 1479 749 L 1483 748 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1245 465 L 1445 750" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1448 754 L 1441 750 L 1445 750 L 1447 746 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1285 673 C 1301 679 1318 679 1335 673 C 1351 667 1368 667 1385 673 L 1385 726 C 1368 720 1351 720 1335 726 C 1318 732 1301 732 1285 726 L 1285 673 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1335" y="704">TOUCH_IDLE</text>
- </g>
- <path d="M 1230 605 L 1294 663" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1298 666 L 1290 664 L 1294 663 L 1295 659 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1285 786 C 1301 792 1318 792 1335 786 C 1351 780 1368 780 1385 786 L 1385 839 C 1368 833 1351 833 1335 839 C 1318 845 1301 845 1285 839 L 1285 786 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1335" y="817">TOUCH_IDLE</text>
+ <rect fill="#ffffff" stroke="none" x="1597" y="745" width="18" height="14" stroke-width="0"/>
+ <text x="1605" y="754.5">
+ yes</text>
+ </g>
+ <path d="M 2211.5 948.7 C 2227.62 954.91 2245.38 954.91 2261.5 948.7 C 2277.62 942.5 2295.38 942.5 2311.5 948.7 L 2311.5 1001.28 C 2295.38 995.08 2277.62 995.08 2261.5 1001.28 C 2245.38 1007.49 2227.62 1007.49 2211.5 1001.28 L 2211.5 948.7 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="2261.25" y="978.75">
+ TOUCH_DEAD</text>
+ </g>
+ <path d="M 2268 1073 L 2274 1110.71" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2274.82 1115.9 L 2270.27 1109.53 L 2274 1110.71 L 2277.18 1108.43 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2179 945 L 2206.02 954.82" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2210.95 956.62 L 2203.17 957.52 L 2206.02 954.82 L 2205.57 950.94 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2053 985 L 2205.64 977.32" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2210.88 977.06 L 2204.07 980.9 L 2205.64 977.32 L 2203.72 973.91 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2362.5 878.7 C 2378.62 884.91 2396.38 884.91 2412.5 878.7 C 2428.62 872.5 2446.38 872.5 2462.5 878.7 L 2462.5 931.28 C 2446.38 925.08 2428.62 925.08 2412.5 931.28 C 2396.38 937.49 2378.62 937.49 2362.5 931.28 L 2362.5 878.7 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="2412.25" y="908.75">
+ TOUCH_IDLE</text>
+ </g>
+ <path d="M 2420 1217 L 2413.16 944.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2413.03 939.12 L 2416.7 946.03 L 2413.16 944.37 L 2409.7 946.2 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1839.5 758.7 C 1855.62 764.91 1873.38 764.91 1889.5 758.7 C 1905.62 752.5 1923.38 752.5 1939.5 758.7 L 1939.5 811.28 C 1923.38 805.08 1905.62 805.08 1889.5 811.28 C 1873.38 817.49 1855.62 817.49 1839.5 811.28 L 1839.5 758.7 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1889.25" y="788.75">
+ TOUCH_TOUCH</text>
+ </g>
+ <path d="M 1901 727 L 1897.2 746.75" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1896.21 751.9 L 1894.1 744.37 L 1897.2 746.75 L 1900.97 745.69 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1663 462 L 1863.34 747.79" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1866.36 752.08 L 1859.47 748.36 L 1863.34 747.79 L 1865.21 744.34 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1702.5 670.7 C 1718.62 676.91 1736.38 676.91 1752.5 670.7 C 1768.62 664.5 1786.38 664.5 1802.5 670.7 L 1802.5 723.28 C 1786.38 717.08 1768.62 717.08 1752.5 723.28 C 1736.38 729.49 1718.62 729.49 1702.5 723.28 L 1702.5 670.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1752.25" y="700.75">
+ TOUCH_IDLE</text>
+ </g>
+ <path d="M 1648 602 L 1712.3 660.71" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1716.17 664.25 L 1708.65 662.11 L 1712.3 660.71 L 1713.36 656.94 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1702.5 783.7 C 1718.62 789.91 1736.38 789.91 1752.5 783.7 C 1768.62 777.5 1786.38 777.5 1802.5 783.7 L 1802.5 836.28 C 1786.38 830.08 1768.62 830.08 1752.5 836.28 C 1736.38 842.49 1718.62 842.49 1702.5 836.28 L 1702.5 783.7 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1752.25" y="813.75">
+ TOUCH_IDLE</text>
+ </g>
+ <path d="M 1755 977 L 1753.1 849.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1753.02 844.12 L 1756.62 851.06 L 1753.1 849.37 L 1749.62 851.17 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2011.5 1088.7 C 2027.62 1094.91 2045.38 1094.91 2061.5 1088.7 C 2077.62 1082.5 2095.38 1082.5 2111.5 1088.7 L 2111.5 1141.28 C 2095.38 1135.08 2077.62 1135.08 2061.5 1141.28 C 2045.38 1147.49 2027.62 1147.49 2011.5 1141.28 L 2011.5 1088.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="2061.25" y="1118.75">
+ TOUCH_IDLE</text>
+ </g>
+ <path d="M 2104 1222 L 2076.39 1153.9" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2074.42 1149.04 L 2080.29 1154.21 L 2076.39 1153.9 L 2073.81 1156.84 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1986.5 1320.7 C 2002.62 1326.91 2020.38 1326.91 2036.5 1320.7 C 2052.62 1314.5 2070.38 1314.5 2086.5 1320.7 L 2086.5 1373.28 C 2070.38 1367.08 2052.62 1367.08 2036.5 1373.28 C 2020.38 1379.49 2002.62 1379.49 1986.5 1373.28 L 1986.5 1320.7 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="2036.25" y="1350.75">
+ TOUCH_TOUCH</text>
+ </g>
+ <path d="M 2213 1276 L 2092.9 1324.61" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2088.04 1326.58 L 2093.21 1320.71 L 2092.9 1324.61 L 2095.84 1327.2 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2275.5 2325.7 C 2291.62 2331.91 2309.38 2331.91 2325.5 2325.7 C 2341.62 2319.5 2359.38 2319.5 2375.5 2325.7 L 2375.5 2378.28 C 2359.38 2372.08 2341.62 2372.08 2325.5 2378.28 C 2309.38 2384.49 2291.62 2384.49 2275.5 2378.28 L 2275.5 2325.7 Z" fill="#e1d5e7" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="2325.25" y="2348.75">
+ that finger</text>
+ <text x="2325.25" y="2362.75">
+ TOUCH_IDLE</text>
+ </g>
+ <path d="M 2086 2356 L 2269.63 2353.1" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2274.88 2353.02 L 2267.94 2356.63 L 2269.63 2353.1 L 2267.83 2349.63 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1958.5 1660.7 C 1974.62 1666.91 1992.38 1666.91 2008.5 1660.7 C 2024.62 1654.5 2042.38 1654.5 2058.5 1660.7 L 2058.5 1713.28 C 2042.38 1707.08 2024.62 1707.08 2008.5 1713.28 C 1992.38 1719.49 1974.62 1719.49 1958.5 1713.28 L 1958.5 1660.7 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="2008.25" y="1690.75">
+ TOUCH_DEAD</text>
+ </g>
+ <path d="M 2054 1582 L 2024.56 1649.17" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2022.45 1653.98 L 2022.05 1646.16 L 2024.56 1649.17 L 2028.46 1648.97 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1955 1592 L 1986.91 1649.43" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1989.46 1654.02 L 1983 1649.6 L 1986.91 1649.43 L 1989.12 1646.2 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1804 1592 L 1804 1643.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1804 1648.88 L 1800.5 1641.88 L 1804 1643.63 L 1807.5 1641.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1887.5 1957.7 C 1903.62 1963.91 1921.38 1963.91 1937.5 1957.7 C 1953.62 1951.5 1971.38 1951.5 1987.5 1957.7 L 1987.5 2010.28 C 1971.38 2004.08 1953.62 2004.08 1937.5 2010.28 C 1921.38 2016.49 1903.62 2016.49 1887.5 2010.28 L 1887.5 1957.7 Z" fill="#e1d5e7" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="1937.25" y="1980.75">
+ that finger</text>
+ <text x="1937.25" y="1994.75">
+ TOUCH_IDLE</text>
+ </g>
+ <path d="M 1786 1977 L 1881.64 1981.69" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1886.88 1981.95 L 1879.72 1985.1 L 1881.64 1981.69 L 1880.06 1978.11 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1827 1750 L 1919.29 1946.24" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1921.52 1950.99 L 1915.38 1946.14 L 1919.29 1946.24 L 1921.71 1943.16 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="11px">
+ <rect fill="#ffffff" stroke="none" x="1868" y="1845" width="15" height="14" stroke-width="0"/>
+ <text x="1874.5" y="1854.5">
+ no</text>
</g>
- <path d="M 1337 980 L 1335 851" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1335 846 L 1339 853 L 1335 851 L 1332 853 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1594 1091 C 1610 1097 1627 1097 1644 1091 C 1660 1085 1677 1085 1694 1091 L 1694 1144 C 1677 1138 1660 1138 1644 1144 C 1627 1150 1610 1150 1594 1144 L 1594 1091 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1252.5 1090.7 C 1268.62 1096.91 1286.38 1096.91 1302.5 1090.7 C 1318.62 1084.5 1336.38 1084.5 1352.5 1090.7 L 1352.5 1143.28 C 1336.38 1137.08 1318.62 1137.08 1302.5 1143.28 C 1286.38 1149.49 1268.62 1149.49 1252.5 1143.28 L 1252.5 1090.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1644" y="1122">TOUCH_IDLE</text>
+ <text x="1302.25" y="1120.75">
+ TOUCH_TOUCH</text>
</g>
- <path d="M 1686 1225 L 1659 1156" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1657 1151 L 1663 1156 L 1659 1156 L 1656 1159 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1569 1323 C 1585 1329 1602 1329 1619 1323 C 1635 1317 1652 1317 1669 1323 L 1669 1376 C 1652 1370 1635 1370 1619 1376 C 1602 1382 1585 1382 1569 1376 L 1569 1323 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1364 1067 L 1346.93 1080.97" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1342.87 1084.29 L 1346.07 1077.15 L 1346.93 1080.97 L 1350.5 1082.57 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 594 984.2 C 610.12 990.41 627.88 990.41 644 984.2 C 660.12 978 677.88 978 694 984.2 L 694 1036.78 C 677.88 1030.58 660.12 1030.58 644 1036.78 C 627.88 1042.99 610.12 1042.99 594 1036.78 L 594 984.2 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1619" y="1354">TOUCH_TOUCH</text>
+ <text x="643.75" y="1014.25">
+ TOUCH_IDLE</text>
</g>
- <path d="M 1795 1279 L 1674 1327" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1670 1329 L 1675 1323 L 1674 1327 L 1677 1330 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1858 2328 C 1874 2334 1891 2334 1908 2328 C 1924 2322 1941 2322 1958 2328 L 1958 2381 C 1941 2375 1924 2375 1908 2381 C 1891 2387 1874 2387 1858 2381 L 1858 2328 Z" fill="#e1d5e7" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1052.5 1373.7 C 1068.62 1379.91 1086.38 1379.91 1102.5 1373.7 C 1118.62 1367.5 1136.38 1367.5 1152.5 1373.7 L 1152.5 1426.28 C 1136.38 1420.08 1118.62 1420.08 1102.5 1426.28 C 1086.38 1432.49 1068.62 1432.49 1052.5 1426.28 L 1052.5 1373.7 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1908" y="2352">that finger</text>
- <text x="1908" y="2366">TOUCH_IDLE</text>
+ <text x="1102.25" y="1403.75">
+ TOUCH_TOUCH</text>
</g>
- <path d="M 1668 2359 L 1851 2356" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1856 2355 L 1849 2359 L 1851 2356 L 1849 2352 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1541 1663 C 1557 1669 1574 1669 1591 1663 C 1607 1657 1624 1657 1641 1663 L 1641 1716 C 1624 1710 1607 1710 1591 1716 C 1574 1722 1557 1722 1541 1716 L 1541 1663 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1124 1307 L 1111.42 1361.79" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1110.25 1366.91 L 1108.4 1359.3 L 1111.42 1361.79 L 1115.23 1360.87 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 860.5 1353.7 C 876.62 1359.91 894.38 1359.91 910.5 1353.7 C 926.62 1347.5 944.38 1347.5 960.5 1353.7 L 960.5 1406.28 C 944.38 1400.08 926.62 1400.08 910.5 1406.28 C 894.38 1412.49 876.62 1412.49 860.5 1406.28 L 860.5 1353.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1591" y="1694">TOUCH_DEAD</text>
+ <text x="910.25" y="1383.75">
+ TOUCH_DEAD</text>
</g>
- <path d="M 1636 1585 L 1607 1651" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1605 1656 L 1604 1648 L 1607 1651 L 1611 1651 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1537 1595 L 1569 1652" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1572 1656 L 1565 1652 L 1569 1652 L 1571 1648 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1386 1595 L 1386 1646" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1386 1651 L 1382 1644 L 1386 1646 L 1389 1644 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1470 1960 C 1486 1966 1503 1966 1520 1960 C 1536 1954 1553 1954 1570 1960 L 1570 2013 C 1553 2007 1536 2007 1520 2013 C 1503 2019 1486 2019 1470 2013 L 1470 1960 Z" fill="#e1d5e7" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 813 1312 L 858.8 1344.33" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 863.09 1347.36 L 855.35 1346.18 L 858.8 1344.33 L 859.39 1340.46 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 957 1307 L 934.41 1342.62" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 931.6 1347.06 L 932.39 1339.27 L 934.41 1342.62 L 938.3 1343.02 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 642.5 1725.7 C 658.62 1731.91 676.38 1731.91 692.5 1725.7 C 708.62 1719.5 726.38 1719.5 742.5 1725.7 L 742.5 1778.28 C 726.38 1772.08 708.62 1772.08 692.5 1778.28 C 676.38 1784.49 658.62 1784.49 642.5 1778.28 L 642.5 1725.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1520" y="1984">that finger</text>
- <text x="1520" y="1998">TOUCH_IDLE</text>
- </g>
- <path d="M 1368 1980 L 1463 1984" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1468 1984 L 1461 1987 L 1463 1984 L 1462 1980 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1409 1753 L 1501 1948" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1504 1953 L 1498 1948 L 1501 1948 L 1504 1945 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="11px">
- <rect fill="#ffffff" stroke="none" x="1450" y="1847" width="14" height="14" stroke-width="0"/>
- <text x="1457" y="1857">no</text>
+ <text x="692.25" y="1755.75">
+ TOUCH_IDLE</text>
</g>
- <path d="M 835 1093 C 851 1099 868 1099 885 1093 C 901 1087 918 1087 935 1093 L 935 1146 C 918 1140 901 1140 885 1146 C 868 1152 851 1152 835 1146 L 835 1093 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 729 1672 L 696.82 1714.91" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 693.67 1719.11 L 695.07 1711.41 L 696.82 1714.91 L 700.67 1715.61 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 521.5 1723.7 C 537.62 1729.91 555.38 1729.91 571.5 1723.7 C 587.62 1717.5 605.38 1717.5 621.5 1723.7 L 621.5 1776.28 C 605.38 1770.08 587.62 1770.08 571.5 1776.28 C 555.38 1782.49 537.62 1782.49 521.5 1776.28 L 521.5 1723.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="885" y="1124">TOUCH_TOUCH</text>
+ <text x="571.25" y="1753.75">
+ TOUCH_TOUCH</text>
</g>
- <path d="M 946 1070 L 929 1083" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 925 1086 L 928 1079 L 929 1083 L 933 1085 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 845 1364 C 861 1370 879 1370 895 1364 C 911 1358 929 1358 945 1364 L 945 1416 C 929 1410 911 1410 895 1416 C 879 1423 861 1423 845 1416 L 845 1364 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 918.5 1820.7 C 934.62 1826.91 952.38 1826.91 968.5 1820.7 C 984.62 1814.5 1002.38 1814.5 1018.5 1820.7 L 1018.5 1873.28 C 1002.38 1867.08 984.62 1867.08 968.5 1873.28 C 952.38 1879.49 934.62 1879.49 918.5 1873.28 L 918.5 1820.7 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="895" y="1394">TOUCH_IDLE</text>
+ <text x="968.25" y="1850.75">
+ TOUCH_TOUCH</text>
</g>
- <path d="M 880 1310 L 893 1351" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 895 1357 L 889 1351 L 893 1351 L 896 1349 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 635 1376 C 651 1382 668 1382 685 1376 C 701 1370 718 1370 735 1376 L 735 1429 C 718 1423 701 1423 685 1429 C 668 1435 651 1435 635 1429 L 635 1376 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 867 1791 L 913.39 1815.98" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 918.02 1818.47 L 910.19 1818.23 L 913.39 1815.98 L 913.51 1812.07 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 867.5 1615.7 C 883.62 1621.91 901.38 1621.91 917.5 1615.7 C 933.62 1609.5 951.38 1609.5 967.5 1615.7 L 967.5 1668.28 C 951.38 1662.08 933.62 1662.08 917.5 1668.28 C 901.38 1674.49 883.62 1674.49 867.5 1668.28 L 867.5 1615.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="685" y="1407">TOUCH_TOUCH</text>
+ <text x="917.25" y="1645.75">
+ TOUCH_IDLE</text>
</g>
- <path d="M 706 1310 L 693 1364" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 692 1369 L 690 1361 L 693 1364 L 697 1363 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 443 1356 C 459 1362 476 1362 493 1356 C 509 1350 526 1350 543 1356 L 543 1409 C 526 1403 509 1403 493 1409 C 476 1415 459 1415 443 1409 L 443 1356 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 927 1704 L 923.08 1681.28" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 922.19 1676.1 L 926.83 1682.41 L 923.08 1681.28 L 919.93 1683.59 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1032.5 1731.7 C 1048.62 1737.91 1066.38 1737.91 1082.5 1731.7 C 1098.62 1725.5 1116.38 1725.5 1132.5 1731.7 L 1132.5 1784.28 C 1116.38 1778.08 1098.62 1778.08 1082.5 1784.28 C 1066.38 1790.49 1048.62 1790.49 1032.5 1784.28 L 1032.5 1731.7 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="493" y="1387">TOUCH_DEAD</text>
+ <text x="1082.25" y="1761.75">
+ TOUCH_IDLE</text>
</g>
- <path d="M 415 1310 L 453 1346" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 457 1349 L 449 1347 L 453 1346 L 454 1342 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 539 1310 L 516 1345" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 514 1349 L 514 1341 L 516 1345 L 520 1345 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 225 1728 C 241 1734 258 1734 275 1728 C 291 1722 308 1722 325 1728 L 325 1781 C 308 1775 291 1775 275 1781 C 258 1787 241 1787 225 1781 L 225 1728 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1097 1700 L 1092.43 1719.8" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1091.25 1724.91 L 1089.42 1717.3 L 1092.43 1719.8 L 1096.24 1718.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1231.5 2011.7 C 1247.62 2017.91 1265.38 2017.91 1281.5 2011.7 C 1297.62 2005.5 1315.38 2005.5 1331.5 2011.7 L 1331.5 2064.28 C 1315.38 2058.08 1297.62 2058.08 1281.5 2064.28 C 1265.38 2070.49 1247.62 2070.49 1231.5 2064.28 L 1231.5 2011.7 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="275" y="1759">TOUCH_IDLE</text>
+ <text x="1281.25" y="2041.75">
+ TOUCH_TOUCH</text>
</g>
- <path d="M 311 1675 L 278 1717" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 275 1721 L 277 1714 L 278 1717 L 282 1718 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 195 1491 C 211 1497 228 1497 245 1491 C 261 1485 278 1485 295 1491 L 295 1544 C 278 1538 261 1538 245 1544 C 228 1550 211 1550 195 1544 L 195 1491 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1202 1672 L 1272.66 1999.77" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1273.76 2004.91 L 1268.87 1998.8 L 1272.66 1999.77 L 1275.71 1997.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2163.5 778.7 C 2179.62 784.91 2197.38 784.91 2213.5 778.7 C 2229.62 772.5 2247.38 772.5 2263.5 778.7 L 2263.5 831.28 C 2247.38 825.08 2229.62 825.08 2213.5 831.28 C 2197.38 837.49 2179.62 837.49 2163.5 831.28 L 2163.5 778.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="245" y="1522">TOUCH_TOUCH</text>
+ <text x="2213.25" y="808.75">
+ TOUCH_IDLE</text>
</g>
- <path d="M 157 1972 L 156 1972 Q 154 1972 154 1962 L 154 1528 Q 154 1518 164 1518 L 188 1518" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 193 1518 L 186 1521 L 188 1518 L 186 1514 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 295 1516 L 406 1512" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 411 1512 L 404 1516 L 406 1512 L 404 1509 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 501 1823 C 517 1829 534 1829 551 1823 C 567 1817 584 1817 601 1823 L 601 1876 C 584 1870 567 1870 551 1876 C 534 1882 517 1882 501 1876 L 501 1823 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2159 760 L 2169.19 768.83" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2173.16 772.27 L 2165.57 770.33 L 2169.19 768.83 L 2170.16 765.04 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1811.5 1190.7 C 1827.62 1196.91 1845.38 1196.91 1861.5 1190.7 C 1877.62 1184.5 1895.38 1184.5 1911.5 1190.7 L 1911.5 1243.28 C 1895.38 1237.08 1877.62 1237.08 1861.5 1243.28 C 1845.38 1249.49 1827.62 1249.49 1811.5 1243.28 L 1811.5 1190.7 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="551" y="1854">TOUCH_TOUCH</text>
+ <text x="1861.25" y="1220.75">
+ TOUCH_TOUCH</text>
</g>
- <path d="M 449 1793 L 495 1819" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 500 1821 L 492 1821 L 495 1819 L 495 1815 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 450 1618 C 466 1624 483 1624 500 1618 C 516 1612 533 1612 550 1618 L 550 1671 C 533 1665 516 1665 500 1671 C 483 1677 466 1677 450 1671 L 450 1618 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1884 1053 L 1866.86 1178.69" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1866.15 1183.89 L 1863.63 1176.48 L 1866.86 1178.69 L 1870.56 1177.43 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1437.5 2666.7 C 1453.62 2672.91 1471.38 2672.91 1487.5 2666.7 C 1503.62 2660.5 1521.38 2660.5 1537.5 2666.7 L 1537.5 2719.28 C 1521.38 2713.08 1503.62 2713.08 1487.5 2719.28 C 1471.38 2725.49 1453.62 2725.49 1437.5 2719.28 L 1437.5 2666.7 Z" fill="#e1d5e7" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="500" y="1649">TOUCH_IDLE</text>
+ <text x="1487.25" y="2689.75">
+ that finger</text>
+ <text x="1487.25" y="2703.75">
+ TOUCH_IDLE</text>
</g>
- <path d="M 509 1707 L 505 1683" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 505 1678 L 509 1685 L 505 1683 L 502 1686 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 615 1734 C 631 1740 648 1740 665 1734 C 681 1728 698 1728 715 1734 L 715 1787 C 698 1781 681 1781 665 1787 C 648 1793 631 1793 615 1787 L 615 1734 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1483 2632 L 1484.56 2654.65" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1484.92 2659.88 L 1480.95 2653.14 L 1484.56 2654.65 L 1487.93 2652.66 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2620 1851.2 C 2636.12 1857.41 2653.88 1857.41 2670 1851.2 C 2686.12 1845 2703.88 1845 2720 1851.2 L 2720 1903.78 C 2703.88 1897.58 2686.12 1897.58 2670 1903.78 C 2653.88 1909.99 2636.12 1909.99 2620 1903.78 L 2620 1851.2 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="665" y="1765">TOUCH_IDLE</text>
+ <text x="2669.75" y="1881.25">
+ TOUCH_DEAD</text>
</g>
- <path d="M 679 1703 L 674 1722" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 673 1727 L 671 1719 L 674 1722 L 678 1721 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 835 1821 C 851 1827 868 1827 885 1821 C 901 1815 918 1815 935 1821 L 935 1874 C 918 1868 901 1868 885 1874 C 868 1880 851 1880 835 1874 L 835 1821 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2672 887 L 2672 1482 Q 2672 1492 2672 1502 L 2672 1838.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2672 1843.88 L 2668.5 1836.88 L 2672 1838.63 L 2675.5 1836.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2619.5 1901.7 C 2635.62 1907.91 2653.38 1907.91 2669.5 1901.7 C 2685.62 1895.5 2703.38 1895.5 2719.5 1901.7 L 2719.5 1954.28 C 2703.38 1948.08 2685.62 1948.08 2669.5 1954.28 C 2653.38 1960.49 2635.62 1960.49 2619.5 1954.28 L 2619.5 1901.7 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="885" y="1852">TOUCH_TOUCH</text>
+ <text x="2669.25" y="1931.75">
+ TOUCH_DEAD</text>
</g>
- <path d="M 793 1675 L 864 1809" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 867 1814 L 860 1810 L 864 1809 L 867 1806 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1746 781 C 1762 787 1779 787 1796 781 C 1812 775 1829 775 1846 781 L 1846 834 C 1829 828 1812 828 1796 834 C 1779 840 1762 840 1746 834 L 1746 781 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2619.5 1951.7 C 2635.62 1957.91 2653.38 1957.91 2669.5 1951.7 C 2685.62 1945.5 2703.38 1945.5 2719.5 1951.7 L 2719.5 2004.28 C 2703.38 1998.08 2685.62 1998.08 2669.5 2004.28 C 2653.38 2010.49 2635.62 2010.49 2619.5 2004.28 L 2619.5 1951.7 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1796" y="812">TOUCH_IDLE</text>
+ <text x="2669.25" y="1981.75">
+ TOUCH_DEAD</text>
</g>
- <path d="M 1741 763 L 1751 771" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1755 774 L 1748 773 L 1751 771 L 1752 767 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1394 1193 C 1410 1199 1427 1199 1444 1193 C 1460 1187 1477 1187 1494 1193 L 1494 1246 C 1477 1240 1460 1240 1444 1246 C 1427 1252 1410 1252 1394 1246 L 1394 1193 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 703 2132.2 C 719.12 2138.41 736.88 2138.41 753 2132.2 C 769.12 2126 786.88 2126 803 2132.2 L 803 2184.78 C 786.88 2178.58 769.12 2178.58 753 2184.78 C 736.88 2190.99 719.12 2190.99 703 2184.78 L 703 2132.2 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1444" y="1224">TOUCH_TOUCH</text>
+ <text x="752.75" y="2162.25">
+ TOUCH_DEAD</text>
</g>
- <path d="M 1466 1056 L 1449 1181" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1448 1186 L 1446 1179 L 1449 1181 L 1453 1180 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1020 2669 C 1036 2675 1053 2675 1070 2669 C 1086 2663 1103 2663 1120 2669 L 1120 2722 C 1103 2716 1086 2716 1070 2722 C 1053 2728 1036 2728 1020 2722 L 1020 2669 Z" fill="#e1d5e7" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 320 2144 L 696.64 2156.78" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 701.88 2156.96 L 694.77 2160.22 L 696.64 2156.78 L 695.01 2153.23 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 845.5 2185.7 C 861.62 2191.91 879.38 2191.91 895.5 2185.7 C 911.62 2179.5 929.38 2179.5 945.5 2185.7 L 945.5 2238.28 C 929.38 2232.08 911.62 2232.08 895.5 2238.28 C 879.38 2244.49 861.62 2244.49 845.5 2238.28 L 845.5 2185.7 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1070" y="2693">that finger</text>
- <text x="1070" y="2707">TOUCH_IDLE</text>
+ <text x="895.25" y="2215.75">
+ TOUCH_DEAD</text>
</g>
- <path d="M 1065 2635 L 1067 2657" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1067 2662 L 1063 2655 L 1067 2657 L 1070 2655 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 2202 1854 C 2218 1860 2236 1860 2252 1854 C 2268 1848 2286 1848 2302 1854 L 2302 1906 C 2286 1900 2268 1900 2252 1906 C 2236 1913 2218 1913 2202 1906 L 2202 1854 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 803 2177 L 840.03 2190.78" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 844.95 2192.61 L 837.17 2193.45 L 840.03 2190.78 L 839.61 2186.89 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 970.5 2230.7 C 986.62 2236.91 1004.38 2236.91 1020.5 2230.7 C 1036.62 2224.5 1054.38 2224.5 1070.5 2230.7 L 1070.5 2283.28 C 1054.38 2277.08 1036.62 2277.08 1020.5 2283.28 C 1004.38 2289.49 986.62 2289.49 970.5 2283.28 L 970.5 2230.7 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="2252" y="1884">TOUCH_DEAD</text>
+ <text x="1020.25" y="2260.75">
+ TOUCH_DEAD</text>
</g>
- <path d="M 2254 890 L 2254 1485 Q 2254 1495 2254 1505 L 2254 1841" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 2254 1846 L 2251 1839 L 2254 1841 L 2258 1839 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 2202 1904 C 2218 1910 2235 1910 2252 1904 C 2268 1898 2285 1898 2302 1904 L 2302 1957 C 2285 1951 2268 1951 2252 1957 C 2235 1963 2218 1963 2202 1957 L 2202 1904 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 946 2230 L 965.01 2236.84" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 969.95 2238.62 L 962.18 2239.54 L 965.01 2236.84 L 964.55 2232.96 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1604.5 805 L 1668 855 L 1604.5 905 L 1541 855 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="2252" y="1935">TOUCH_DEAD</text>
+ <text x="1604.25" y="851.75">
+ state ==</text>
+ <text x="1604.25" y="865.75">
+ TOUCH_TOUCH</text>
</g>
- <path d="M 2202 1954 C 2218 1960 2235 1960 2252 1954 C 2268 1948 2285 1948 2302 1954 L 2302 2007 C 2285 2001 2268 2001 2252 2007 C 2235 2013 2218 2013 2202 2007 L 2202 1954 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1803.5 1650 L 1867 1700 L 1803.5 1750 L 1740 1700 Z" fill="#e1d5e7" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="2252" y="1985">TOUCH_DEAD</text>
+ <text x="1803.25" y="1696.75">
+ that finger state ==</text>
+ <text x="1803.25" y="1710.75">
+ TOUCH_TOUCH</text>
</g>
- <path d="M 285 2135 C 301 2141 319 2141 335 2135 C 351 2129 369 2129 385 2135 L 385 2187 C 369 2181 351 2181 335 2187 C 319 2194 301 2194 285 2187 L 285 2135 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="335" y="2165">TOUCH_DEAD</text>
+ <path d="M 1652 805 L 1717.65 734.66" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1721.24 730.82 L 1719.02 738.32 L 1717.65 734.66 L 1713.9 733.55 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="11px">
+ <rect fill="#ffffff" stroke="none" x="1680" y="762" width="15" height="14" stroke-width="0"/>
+ <text x="1687" y="771">
+ no</text>
</g>
- <path d="M 122 2074 L 279 2138" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 284 2140 L 276 2141 L 279 2138 L 279 2134 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 428 2188 C 444 2194 461 2194 478 2188 C 494 2182 511 2182 528 2188 L 528 2241 C 511 2235 494 2235 478 2241 C 461 2247 444 2247 428 2241 L 428 2188 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2213.5 1013.7 C 2229.62 1019.91 2247.38 1019.91 2263.5 1013.7 C 2279.62 1007.5 2297.38 1007.5 2313.5 1013.7 L 2313.5 1066.28 C 2297.38 1060.08 2279.62 1060.08 2263.5 1066.28 C 2247.38 1072.49 2229.62 1072.49 2213.5 1066.28 L 2213.5 1013.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="478" y="2219">TOUCH_DEAD</text>
+ <text x="2263.25" y="1043.75">
+ TOUCH_DEAD</text>
</g>
- <path d="M 385 2180 L 422 2194" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 426 2195 L 419 2196 L 422 2194 L 421 2190 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 553 2233 C 569 2239 586 2239 603 2233 C 619 2227 636 2227 653 2233 L 653 2286 C 636 2280 619 2280 603 2286 C 586 2292 569 2292 553 2286 L 553 2233 Z" fill="#ffb570" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2263 1008 L 2263 1008" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 2263 1008 L 2263 1008 L 2263 1008 L 2263 1008 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1958.5 1712.7 C 1974.62 1718.91 1992.38 1718.91 2008.5 1712.7 C 2024.62 1706.5 2042.38 1706.5 2058.5 1712.7 L 2058.5 1765.28 C 2042.38 1759.08 2024.62 1759.08 2008.5 1765.28 C 1992.38 1771.49 1974.62 1771.49 1958.5 1765.28 L 1958.5 1712.7 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="603" y="2264">TOUCH_DEAD</text>
+ <text x="2008.25" y="1742.75">
+ TOUCH_DEAD</text>
</g>
- <path d="M 528 2233 L 547 2239" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 551 2241 L 544 2242 L 547 2239 L 546 2236 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1187 808 L 1250 858 L 1187 908 L 1123 858 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1958.5 1764.7 C 1974.62 1770.91 1992.38 1770.91 2008.5 1764.7 C 2024.62 1758.5 2042.38 1758.5 2058.5 1764.7 L 2058.5 1817.28 C 2042.38 1811.08 2024.62 1811.08 2008.5 1817.28 C 1992.38 1823.49 1974.62 1823.49 1958.5 1817.28 L 1958.5 1764.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1187" y="855">state ==</text>
- <text x="1187" y="869">TOUCH_TOUCH</text>
+ <text x="2008.25" y="1794.75">
+ TOUCH_DEAD</text>
</g>
- <path d="M 1386 1653 L 1449 1703 L 1386 1753 L 1322 1703 Z" fill="#e1d5e7" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 224.54 902 C 227.34 898.86 231.32 897.05 235.52 897 L 269.49 897 C 273.69 897.05 277.67 898.86 280.47 902 L 300.45 925 C 301.01 926.28 301.01 927.72 300.45 929 L 280.47 952 C 277.67 955.14 273.69 956.95 269.49 957 L 235.52 957 C 231.32 956.95 227.34 955.14 224.54 952 L 204.56 929 C 204 927.72 204 926.28 204.56 925 L 224.54 902 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1386" y="1700">that finger state ==</text>
- <text x="1386" y="1714">TOUCH_TOUCH</text>
- </g>
- <path d="M 1233 808 L 1300 737" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1303 733 L 1301 740 L 1300 737 L 1296 736 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="11px">
- <rect fill="#ffffff" stroke="none" x="1262" y="764" width="14" height="14" stroke-width="0"/>
- <text x="1269" y="773">no</text>
+ <text x="252.25" y="923.75">
+ first</text>
+ <text x="252.25" y="937.75">
+ finger down</text>
</g>
- <path d="M 1796 1016 C 1812 1022 1829 1022 1846 1016 C 1862 1010 1879 1010 1896 1016 L 1896 1069 C 1879 1063 1862 1063 1846 1069 C 1829 1075 1812 1075 1796 1069 L 1796 1016 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <rect x="261.5" y="1062" width="209" height="40" rx="2.4" ry="2.4" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1846" y="1047">TOUCH_DEAD</text>
+ <text x="365.75" y="1085.75">
+ MULTITAP</text>
</g>
- <path d="M 1845 1010 L 1845 1010" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1845 1010 L 1845 1010 L 1845 1010 L 1844 1010 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
- <path d="M 1541 1715 C 1557 1721 1574 1721 1591 1715 C 1607 1709 1624 1709 1641 1715 L 1641 1768 C 1624 1762 1607 1762 1591 1768 C 1574 1774 1557 1774 1541 1768 L 1541 1715 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 455 1023 L 401.31 1058.49" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 396.93 1061.38 L 400.84 1054.6 L 401.31 1058.49 L 404.7 1060.44 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 351 1062 L 277.77 962.14" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 274.66 957.9 L 281.62 961.48 L 277.77 962.14 L 275.98 965.62 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 312.54 1182 C 315.34 1178.86 319.32 1177.05 323.52 1177 L 357.49 1177 C 361.69 1177.05 365.67 1178.86 368.47 1182 L 388.45 1205 C 389.01 1206.28 389.01 1207.72 388.45 1209 L 368.47 1232 C 365.67 1235.14 361.69 1236.95 357.49 1237 L 323.52 1237 C 319.32 1236.95 315.34 1235.14 312.54 1232 L 292.56 1209 C 292 1207.72 292 1206.28 292.56 1205 L 312.54 1182 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1591" y="1746">TOUCH_DEAD</text>
- </g>
- <path d="M 1541 1767 C 1557 1773 1574 1773 1591 1767 C 1607 1761 1624 1761 1641 1767 L 1641 1820 C 1624 1814 1607 1814 1591 1820 C 1574 1826 1557 1826 1541 1820 L 1541 1767 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
- <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
- <text x="1591" y="1798">TOUCH_DEAD</text>
+ <text x="340.25" y="1210.75">
+ timeout</text>
+ </g>
+ <path d="M 319 1237 L 276.68 1296.8" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 273.65 1301.09 L 274.83 1293.35 L 276.68 1296.8 L 280.55 1297.4 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 362 1102 L 348.25 1170.76" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 347.22 1175.9 L 345.16 1168.35 L 348.25 1170.76 L 352.02 1169.73 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <ellipse cx="253" cy="1602" rx="49.5" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="252.25" y="1605.75">
+ IDLE</text>
</g>
+ <path d="M 253 1502 L 253 1565.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 253 1570.88 L 249.5 1563.88 L 253 1565.63 L 256.5 1563.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 594 1004 L 556.33 999.72" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 551.11 999.13 L 558.46 996.44 L 556.33 999.72 L 557.67 1003.39 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 764 1034 L 700.24 1021.25" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 695.1 1020.22 L 702.65 1018.16 L 700.24 1021.25 L 701.27 1025.02 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <rect x="621.5" y="562" width="209" height="40" rx="2.4" ry="2.4" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="725.75" y="585.75">
+ MULTITAP_DOWN</text>
+ </g>
+ <path d="M 373.22 645.1 C 374.36 641.9 376.22 640.04 378.18 640.12 L 446.64 640.12 C 448.2 640.08 449.7 640.63 450.67 641.61 C 451.65 642.59 451.99 643.88 451.6 645.1 L 432.75 694.9 C 431.61 698.1 429.75 699.96 427.79 699.88 L 358.34 699.88 C 356.95 699.69 355.73 699.05 354.97 698.1 C 354.22 697.15 354 695.99 354.37 694.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="402.75" y="666.75">
+ button 1</text>
+ <text x="402.75" y="680.75">
+ press</text>
+ </g>
+ <path d="M 291 793 L 371.7 704.7" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 375.25 700.83 L 373.11 708.35 L 371.7 704.7 L 367.94 703.63 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 452 657 L 646.86 603.68" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 651.92 602.3 L 646.09 607.52 L 646.86 603.68 L 644.25 600.77 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 473.54 685 C 476.34 681.86 480.32 680.05 484.52 680 L 518.49 680 C 522.69 680.05 526.67 681.86 529.47 685 L 549.45 708 C 550.01 709.28 550.01 710.72 549.45 712 L 529.47 735 C 526.67 738.14 522.69 739.95 518.49 740 L 484.52 740 C 480.32 739.95 476.34 738.14 473.54 735 L 453.56 712 C 453 710.72 453 709.28 453.56 708 L 473.54 685 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="501.25" y="706.75">
+ first</text>
+ <text x="501.25" y="720.75">
+ finger up</text>
+ </g>
+ <path d="M 691 602 L 555.54 678.86" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 550.97 681.45 L 555.33 674.95 L 555.54 678.86 L 558.79 681.04 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 353.22 910.1 C 354.36 906.9 356.22 905.04 358.18 905.12 L 426.64 905.12 C 428.2 905.08 429.7 905.63 430.67 906.61 C 431.65 907.59 431.99 908.88 431.6 910.1 L 412.75 959.9 C 411.61 963.1 409.75 964.96 407.79 964.88 L 338.34 964.88 C 336.95 964.69 335.73 964.05 334.97 963.1 C 334.22 962.15 334 960.99 334.37 959.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="382.75" y="931.75">
+ button 1</text>
+ <text x="382.75" y="945.75">
+ release</text>
+ </g>
+ <path d="M 456 848 L 412.1 900.13" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 408.72 904.14 L 410.55 896.54 L 412.1 900.13 L 415.91 901.04 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 762.54 687 C 765.34 683.86 769.32 682.05 773.52 682 L 807.49 682 C 811.69 682.05 815.67 683.86 818.47 687 L 838.45 710 C 839.01 711.28 839.01 712.72 838.45 714 L 818.47 737 C 815.67 740.14 811.69 741.95 807.49 742 L 773.52 742 C 769.32 741.95 765.34 740.14 762.54 737 L 742.56 714 C 742 712.72 742 711.28 742.56 710 L 762.54 687 Z" fill="#99ffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="790.25" y="715.75">
+ timeout</text>
+ </g>
+ <path d="M 885.54 685 C 888.34 681.86 892.32 680.05 896.52 680 L 930.49 680 C 934.69 680.05 938.67 681.86 941.47 685 L 961.45 708 C 962.01 709.28 962.01 710.72 961.45 712 L 941.47 735 C 938.67 738.14 934.69 739.95 930.49 740 L 896.52 740 C 892.32 739.95 888.34 738.14 885.54 735 L 865.56 712 C 865 710.72 865 709.28 865.56 708 L 885.54 685 Z" fill="#7ea6e0" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="913.25" y="706.75">
+ second</text>
+ <text x="913.25" y="720.75">
+ finger down</text>
+ </g>
+ <path d="M 586.54 685 C 589.34 681.86 593.32 680.05 597.52 680 L 631.49 680 C 635.69 680.05 639.67 681.86 642.47 685 L 662.45 708 C 663.01 709.28 663.01 710.72 662.45 712 L 642.47 735 C 639.67 738.14 635.69 739.95 631.49 740 L 597.52 740 C 593.32 739.95 589.34 738.14 586.54 735 L 566.56 712 C 566 710.72 566 709.28 566.56 708 L 586.54 685 Z" fill="#67ab9f" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="614.25" y="706.75">
+ move &gt; </text>
+ <text x="614.25" y="720.75">
+ threshold</text>
+ </g>
+ <path d="M 709 602 L 645.18 675.2" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 641.73 679.16 L 643.7 671.58 L 645.18 675.2 L 648.97 676.18 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 736 602 L 773.15 676.3" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 775.5 681 L 769.24 676.3 L 773.15 676.3 L 775.5 673.17 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 755 602 L 864.73 676.43" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 869.07 679.37 L 861.32 678.34 L 864.73 676.43 L 865.25 672.55 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 651.22 800.1 C 652.36 796.9 654.22 795.04 656.18 795.12 L 724.64 795.12 C 726.2 795.08 727.7 795.63 728.67 796.61 C 729.65 797.59 729.99 798.88 729.6 800.1 L 710.75 849.9 C 709.61 853.1 707.75 854.96 705.79 854.88 L 636.34 854.88 C 634.95 854.69 633.73 854.05 632.97 853.1 C 632.22 852.15 632 850.99 632.37 849.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="680.75" y="821.75">
+ button 1</text>
+ <text x="680.75" y="835.75">
+ release</text>
+ </g>
+ <path d="M 667.22 912.1 C 668.36 908.9 670.22 907.04 672.18 907.12 L 740.64 907.12 C 742.2 907.08 743.7 907.63 744.67 908.61 C 745.65 909.59 745.99 910.88 745.6 912.1 L 726.75 961.9 C 725.61 965.1 723.75 966.96 721.79 966.88 L 652.34 966.88 C 650.95 966.69 649.73 966.05 648.97 965.1 C 648.22 964.15 648 962.99 648.37 961.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="696.75" y="933.75">
+ button 1</text>
+ <text x="696.75" y="947.75">
+ press</text>
+ </g>
+ <path d="M 632 740 L 660.8 789.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 663.44 794.03 L 656.89 789.74 L 660.8 789.5 L 662.94 786.22 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 685 855 L 692.03 900.71" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 692.83 905.9 L 688.31 899.51 L 692.03 900.71 L 695.22 898.44 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 711 967 L 892.23 1342.27" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 894.51 1346.99 L 888.32 1342.21 L 892.23 1342.27 L 894.62 1339.17 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 782.22 802.1 C 783.36 798.9 785.22 797.04 787.18 797.12 L 855.64 797.12 C 857.2 797.08 858.7 797.63 859.67 798.61 C 860.65 799.59 860.99 800.88 860.6 802.1 L 841.75 851.9 C 840.61 855.1 838.75 856.96 836.79 856.88 L 767.34 856.88 C 765.95 856.69 764.73 856.05 763.97 855.1 C 763.22 854.15 763 852.99 763.37 851.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="811.75" y="823.75">
+ button 1</text>
+ <text x="811.75" y="837.75">
+ release</text>
+ </g>
+ <path d="M 861.22 910.1 C 862.36 906.9 864.22 905.04 866.18 905.12 L 934.64 905.12 C 936.2 905.08 937.7 905.63 938.67 906.61 C 939.65 907.59 939.99 908.88 939.6 910.1 L 920.75 959.9 C 919.61 963.1 917.75 964.96 915.79 964.88 L 846.34 964.88 C 844.95 964.69 843.73 964.05 842.97 963.1 C 842.22 962.15 842 960.99 842.37 959.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="890.75" y="931.75">
+ button 1</text>
+ <text x="890.75" y="945.75">
+ press</text>
+ </g>
+ <path d="M 834 857 L 865.25 899.85" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 868.34 904.1 L 861.39 900.5 L 865.25 899.85 L 867.05 896.38 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 796 742 L 804.86 790.73" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 805.8 795.9 L 801.1 789.64 L 804.86 790.73 L 807.99 788.39 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 892 965 L 908.72 1341.64" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 908.95 1346.88 L 905.14 1340.05 L 908.72 1341.64 L 912.14 1339.73 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 903.22 800.1 C 904.36 796.9 906.22 795.04 908.18 795.12 L 976.64 795.12 C 978.2 795.08 979.7 795.63 980.67 796.61 C 981.65 797.59 981.99 798.88 981.6 800.1 L 962.75 849.9 C 961.61 853.1 959.75 854.96 957.79 854.88 L 888.34 854.88 C 886.95 854.69 885.73 854.05 884.97 853.1 C 884.22 852.15 884 850.99 884.37 849.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="932.75" y="821.75">
+ button 1</text>
+ <text x="932.75" y="835.75">
+ release</text>
+ </g>
+ <path d="M 965.22 912.1 C 966.36 908.9 968.22 907.04 970.18 907.12 L 1038.64 907.12 C 1040.2 907.08 1041.7 907.63 1042.67 908.61 C 1043.65 909.59 1043.99 910.88 1043.6 912.1 L 1024.75 961.9 C 1023.61 965.1 1021.75 966.96 1019.79 966.88 L 950.34 966.88 C 948.95 966.69 947.73 966.05 946.97 965.1 C 946.22 964.15 946 962.99 946.37 961.9 Z" fill="#ff6666" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="994.75" y="933.75">
+ button 1</text>
+ <text x="994.75" y="947.75">
+ press</text>
+ </g>
+ <path d="M 949 855 L 974.9 901.44" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 977.46 906.02 L 970.99 901.61 L 974.9 901.44 L 977.1 898.21 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 919 740 L 926.97 788.72" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 927.82 793.9 L 923.23 787.55 L 926.97 788.72 L 930.14 786.42 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1002 967 L 1093.56 1361.8" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 1094.75 1366.91 L 1089.76 1360.88 L 1093.56 1361.8 L 1096.58 1359.3 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 379 965 L 368.72 1055.67" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 368.13 1060.89 L 365.44 1053.54 L 368.72 1055.67 L 372.39 1054.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 211.5 798.7 C 227.62 804.91 245.38 804.91 261.5 798.7 C 277.62 792.5 295.38 792.5 311.5 798.7 L 311.5 851.28 C 295.38 845.08 277.62 845.08 261.5 851.28 C 245.38 857.49 227.62 857.49 211.5 851.28 L 211.5 798.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="261.25" y="828.75">
+ TOUCH_TOUCH</text>
+ </g>
+ <path d="M 255 897 L 258.35 864.33" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 258.89 859.11 L 261.65 866.43 L 258.35 864.33 L 254.69 865.72 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 432.5 788.7 C 448.62 794.91 466.38 794.91 482.5 788.7 C 498.62 782.5 516.38 782.5 532.5 788.7 L 532.5 841.28 C 516.38 835.08 498.62 835.08 482.5 841.28 C 466.38 847.49 448.62 847.49 432.5 841.28 L 432.5 788.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="482.25" y="818.75">
+ TOUCH_IDLE</text>
+ </g>
+ <path d="M 496 740 L 489.16 776.74" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 488.2 781.9 L 486.04 774.38 L 489.16 776.74 L 492.93 775.66 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 22.54 1307 C 25.34 1303.86 29.32 1302.05 33.52 1302 L 67.49 1302 C 71.69 1302.05 75.67 1303.86 78.47 1307 L 98.45 1330 C 99.01 1331.28 99.01 1332.72 98.45 1334 L 78.47 1357 C 75.67 1360.14 71.69 1361.95 67.49 1362 L 33.52 1362 C 29.32 1361.95 25.34 1360.14 22.54 1357 L 2.56 1334 C 2 1332.72 2 1331.28 2.56 1330 L 22.54 1307 Z" fill="#000000" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#FFFFFF" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="50.25" y="1321.75">
+ phys</text>
+ <text x="50.25" y="1335.75">
+ button</text>
+ <text x="50.25" y="1349.75">
+ press</text>
+ </g>
+ <path d="M 341 1102 L 93 1298.05" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 88.88 1301.31 L 92.2 1294.22 L 93 1298.05 L 96.54 1299.71 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 73 1362 L 226.19 1566.9" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 229.33 1571.1 L 222.34 1567.59 L 226.19 1566.9 L 227.94 1563.4 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 718 602 L 484.35 1191.08" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 482.41 1195.96 L 481.74 1188.16 L 484.35 1191.08 L 488.25 1190.74 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <rect x="439.5" y="1512" width="209" height="40" rx="2.4" ry="2.4" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="543.75" y="1535.75">
+ DRAGGING_OR_TAP</text>
+ </g>
+ <path d="M 424.04 1777 C 426.84 1773.86 430.82 1772.05 435.02 1772 L 468.99 1772 C 473.19 1772.05 477.17 1773.86 479.97 1777 L 499.95 1800 C 500.51 1801.28 500.51 1802.72 499.95 1804 L 479.97 1827 C 477.17 1830.14 473.19 1831.95 468.99 1832 L 435.02 1832 C 430.82 1831.95 426.84 1830.14 424.04 1827 L 404.06 1804 C 403.5 1802.72 403.5 1801.28 404.06 1800 L 424.04 1777 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="451.75" y="1798.75">
+ first</text>
+ <text x="451.75" y="1812.75">
+ finger up</text>
+ </g>
+ <path d="M 537 1552 L 464.05 1765.97" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 462.36 1770.94 L 461.31 1763.19 L 464.05 1765.97 L 467.93 1765.45 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 596.04 1417 C 598.84 1413.86 602.82 1412.05 607.02 1412 L 640.99 1412 C 645.19 1412.05 649.17 1413.86 651.97 1417 L 671.95 1440 C 672.51 1441.28 672.51 1442.72 671.95 1444 L 651.97 1467 C 649.17 1470.14 645.19 1471.95 640.99 1472 L 607.02 1472 C 602.82 1471.95 598.84 1470.14 596.04 1467 L 576.06 1444 C 575.5 1442.72 575.5 1441.28 576.06 1440 L 596.04 1417 Z" fill="#99ffff" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="623.75" y="1445.75">
+ timeout</text>
+ </g>
+ <path d="M 562 1512 L 592.81 1476.79" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 596.26 1472.84 L 594.29 1480.41 L 592.81 1476.79 L 589.02 1475.8 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 673 1454 L 823.8 1489.54" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 828.91 1490.74 L 821.3 1492.54 L 823.8 1489.54 L 822.9 1485.73 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 713.54 1517 C 716.34 1513.86 720.32 1512.05 724.52 1512 L 758.49 1512 C 762.69 1512.05 766.67 1513.86 769.47 1517 L 789.45 1540 C 790.01 1541.28 790.01 1542.72 789.45 1544 L 769.47 1567 C 766.67 1570.14 762.69 1571.95 758.49 1572 L 724.52 1572 C 720.32 1571.95 716.34 1570.14 713.54 1567 L 693.56 1544 C 693 1542.72 693 1541.28 693.56 1540 L 713.54 1517 Z" fill="#67ab9f" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="741.25" y="1538.75">
+ move &gt; </text>
+ <text x="741.25" y="1552.75">
+ threshold</text>
+ </g>
+ <path d="M 649 1538 L 686.64 1539.71" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 691.88 1539.95 L 684.73 1543.13 L 686.64 1539.71 L 685.05 1536.13 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 790 1531 L 823.79 1523.4" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 828.91 1522.25 L 822.85 1527.2 L 823.79 1523.4 L 821.31 1520.37 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 616 1939 L 580.47 1789.2" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 579.26 1784.09 L 584.28 1790.09 L 580.47 1789.2 L 577.47 1791.71 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 567 1718 L 547.76 1558.32" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 547.13 1553.11 L 551.45 1559.64 L 547.76 1558.32 L 544.5 1560.48 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 568 1552 L 778.14 1729.89" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 782.15 1733.28 L 774.54 1731.43 L 778.14 1729.89 L 779.07 1726.08 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 469.5 2037.7 C 485.62 2043.91 503.38 2043.91 519.5 2037.7 C 535.62 2031.5 553.38 2031.5 569.5 2037.7 L 569.5 2090.28 C 553.38 2084.08 535.62 2084.08 519.5 2090.28 C 503.38 2096.49 485.62 2096.49 469.5 2090.28 L 469.5 2037.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
+ <g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
+ <text x="519.25" y="2067.75">
+ TOUCH_IDLE</text>
+ </g>
+ <path d="M 460 1832 L 509.43 2025.83" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 510.72 2030.92 L 505.6 2025 L 509.43 2025.83 L 512.39 2023.27 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 570 2054 L 877.75 1994.21" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 882.9 1993.21 L 876.7 1997.98 L 877.75 1994.21 L 875.36 1991.11 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 539 1512 L 479.48 1263.19" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
+ <path d="M 478.26 1258.09 L 483.29 1264.08 L 479.48 1263.19 L 476.48 1265.71 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
</g>
</svg>
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 <stdint.h>
+
+#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;
+
+ /* 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 (delta.x == 0.0 && delta.y == 0.0)
+ 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);
@@ -127,6 +131,12 @@ tp_tap_set_timer(struct tp_dispatch *tp, uint64_t time)
}
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)
{
libinput_timer_cancel(&tp->tap.timer);
@@ -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_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
- tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
+ tp->tap.state = TAP_STATE_MULTITAP;
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:
@@ -419,6 +428,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,
enum tap_event event, uint64_t time)
@@ -444,6 +479,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,
enum tap_event event,
@@ -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;
-
- 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 (normalized_is_zero(*unaccelerated))
+ return *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;
-
- 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);
-
- return normalized;
+ return zero;
+
+ 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 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);
@@ -557,12 +614,23 @@ 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)
{
struct tp_touch *t;
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;
@@ -727,6 +802,12 @@ tp_destroy(struct evdev_dispatch *dispatch)
}
static void
+tp_release_fake_touches(struct tp_dispatch *tp)
+{
+ tp->fake_touches = 0;
+}
+
+static void
tp_clear_state(struct tp_dispatch *tp)
{
uint64_t now = libinput_now(tp->device->base.seat->libinput);
@@ -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);
}
@@ -772,6 +854,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)
{
if (tp->buttons.has_topbuttons) {
@@ -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
@@ -138,6 +138,21 @@ evdev_keyboard_notify_key(struct evdev_device *device,
}
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,
int button,
@@ -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),
@@ -745,6 +761,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)
{
free(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 <errno.h>
+#include <math.h>
#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 <ctype.h>
+#include <locale.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
@@ -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=<float>
+ *
+ * @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
@@ -2186,6 +2186,22 @@ 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. <i>KEY_ESC</i>
+ *
+ * @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
+ *
* Increase the refcount of the device group. A device group will be freed
* whenever the refcount reaches 0. This may happen during
* libinput_dispatch() if all devices of this group were removed from the
@@ -2916,6 +2932,130 @@ 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
*
* The scroll method of a device selects when to generate scroll axis events
* instead of pointer motion events.
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 <assert.h>
+
+#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 <config.h>
+
+#include <check.h>
+#include <signal.h>
+
+#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 <marcin.slusarz@gmail.com>
*
* 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 <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <fnmatch.h>
#include <getopt.h>
#include <poll.h>
#include <stdint.h>
@@ -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 <libunwind.h>
+#include <dlfcn.h>
+
+/* 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,93 +500,175 @@ litest_add_tcase_no_device(struct suite *suite, void *func)
suite_add_tcase(suite->suite, t->tc);
}
+static struct suite *
+get_suite(const char *name)
+{
+ struct suite *s;
+
+ if (all_tests.next == NULL && all_tests.prev == NULL)
+ list_init(&all_tests);
+
+ list_for_each(s, &all_tests, node) {
+ if (strcmp(s->name, name) == 0)
+ return s;
+ }
+
+ s = zalloc(sizeof(*s));
+ assert(s != NULL);
+ s->name = strdup(name);
+ s->suite = suite_create(s->name);
+
+ list_init(&s->tests);
+ list_insert(&all_tests, &s->node);
+
+ return s;
+}
+
static void
-litest_add_tcase(struct suite *suite, void *func,
+litest_add_tcase(const char *suite_name,
+ const char *funcname,
+ void *func,
enum litest_device_feature required,
- enum litest_device_feature excluded)
+ enum litest_device_feature excluded,
+ const struct range *range)
{
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);
+ litest_add_tcase_no_device(suite, func, range);
} 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++;
+ 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 {
- while (*dev) {
- litest_add_tcase_for_device(suite, func, *dev);
- dev++;
+ 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_no_device(const char *name, void *func)
+_litest_add_no_device(const char *name, const char *funcname, void *func)
{
- litest_add(name, func, LITEST_DISABLE_DEVICE, LITEST_DISABLE_DEVICE);
+ _litest_add(name, funcname, func, LITEST_DISABLE_DEVICE, LITEST_DISABLE_DEVICE);
}
-static struct suite *
-get_suite(const char *name)
+void
+_litest_add_ranged_no_device(const char *name,
+ const char *funcname,
+ void *func,
+ const struct range *range)
{
- struct suite *s;
-
- if (all_tests.next == NULL && all_tests.prev == NULL)
- list_init(&all_tests);
-
- list_for_each(s, &all_tests, node) {
- if (strcmp(s->name, name) == 0)
- return s;
- }
-
- s = zalloc(sizeof(*s));
- assert(s != NULL);
- s->name = strdup(name);
- s->suite = suite_create(s->name);
+ _litest_add_ranged(name,
+ funcname,
+ func,
+ LITEST_DISABLE_DEVICE,
+ LITEST_DISABLE_DEVICE,
+ range);
+}
- list_init(&s->tests);
- list_insert(&all_tests, &s->node);
+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);
+}
- return s;
+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(const char *name,
- void *func,
- enum litest_device_feature required,
- enum litest_device_feature excluded)
+_litest_add_for_device(const char *name,
+ const char *funcname,
+ void *func,
+ enum litest_device_type type)
{
- litest_add_tcase(get_suite(name), func, required, excluded);
+ _litest_add_ranged_for_device(name, funcname, func, type, NULL);
}
void
-litest_add_for_device(const char *name,
- void *func,
- enum litest_device_type type)
+_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,13 +1177,26 @@ 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)
{
struct input_event *ev;
@@ -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,13 +1245,25 @@ 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,
double x_from, double y_from,
@@ -1037,6 +1395,98 @@ litest_touch_move_two_touches(struct litest_device *d,
}
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);
- /* 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);
+ litest_assert_notnull(devnode);
fd = open(devnode, O_RDONLY);
- ck_assert_int_gt(fd, -1);
+ litest_assert_int_gt(fd, -1);
rc = libevdev_new_from_fd(fd, &dev);
- ck_assert_int_eq(rc, 0);
+ 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 */
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);
@@ -1536,6 +2081,12 @@ litest_timeout_tap(void)
}
void
+litest_timeout_tapndrag(void)
+{
+ msleep(520);
+}
+
+void
litest_timeout_softbuttons(void)
{
msleep(300);
@@ -1560,6 +2111,12 @@ litest_timeout_edgescroll(void)
}
void
+litest_timeout_middlebutton(void)
+{
+ msleep(70);
+}
+
+void
litest_push_event_frame(struct litest_device *dev)
{
assert(!dev->skip_ev_syn);
@@ -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 <libevdev/libevdev.h>
#include <libevdev/libevdev-uinput.h>
#include <libinput.h>
+#include <math.h>
+
+#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 <errno.h>
#include <fcntl.h>
#include <libinput.h>
-#include <libudev.h>
#include <unistd.h>
#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 <errno.h>
#include <fcntl.h>
#include <libinput.h>
-#include <libudev.h>
#include <unistd.h>
#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));
+
+ 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);
+ 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);
-
- 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_is_button_event(e, button, 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 <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <libudev.h>
+
+#include <libinput.h>
+#include <libinput-version.h>
+
+#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 <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <filter.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+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=<motion|accel|delta|sequence> \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=<double>\n ... in motion mode only. Stop increasing dx at maxdx\n"
+ "--steps=<double>\n ... in motion and delta modes only. Increase dx by step each round\n"
+ "--speed=<double>\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 <string.h>
#include <libudev.h>
+#include <libevdev/libevdev.h>
+
#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=<value>.... 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:<modalias>
+# libinput:name:<name>:dmi:<dmi string>
+
+#
+# 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:<modalias>
+IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=libinput:", \
+ GOTO="libinput_model_quirks_end"
+
+# libinput:name:<name>:dmi:<dmi string>
+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