diff options
author | Peter Hutterer <peter.hutterer@who-t.net> | 2015-05-22 14:21:21 +1000 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2015-05-22 14:21:21 +1000 |
commit | 087d25a54e5c507325a678f01fec4e5909d478e1 (patch) | |
tree | 4db61dd4d45005e48561891f667e519823650413 | |
parent | 29ba32b330e390db2b79bf6982d1376906392370 (diff) | |
parent | a6be43990cb31c004697bfa3c433d4c9bb9a5d3d (diff) |
Merge branch 'master' into tablet-support
69 files changed, 10178 insertions, 1472 deletions
@@ -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 > </text> - <text x="1413" y="446">threshold</text> + <text x="1830.25" y="428.75"> + move > </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 > </text> - <text x="1586" y="1001">threshold</text> + <text x="2004.25" y="983.75"> + move > </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 > </text> - <text x="1520" y="1576">threshold</text> + <text x="1937.75" y="1558.75"> + move > </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 > </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 > </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 > </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 > </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; @@ -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); @@ -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 |