summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2016-01-25 15:29:11 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2016-01-25 15:29:11 +1000
commitde3f1fa6fa23cebef17640dc1eb4ea8df270ecd9 (patch)
tree0eb7ae087e9dd10fc2b962a8879d6906773a674a
parent6920a42fd4ec8dfa633ef5bc1865f994afc63693 (diff)
parentb6f59d0e3b2e38abd0c79e36a5971dc7ea1e2b16 (diff)
Merge branch 'master' into tablet-supporttablet-support
-rw-r--r--.gitignore1
-rw-r--r--doc/gestures.dox38
-rw-r--r--doc/svg/gesture-2fg-ambiguity.svg496
-rw-r--r--doc/svg/pinch-gestures-softbuttons.svg365
-rw-r--r--src/evdev-mt-touchpad-buttons.c27
-rw-r--r--src/evdev-mt-touchpad-edge-scroll.c5
-rw-r--r--src/evdev-mt-touchpad-gestures.c394
-rw-r--r--src/evdev-mt-touchpad-tap.c3
-rw-r--r--src/evdev-mt-touchpad.c36
-rw-r--r--src/evdev-mt-touchpad.h33
-rw-r--r--src/libinput-private.h2
-rw-r--r--src/libinput.c8
-rw-r--r--test/gestures.c824
-rw-r--r--test/litest-selftest.c4
-rw-r--r--test/litest.c4
-rw-r--r--test/litest.h10
-rw-r--r--test/touchpad-tap.c106
-rw-r--r--test/touchpad.c10
18 files changed, 2057 insertions, 309 deletions
diff --git a/.gitignore b/.gitignore
index 8893a5fe..ba5d0f53 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@ Makefile
Makefile.in
aclocal.m4
autom4te.cache/
+compile
config.guess
config.h
config.h.in
diff --git a/doc/gestures.dox b/doc/gestures.dox
index 02ef09ab..325c795b 100644
--- a/doc/gestures.dox
+++ b/doc/gestures.dox
@@ -88,4 +88,42 @@ thus suggesting a window movement. libinput only has knowledge of the finger
coordinates (and even then only in device coordinates, not in screen
coordinates) and thus cannot differentiate the two.
+@section gestures_softbuttons Gestures with enabled software buttons
+
+If the touchpad device is a @ref touchpads_buttons_clickpads "Clickpad", it
+is recommended that a caller switches to @ref clickfinger.
+Usually fingers placed in a @ref software_buttons "software button area" is not
+considered for gestures, resulting in some gestures to be interpreted as
+pointer motion or two-finger scroll events.
+
+@image html pinch-gestures-softbuttons.svg "Interference of software buttons and pinch gestures"
+
+In the example above, the software button area is highlighted in red. The
+user executes a three-finger pinch gesture, with the thumb remaining in the
+software button area. libinput ignores fingers within the software button
+areas, the movement of the remaining fingers is thus interpreted as a
+two-finger scroll motion.
+
+@section gestures_twofinger_touchpads Gestures on two-finger touchpads
+
+As of kernel 4.2, many @ref touchpads_touch_partial_mt provide only two
+slots. This affects how gestures can be interpreted. Touchpads with only two
+slots can identify two touches by position but can usually tell that there
+is a third (or fourth) finger down on the touchpad - without providing
+positional information for that finger.
+
+Touchpoints are assigned in sequential order and only the first two touch
+points are trackable. For libinput this produces an ambiguity where it is
+impossible to detect whether a gesture is a pinch gesture or a swipe gesture
+whenever a user puts the index and middle finger down first. Since the third
+finger does not have positional information, it's location cannot be
+determined.
+
+@image html gesture-2fg-ambiguity.svg "Ambiguity of three-finger gestures on two-finger touchpads"
+
+The image above illustrates this ambiguity. The index and middle finger are
+set down first, the data stream from both finger positions looks identical.
+In this case, libinput assumes the fingers are in a horizontal arrangement
+(the right image above) and use a swipe gesture.
+
*/
diff --git a/doc/svg/gesture-2fg-ambiguity.svg b/doc/svg/gesture-2fg-ambiguity.svg
new file mode 100644
index 00000000..e4996ca9
--- /dev/null
+++ b/doc/svg/gesture-2fg-ambiguity.svg
@@ -0,0 +1,496 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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"
+ width="210.95885mm"
+ height="65.170769mm"
+ viewBox="0 0 747.49199 230.92005"
+ id="svg4313"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="gesture-2fg-ambiguity.svg">
+ <defs
+ id="defs4315">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4506"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4494"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mstart"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4491"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker7694"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path7696"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker7562"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path7564"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4995"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="marker7356"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="Arrow1Mstart">
+ <path
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ id="path7358"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Send"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Send"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4992"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ transform="matrix(-0.2,0,0,-0.2,-1.2,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Sstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Sstart"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path5007"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(0.3,0,0,0.3,-0.69,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="marker6499"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="Arrow2Lstart">
+ <path
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ id="path6501"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker5837"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path5839"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ transform="matrix(0.8,0,0,0.8,10,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="marker5365"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="Arrow2Lend">
+ <path
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ id="path5367"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker5291"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path5293"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4980"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4977"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ transform="matrix(0.8,0,0,0.8,10,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.8735525"
+ inkscape:cx="423.84385"
+ inkscape:cy="193.39486"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1920"
+ inkscape:window-height="1136"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" />
+ <metadata
+ id="metadata4318">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-1.110285,-124.21518)">
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.76355982;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect2858-0"
+ y="127.09696"
+ x="3.992065"
+ height="209.27208"
+ width="309.33386" />
+ <g
+ id="g4897"
+ transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,-136.33634,-258.03322)">
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="path4899"
+ d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="path4901"
+ d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
+ id="path4903"
+ d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
+ </g>
+ <g
+ transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,-167.25783,-222.59332)"
+ id="g5039">
+ <path
+ d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
+ id="path5041"
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
+ id="path5043"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ <path
+ d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
+ id="path5045"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g7665"
+ transform="matrix(0.98639446,-0.16439576,0.16439576,0.98639446,-43.838837,-4.728819)">
+ <path
+ sodipodi:nodetypes="sszzzcss"
+ d="m 136.26948,381.29633 c -24.01774,-7.29937 -29.0012,-10.10221 -30.51977,-10.54973 -10.672936,-3.14527 -18.270506,-5.54063 -23.777576,-13.4704 -5.50707,-7.92977 -5.34967,-20.78347 8.87612,-26.31603 14.225796,-5.53258 39.343506,8.79596 60.130606,16.16341 20.7871,7.36743 33.04562,11.44544 39.33421,13.8755 -8.10021,18.05041 -7.22128,21.15857 -10.11054,33.34117 -0.0481,0.20261 -17.87458,-5.12433 -43.93305,-13.04392 z"
+ id="path2824-1-1-3"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.00100005;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ d="m 107.01743,369.53232 c -10.672936,-1.94747 -17.884406,-5.64477 -21.626906,-8.75386 -8.11652,-9.03765 -6.31775,-15.03428 -3.3272,-13.99784 8.90495,-0.9097 30.203836,9.01528 33.860416,10.17935 -5.80268,11.37909 -1.08919,13.70271 -8.90631,12.57235 z"
+ id="path2824-7-1-4-3"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g9940"
+ transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,-167.25783,-222.59332)">
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="path9942"
+ d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="path9944"
+ d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
+ id="path9946"
+ d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
+ </g>
+ <rect
+ width="309.33386"
+ height="209.27208"
+ x="423.99207"
+ y="127.09696"
+ id="rect9948"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.76355982;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" />
+ <g
+ transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,283.66366,-258.03322)"
+ id="g9950">
+ <path
+ d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
+ id="path9952"
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
+ id="path9954"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ <path
+ d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
+ id="path9956"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g9958"
+ transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,252.74217,-222.59332)">
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="path9960"
+ d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="path9962"
+ d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
+ id="path9964"
+ d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
+ </g>
+ <g
+ transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,252.74217,-222.59332)"
+ id="g9972">
+ <path
+ d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
+ id="path9974"
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
+ id="path9976"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ <path
+ d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
+ id="path9978"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g9980"
+ transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,342.56693,-256.56067)">
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="path9982"
+ d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="path9984"
+ d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
+ id="path9986"
+ d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
+ </g>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 146.82277,201.01077 419.52386,-1.0675"
+ id="path9992"
+ inkscape:connector-curvature="0" />
+ <rect
+ y="188.0482"
+ x="330.96494"
+ height="22.417305"
+ width="77.393082"
+ id="rect10086"
+ style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.97799999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.78531075" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="336.83612"
+ y="205.01161"
+ id="text10074"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan10076"
+ x="336.83612"
+ y="205.01161">Touch 1</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ id="path10082"
+ d="m 180.82277,169.01077 419.52386,-1.0675"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <rect
+ style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.97799999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.78531075"
+ id="rect10084"
+ width="77.393082"
+ height="22.417305"
+ x="330.96494"
+ y="156.0482" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="335.76865"
+ y="173.66182"
+ id="text10078"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan10080"
+ x="335.76865"
+ y="173.66182">Touch 2</tspan></text>
+ </g>
+</svg>
diff --git a/doc/svg/pinch-gestures-softbuttons.svg b/doc/svg/pinch-gestures-softbuttons.svg
new file mode 100644
index 00000000..959cb4f4
--- /dev/null
+++ b/doc/svg/pinch-gestures-softbuttons.svg
@@ -0,0 +1,365 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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"
+ width="88.927498mm"
+ height="60.687836mm"
+ viewBox="0 0 315.09744 215.03564"
+ id="svg4313"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="pinch-gestures-softbuttons.svg">
+ <defs
+ id="defs4315">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4506"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4494"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mstart"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4491"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker7694"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path7696"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker7562"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path7564"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4995"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="marker7356"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="Arrow1Mstart">
+ <path
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ id="path7358"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Send"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Send"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4992"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ transform="matrix(-0.2,0,0,-0.2,-1.2,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Sstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Sstart"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path5007"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(0.3,0,0,0.3,-0.69,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="marker6499"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="Arrow2Lstart">
+ <path
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ id="path6501"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker5837"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path5839"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ transform="matrix(0.8,0,0,0.8,10,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="marker5365"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="Arrow2Lend">
+ <path
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ id="path5367"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker5291"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path5293"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4980"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path4977"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+ transform="matrix(0.8,0,0,0.8,10,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.6496034"
+ inkscape:cx="131.47222"
+ inkscape:cy="93.839318"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1920"
+ inkscape:window-height="1136"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" />
+ <metadata
+ id="metadata4318">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-1.110285,-124.21518)">
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.76355982;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect2858-0"
+ y="127.09696"
+ x="3.992065"
+ height="209.27208"
+ width="309.33386" />
+ <g
+ id="g4897"
+ transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,-136.33634,-278.03322)">
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="path4899"
+ d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="path4901"
+ d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
+ id="path4903"
+ d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
+ </g>
+ <rect
+ style="opacity:0.92000002;fill:#ff0000;fill-opacity:0.31073447;stroke:#000000;stroke-width:0.97799999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.78531075"
+ id="rect8337"
+ width="303.45676"
+ height="57.133244"
+ x="6.9702415"
+ y="276.61765" />
+ <g
+ transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,-167.25783,-242.59332)"
+ id="g5039">
+ <path
+ d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
+ id="path5041"
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
+ id="path5043"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ <path
+ d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
+ id="path5045"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g7665"
+ transform="matrix(0.98639446,-0.16439576,0.16439576,0.98639446,-83.838837,-24.728819)">
+ <path
+ sodipodi:nodetypes="sszzzcss"
+ d="m 136.26948,381.29633 c -24.01774,-7.29937 -29.0012,-10.10221 -30.51977,-10.54973 -10.672936,-3.14527 -18.270506,-5.54063 -23.777576,-13.4704 -5.50707,-7.92977 -5.34967,-20.78347 8.87612,-26.31603 14.225796,-5.53258 39.343506,8.79596 60.130606,16.16341 20.7871,7.36743 33.04562,11.44544 39.33421,13.8755 -8.10021,18.05041 -7.22128,21.15857 -10.11054,33.34117 -0.0481,0.20261 -17.87458,-5.12433 -43.93305,-13.04392 z"
+ id="path2824-1-1-3"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.00100005;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ d="m 107.01743,369.53232 c -10.672936,-1.94747 -17.884406,-5.64477 -21.626906,-8.75386 -8.11652,-9.03765 -6.31775,-15.03428 -3.3272,-13.99784 8.90495,-0.9097 30.203836,9.01528 33.860416,10.17935 -5.80268,11.37909 -1.08919,13.70271 -8.90631,12.57235 z"
+ id="path2824-7-1-4-3"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ </g>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend)"
+ d="m 126.40547,189.76337 -18.14734,34.1597"
+ id="path8341"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path8343"
+ d="m 94.40547,249.76337 -18.14734,34.1597"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Lstart)" />
+ </g>
+</svg>
diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c
index 8822e08c..82c99c76 100644
--- a/src/evdev-mt-touchpad-buttons.c
+++ b/src/evdev-mt-touchpad-buttons.c
@@ -78,47 +78,54 @@ button_event_to_str(enum button_event event) {
}
static inline bool
-is_inside_bottom_button_area(struct tp_dispatch *tp, struct tp_touch *t)
+is_inside_bottom_button_area(const struct tp_dispatch *tp,
+ const struct tp_touch *t)
{
return t->point.y >= tp->buttons.bottom_area.top_edge;
}
static inline bool
-is_inside_bottom_right_area(struct tp_dispatch *tp, struct tp_touch *t)
+is_inside_bottom_right_area(const struct tp_dispatch *tp,
+ const struct tp_touch *t)
{
return is_inside_bottom_button_area(tp, t) &&
t->point.x > tp->buttons.bottom_area.rightbutton_left_edge;
}
static inline bool
-is_inside_bottom_left_area(struct tp_dispatch *tp, struct tp_touch *t)
+is_inside_bottom_left_area(const struct tp_dispatch *tp,
+ const struct tp_touch *t)
{
return is_inside_bottom_button_area(tp, t) &&
!is_inside_bottom_right_area(tp, t);
}
static inline bool
-is_inside_top_button_area(struct tp_dispatch *tp, struct tp_touch *t)
+is_inside_top_button_area(const struct tp_dispatch *tp,
+ const struct tp_touch *t)
{
return t->point.y <= tp->buttons.top_area.bottom_edge;
}
static inline bool
-is_inside_top_right_area(struct tp_dispatch *tp, struct tp_touch *t)
+is_inside_top_right_area(const struct tp_dispatch *tp,
+ const struct tp_touch *t)
{
return is_inside_top_button_area(tp, t) &&
t->point.x > tp->buttons.top_area.rightbutton_left_edge;
}
static inline bool
-is_inside_top_left_area(struct tp_dispatch *tp, struct tp_touch *t)
+is_inside_top_left_area(const struct tp_dispatch *tp,
+ const struct tp_touch *t)
{
return is_inside_top_button_area(tp, t) &&
t->point.x < tp->buttons.top_area.leftbutton_right_edge;
}
static inline bool
-is_inside_top_middle_area(struct tp_dispatch *tp, struct tp_touch *t)
+is_inside_top_middle_area(const struct tp_dispatch *tp,
+ const struct tp_touch *t)
{
return is_inside_top_button_area(tp, t) &&
t->point.x >= tp->buttons.top_area.leftbutton_right_edge &&
@@ -1042,13 +1049,15 @@ tp_post_button_events(struct tp_dispatch *tp, uint64_t time)
}
int
-tp_button_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
+tp_button_touch_active(const struct tp_dispatch *tp,
+ const struct tp_touch *t)
{
return t->button.state == BUTTON_STATE_AREA;
}
bool
-tp_button_is_inside_softbutton_area(struct tp_dispatch *tp, struct tp_touch *t)
+tp_button_is_inside_softbutton_area(const struct tp_dispatch *tp,
+ const struct tp_touch *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 b572a9fb..fcc05121 100644
--- a/src/evdev-mt-touchpad-edge-scroll.c
+++ b/src/evdev-mt-touchpad-edge-scroll.c
@@ -73,7 +73,7 @@ edge_event_to_str(enum scroll_event event)
}
uint32_t
-tp_touch_get_edge(struct tp_dispatch *tp, struct tp_touch *t)
+tp_touch_get_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
{
uint32_t edge = EDGE_NONE;
@@ -455,7 +455,8 @@ tp_edge_scroll_stop_events(struct tp_dispatch *tp, uint64_t time)
}
int
-tp_edge_scroll_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
+tp_edge_scroll_touch_active(const struct tp_dispatch *tp,
+ const struct tp_touch *t)
{
return t->scroll.edge_state == EDGE_SCROLL_TOUCH_STATE_AREA;
}
diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c
index 80aa89ff..dc8d6060 100644
--- a/src/evdev-mt-touchpad-gestures.c
+++ b/src/evdev-mt-touchpad-gestures.c
@@ -33,13 +33,14 @@
#define DEFAULT_GESTURE_2FG_SCROLL_TIMEOUT ms2us(500)
static inline const char*
-gesture_state_to_str(enum tp_gesture_2fg_state state)
+gesture_state_to_str(enum tp_gesture_state state)
{
switch (state) {
- CASE_RETURN_STRING(GESTURE_2FG_STATE_NONE);
- CASE_RETURN_STRING(GESTURE_2FG_STATE_UNKNOWN);
- CASE_RETURN_STRING(GESTURE_2FG_STATE_SCROLL);
- CASE_RETURN_STRING(GESTURE_2FG_STATE_PINCH);
+ CASE_RETURN_STRING(GESTURE_STATE_NONE);
+ CASE_RETURN_STRING(GESTURE_STATE_UNKNOWN);
+ CASE_RETURN_STRING(GESTURE_STATE_SCROLL);
+ CASE_RETURN_STRING(GESTURE_STATE_PINCH);
+ CASE_RETURN_STRING(GESTURE_STATE_SWIPE);
}
return NULL;
}
@@ -48,15 +49,19 @@ static struct normalized_coords
tp_get_touches_delta(struct tp_dispatch *tp, bool average)
{
struct tp_touch *t;
- unsigned int i, nchanged = 0;
+ unsigned int i, nactive = 0;
struct normalized_coords normalized;
struct normalized_coords delta = {0.0, 0.0};
for (i = 0; i < tp->num_slots; i++) {
t = &tp->touches[i];
- if (tp_touch_active(tp, t) && t->dirty) {
- nchanged++;
+ if (!tp_touch_active(tp, t))
+ continue;
+
+ nactive++;
+
+ if (t->dirty) {
normalized = tp_get_delta(t);
delta.x += normalized.x;
@@ -64,11 +69,11 @@ tp_get_touches_delta(struct tp_dispatch *tp, bool average)
}
}
- if (!average || nchanged == 0)
+ if (!average || nactive == 0)
return delta;
- delta.x /= nchanged;
- delta.y /= nchanged;
+ delta.x /= nactive;
+ delta.y /= nactive;
return delta;
}
@@ -94,33 +99,30 @@ tp_gesture_start(struct tp_dispatch *tp, uint64_t time)
if (tp->gesture.started)
return;
- switch (tp->gesture.finger_count) {
- case 2:
- switch (tp->gesture.twofinger_state) {
- case GESTURE_2FG_STATE_NONE:
- case GESTURE_2FG_STATE_UNKNOWN:
- log_bug_libinput(libinput,
- "%s in unknown gesture mode\n",
- __func__);
- break;
- case GESTURE_2FG_STATE_SCROLL:
- /* NOP */
- break;
- case GESTURE_2FG_STATE_PINCH:
- gesture_notify_pinch(&tp->device->base, time,
- LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
- &zero, &zero, 1.0, 0.0);
- break;
- }
+ switch (tp->gesture.state) {
+ case GESTURE_STATE_NONE:
+ case GESTURE_STATE_UNKNOWN:
+ log_bug_libinput(libinput,
+ "%s in unknown gesture mode\n",
+ __func__);
break;
- case 3:
- case 4:
+ case GESTURE_STATE_SCROLL:
+ /* NOP */
+ break;
+ case GESTURE_STATE_PINCH:
+ gesture_notify_pinch(&tp->device->base, time,
+ LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
+ tp->gesture.finger_count,
+ &zero, &zero, 1.0, 0.0);
+ break;
+ case GESTURE_STATE_SWIPE:
gesture_notify_swipe(&tp->device->base, time,
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
tp->gesture.finger_count,
&zero, &zero);
break;
}
+
tp->gesture.started = true;
}
@@ -148,7 +150,7 @@ tp_gesture_post_pointer_motion(struct tp_dispatch *tp, uint64_t time)
}
static unsigned int
-tp_gesture_get_active_touches(struct tp_dispatch *tp,
+tp_gesture_get_active_touches(const struct tp_dispatch *tp,
struct tp_touch **touches,
unsigned int count)
{
@@ -184,18 +186,7 @@ tp_gesture_get_direction(struct tp_dispatch *tp, struct tp_touch *touch)
{
struct normalized_coords normalized;
struct device_float_coords delta;
- double move_threshold;
-
- /*
- * Semi-mt touchpads have somewhat inaccurate coordinates when
- * 2 fingers are down, so use a slightly larger threshold.
- * Elantech semi-mt touchpads are accurate enough though.
- */
- if (tp->semi_mt &&
- (tp->device->model_flags & EVDEV_MODEL_ELANTECH_TOUCHPAD) == 0)
- move_threshold = TP_MM_TO_DPI_NORMALIZED(4);
- else
- move_threshold = TP_MM_TO_DPI_NORMALIZED(1);
+ double move_threshold = TP_MM_TO_DPI_NORMALIZED(1);
delta = device_delta(touch->point, touch->gesture.initial);
@@ -221,11 +212,7 @@ tp_gesture_get_pinch_info(struct tp_dispatch *tp,
delta = device_delta(first->point, second->point);
normalized = tp_normalize_delta(tp, delta);
*distance = normalized_length(normalized);
-
- if (!tp->semi_mt)
- *angle = atan2(normalized.y, normalized.x) * 180.0 / M_PI;
- else
- *angle = 0.0;
+ *angle = atan2(normalized.y, normalized.x) * 180.0 / M_PI;
*center = device_average(first->point, second->point);
}
@@ -245,94 +232,156 @@ tp_gesture_set_scroll_buildup(struct tp_dispatch *tp)
tp->device->scroll.buildup = tp_normalize_delta(tp, average);
}
-static enum tp_gesture_2fg_state
-tp_gesture_twofinger_handle_state_none(struct tp_dispatch *tp, uint64_t time)
+static enum tp_gesture_state
+tp_gesture_handle_state_none(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *first, *second;
+ struct tp_touch *touches[4];
+ unsigned int ntouches;
+ unsigned int i;
+
+ ntouches = tp_gesture_get_active_touches(tp, touches, 4);
+ if (ntouches < 2)
+ return GESTURE_STATE_NONE;
+
+ if (!tp->gesture.enabled) {
+ if (ntouches == 2)
+ return GESTURE_STATE_SCROLL;
+ else
+ return GESTURE_STATE_SWIPE;
+ }
+
+ first = touches[0];
+ second = touches[1];
- if (tp_gesture_get_active_touches(tp, tp->gesture.touches, 2) != 2)
- return GESTURE_2FG_STATE_NONE;
+ /* For 3+ finger gestures we cheat. A human hand's finger
+ * arrangement means that for a 3 or 4 finger swipe gesture, the
+ * fingers are roughly arranged in a horizontal line.
+ * They will all move in the same direction, so we can simply look
+ * at the left and right-most ones only. If we have fake touches, we
+ * just take the left/right-most real touch position, since the fake
+ * touch has the same location as one of those.
+ *
+ * For a 3 or 4 finger pinch gesture, 2 or 3 fingers are roughly in
+ * a horizontal line, with the thumb below and left (right-handed
+ * users) or right (left-handed users). Again, the row of non-thumb
+ * fingers moves identically so we can look at the left and
+ * right-most only and then treat it like a two-finger
+ * gesture.
+ */
+ if (ntouches > 2) {
+ second = touches[0];
+
+ for (i = 1; i < ntouches && i < tp->num_slots; i++) {
+ if (touches[i]->point.x < first->point.x)
+ first = touches[i];
+ else if (touches[i]->point.x > second->point.x)
+ second = touches[i];
+ }
- first = tp->gesture.touches[0];
- second = tp->gesture.touches[1];
+ if (first == second)
+ return GESTURE_STATE_NONE;
+
+ }
tp->gesture.initial_time = time;
first->gesture.initial = first->point;
second->gesture.initial = second->point;
+ tp->gesture.touches[0] = first;
+ tp->gesture.touches[1] = second;
+
+ return GESTURE_STATE_UNKNOWN;
+}
+
+static inline int
+tp_gesture_same_directions(int dir1, int dir2)
+{
+ /*
+ * In some cases (semi-mt touchpads) we may seen one finger move
+ * e.g. N/NE and the other W/NW so we not only check for overlapping
+ * directions, but also for neighboring bits being set.
+ * The ((dira & 0x80) && (dirb & 0x01)) checks are to check for bit 0
+ * and 7 being set as they also represent neighboring directions.
+ */
+ return ((dir1 | (dir1 >> 1)) & dir2) ||
+ ((dir2 | (dir2 >> 1)) & dir1) ||
+ ((dir1 & 0x80) && (dir2 & 0x01)) ||
+ ((dir2 & 0x80) && (dir1 & 0x01));
+}
- return GESTURE_2FG_STATE_UNKNOWN;
+static inline void
+tp_gesture_init_pinch( struct tp_dispatch *tp)
+{
+ tp_gesture_get_pinch_info(tp,
+ &tp->gesture.initial_distance,
+ &tp->gesture.angle,
+ &tp->gesture.center);
+ tp->gesture.prev_scale = 1.0;
}
-static enum tp_gesture_2fg_state
-tp_gesture_twofinger_handle_state_unknown(struct tp_dispatch *tp, uint64_t time)
+static enum tp_gesture_state
+tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *first = tp->gesture.touches[0],
*second = tp->gesture.touches[1];
int dir1, dir2;
+ int yres = tp->device->abs.absinfo_y->resolution;
+ int vert_distance;
- /* if fingers stay unmoving for a while, assume (slow) scroll */
- if (time > (tp->gesture.initial_time + DEFAULT_GESTURE_2FG_SCROLL_TIMEOUT)) {
+ /* for two-finger gestures, if the fingers stay unmoving for a
+ * while, assume (slow) scroll */
+ if (tp->gesture.finger_count == 2 &&
+ time > (tp->gesture.initial_time + DEFAULT_GESTURE_2FG_SCROLL_TIMEOUT)) {
tp_gesture_set_scroll_buildup(tp);
- return GESTURE_2FG_STATE_SCROLL;
+ return GESTURE_STATE_SCROLL;
+ }
+
+ /* Else check if one finger is > 20mm below the others */
+ vert_distance = abs(first->point.y - second->point.y);
+ if (vert_distance > 20 * yres &&
+ tp->gesture.enabled) {
+ tp_gesture_init_pinch(tp);
+ return GESTURE_STATE_PINCH;
}
/* Else wait for both fingers to have moved */
dir1 = tp_gesture_get_direction(tp, first);
dir2 = tp_gesture_get_direction(tp, second);
if (dir1 == UNDEFINED_DIRECTION || dir2 == UNDEFINED_DIRECTION)
- return GESTURE_2FG_STATE_UNKNOWN;
-
- /*
- * If both touches are moving in the same direction assume scroll.
- *
- * In some cases (semi-mt touchpads) We may seen one finger move
- * e.g. N/NE and the other W/NW so we not only check for overlapping
- * directions, but also for neighboring bits being set.
- * The ((dira & 0x80) && (dirb & 0x01)) checks are to check for bit 0
- * and 7 being set as they also represent neighboring directions.
- */
- if (((dir1 | (dir1 >> 1)) & dir2) ||
- ((dir2 | (dir2 >> 1)) & dir1) ||
- ((dir1 & 0x80) && (dir2 & 0x01)) ||
- ((dir2 & 0x80) && (dir1 & 0x01))) {
- tp_gesture_set_scroll_buildup(tp);
- return GESTURE_2FG_STATE_SCROLL;
- } else if (tp->gesture.enabled) {
- tp_gesture_get_pinch_info(tp,
- &tp->gesture.initial_distance,
- &tp->gesture.angle,
- &tp->gesture.center);
- tp->gesture.prev_scale = 1.0;
- return GESTURE_2FG_STATE_PINCH;
+ return GESTURE_STATE_UNKNOWN;
+
+ /* If both touches are moving in the same direction assume
+ * scroll or swipe */
+ if (tp_gesture_same_directions(dir1, dir2)) {
+ if (tp->gesture.finger_count == 2) {
+ tp_gesture_set_scroll_buildup(tp);
+ return GESTURE_STATE_SCROLL;
+ } else if (tp->gesture.enabled) {
+ return GESTURE_STATE_SWIPE;
+ }
+ } else {
+ tp_gesture_init_pinch(tp);
+ return GESTURE_STATE_PINCH;
}
- return GESTURE_2FG_STATE_UNKNOWN;
+ return GESTURE_STATE_UNKNOWN;
}
-static enum tp_gesture_2fg_state
-tp_gesture_twofinger_handle_state_scroll(struct tp_dispatch *tp, uint64_t time)
+static enum tp_gesture_state
+tp_gesture_handle_state_scroll(struct tp_dispatch *tp, uint64_t time)
{
struct normalized_coords delta;
if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG)
- return GESTURE_2FG_STATE_SCROLL;
+ return GESTURE_STATE_SCROLL;
- /* 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 GESTURE_2FG_STATE_SCROLL;
-
- delta = tp_get_delta(&tp->touches[0]);
- } else {
- delta = tp_get_average_touches_delta(tp);
- }
+ delta = tp_get_average_touches_delta(tp);
/* scroll is not accelerated */
delta = tp_filter_motion_unaccelerated(tp, &delta, time);
if (normalized_is_zero(delta))
- return GESTURE_2FG_STATE_SCROLL;
+ return GESTURE_STATE_SCROLL;
tp_gesture_start(tp, time);
evdev_post_scroll(tp->device,
@@ -340,11 +389,30 @@ tp_gesture_twofinger_handle_state_scroll(struct tp_dispatch *tp, uint64_t time)
LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
&delta);
- return GESTURE_2FG_STATE_SCROLL;
+ return GESTURE_STATE_SCROLL;
+}
+
+static enum tp_gesture_state
+tp_gesture_handle_state_swipe(struct tp_dispatch *tp, uint64_t time)
+{
+ struct normalized_coords delta, unaccel;
+
+ unaccel = tp_get_average_touches_delta(tp);
+ delta = tp_filter_motion(tp, &unaccel, time);
+
+ if (!normalized_is_zero(delta) || !normalized_is_zero(unaccel)) {
+ tp_gesture_start(tp, time);
+ gesture_notify_swipe(&tp->device->base, time,
+ LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
+ tp->gesture.finger_count,
+ &delta, &unaccel);
+ }
+
+ return GESTURE_STATE_SWIPE;
}
-static enum tp_gesture_2fg_state
-tp_gesture_twofinger_handle_state_pinch(struct tp_dispatch *tp, uint64_t time)
+static enum tp_gesture_state
+tp_gesture_handle_state_pinch(struct tp_dispatch *tp, uint64_t time)
{
double angle, angle_delta, distance, scale;
struct device_float_coords center, fdelta;
@@ -368,60 +436,48 @@ tp_gesture_twofinger_handle_state_pinch(struct tp_dispatch *tp, uint64_t time)
if (normalized_is_zero(delta) && normalized_is_zero(unaccel) &&
scale == tp->gesture.prev_scale && angle_delta == 0.0)
- return GESTURE_2FG_STATE_PINCH;
+ return GESTURE_STATE_PINCH;
tp_gesture_start(tp, time);
gesture_notify_pinch(&tp->device->base, time,
LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
+ tp->gesture.finger_count,
&delta, &unaccel, scale, angle_delta);
tp->gesture.prev_scale = scale;
- return GESTURE_2FG_STATE_PINCH;
+ return GESTURE_STATE_PINCH;
}
static void
-tp_gesture_post_twofinger(struct tp_dispatch *tp, uint64_t time)
+tp_gesture_post_gesture(struct tp_dispatch *tp, uint64_t time)
{
- enum tp_gesture_2fg_state oldstate = tp->gesture.twofinger_state;
+ enum tp_gesture_state oldstate = tp->gesture.state;
+
+ if (tp->gesture.state == GESTURE_STATE_NONE)
+ tp->gesture.state =
+ tp_gesture_handle_state_none(tp, time);
- if (tp->gesture.twofinger_state == GESTURE_2FG_STATE_NONE)
- tp->gesture.twofinger_state =
- tp_gesture_twofinger_handle_state_none(tp, time);
+ if (tp->gesture.state == GESTURE_STATE_UNKNOWN)
+ tp->gesture.state =
+ tp_gesture_handle_state_unknown(tp, time);
- if (tp->gesture.twofinger_state == GESTURE_2FG_STATE_UNKNOWN)
- tp->gesture.twofinger_state =
- tp_gesture_twofinger_handle_state_unknown(tp, time);
+ if (tp->gesture.state == GESTURE_STATE_SCROLL)
+ tp->gesture.state =
+ tp_gesture_handle_state_scroll(tp, time);
- if (tp->gesture.twofinger_state == GESTURE_2FG_STATE_SCROLL)
- tp->gesture.twofinger_state =
- tp_gesture_twofinger_handle_state_scroll(tp, time);
+ if (tp->gesture.state == GESTURE_STATE_SWIPE)
+ tp->gesture.state =
+ tp_gesture_handle_state_swipe(tp, time);
- if (tp->gesture.twofinger_state == GESTURE_2FG_STATE_PINCH)
- tp->gesture.twofinger_state =
- tp_gesture_twofinger_handle_state_pinch(tp, time);
+ if (tp->gesture.state == GESTURE_STATE_PINCH)
+ tp->gesture.state =
+ tp_gesture_handle_state_pinch(tp, time);
log_debug(tp_libinput_context(tp),
"gesture state: %s → %s\n",
gesture_state_to_str(oldstate),
- gesture_state_to_str(tp->gesture.twofinger_state));
-}
-
-static void
-tp_gesture_post_swipe(struct tp_dispatch *tp, uint64_t time)
-{
- struct normalized_coords delta, unaccel;
-
- unaccel = tp_get_average_touches_delta(tp);
- delta = tp_filter_motion(tp, &unaccel, time);
-
- if (!normalized_is_zero(delta) || !normalized_is_zero(unaccel)) {
- tp_gesture_start(tp, time);
- gesture_notify_swipe(&tp->device->base, time,
- LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
- tp->gesture.finger_count,
- &delta, &unaccel);
- }
+ gesture_state_to_str(tp->gesture.state));
}
void
@@ -446,11 +502,9 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time)
tp_gesture_post_pointer_motion(tp, time);
break;
case 2:
- tp_gesture_post_twofinger(tp, time);
- break;
case 3:
case 4:
- tp_gesture_post_swipe(tp, time);
+ tp_gesture_post_gesture(tp, time);
break;
}
}
@@ -470,38 +524,37 @@ static void
tp_gesture_end(struct tp_dispatch *tp, uint64_t time, bool cancelled)
{
struct libinput *libinput = tp->device->base.seat->libinput;
- enum tp_gesture_2fg_state twofinger_state = tp->gesture.twofinger_state;
+ enum tp_gesture_state state = tp->gesture.state;
- tp->gesture.twofinger_state = GESTURE_2FG_STATE_NONE;
+ tp->gesture.state = GESTURE_STATE_NONE;
if (!tp->gesture.started)
return;
- switch (tp->gesture.finger_count) {
- case 2:
- switch (twofinger_state) {
- case GESTURE_2FG_STATE_NONE:
- case GESTURE_2FG_STATE_UNKNOWN:
- log_bug_libinput(libinput,
- "%s in unknown gesture mode\n",
- __func__);
- break;
- case GESTURE_2FG_STATE_SCROLL:
- tp_gesture_stop_twofinger_scroll(tp, time);
- break;
- case GESTURE_2FG_STATE_PINCH:
- gesture_notify_pinch_end(&tp->device->base, time,
- tp->gesture.prev_scale,
- cancelled);
- break;
- }
+ switch (state) {
+ case GESTURE_STATE_NONE:
+ case GESTURE_STATE_UNKNOWN:
+ log_bug_libinput(libinput,
+ "%s in unknown gesture mode\n",
+ __func__);
break;
- case 3:
- case 4:
- gesture_notify_swipe_end(&tp->device->base, time,
- tp->gesture.finger_count, cancelled);
+ case GESTURE_STATE_SCROLL:
+ tp_gesture_stop_twofinger_scroll(tp, time);
+ break;
+ case GESTURE_STATE_PINCH:
+ gesture_notify_pinch_end(&tp->device->base, time,
+ tp->gesture.finger_count,
+ tp->gesture.prev_scale,
+ cancelled);
+ break;
+ case GESTURE_STATE_SWIPE:
+ gesture_notify_swipe_end(&tp->device->base,
+ time,
+ tp->gesture.finger_count,
+ cancelled);
break;
}
+
tp->gesture.started = false;
}
@@ -535,13 +588,10 @@ tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time)
{
unsigned int active_touches = 0;
struct tp_touch *t;
- int i = 0;
tp_for_each_touch(tp, t) {
if (tp_touch_active(tp, t))
active_touches++;
-
- i++;
}
if (active_touches != tp->gesture.finger_count) {
@@ -568,12 +618,12 @@ tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time)
int
tp_init_gesture(struct tp_dispatch *tp)
{
- if (tp->device->model_flags & EVDEV_MODEL_JUMPING_SEMI_MT)
- tp->gesture.enabled = false;
- else
- tp->gesture.enabled = true;
+ /* two-finger scrolling is always enabled, this flag just
+ * decides whether we detect pinch. semi-mt devices are too
+ * unreliable to do pinch gestures. */
+ tp->gesture.enabled = !tp->semi_mt && tp->num_slots > 1;
- tp->gesture.twofinger_state = GESTURE_2FG_STATE_NONE;
+ tp->gesture.state = GESTURE_STATE_NONE;
libinput_timer_init(&tp->gesture.finger_count_switch_timer,
tp->device->base.seat->libinput,
diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c
index dda528a8..5556ee94 100644
--- a/src/evdev-mt-touchpad-tap.c
+++ b/src/evdev-mt-touchpad-tap.c
@@ -561,7 +561,6 @@ tp_tap_multitap_handle_event(struct tp_dispatch *tp,
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;
@@ -1022,7 +1021,7 @@ tp_tap_resume(struct tp_dispatch *tp, uint64_t time)
}
bool
-tp_tap_dragging(struct tp_dispatch *tp)
+tp_tap_dragging(const struct tp_dispatch *tp)
{
switch (tp->tap.state) {
case TAP_STATE_DRAGGING:
diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index 11cd1c30..f2491164 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -140,6 +140,14 @@ tp_get_touch(struct tp_dispatch *tp, unsigned int slot)
static inline unsigned int
tp_fake_finger_count(struct tp_dispatch *tp)
{
+ /* Only one of BTN_TOOL_DOUBLETAP/TRIPLETAP/... may be set at any
+ * time */
+ if (__builtin_popcount(
+ tp->fake_touches & ~(FAKE_FINGER_OVERFLOW|0x1)) > 1)
+ log_bug_kernel(tp->device->base.seat->libinput,
+ "Invalid fake finger state %#x\n",
+ tp->fake_touches);
+
if (tp->fake_touches & FAKE_FINGER_OVERFLOW)
return FAKE_FINGER_OVERFLOW;
else /* don't count BTN_TOUCH */
@@ -510,7 +518,7 @@ tp_pin_fingers(struct tp_dispatch *tp)
}
int
-tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
+tp_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t)
{
return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
t->palm.state == PALM_NONE &&
@@ -521,7 +529,7 @@ tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
}
bool
-tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t)
+tp_palm_tap_is_palm(const struct tp_dispatch *tp, const struct tp_touch *t)
{
if (t->state != TOUCH_BEGIN)
return false;
@@ -743,6 +751,9 @@ tp_unhover_abs_distance(struct tp_dispatch *tp, uint64_t time)
for (i = 0; i < tp->ntouches; i++) {
t = tp_get_touch(tp, i);
+ if (!t->dirty)
+ continue;
+
if (t->state == TOUCH_HOVERING) {
if (t->distance == 0) {
/* avoid jumps when landing a finger */
@@ -1488,17 +1499,22 @@ tp_init_slots(struct tp_dispatch *tp,
tp->semi_mt = libevdev_has_property(device->evdev, INPUT_PROP_SEMI_MT);
- /* This device has a terrible resolution when two fingers are down,
+ /* Semi-mt devices are not reliable for true multitouch data, so we
+ * simply pretend they're single touch touchpads with BTN_TOOL bits.
+ * Synaptics:
+ * Terrible resolution when two fingers are down,
* causing scroll jumps. The single-touch emulation ABS_X/Y is
* accurate but the ABS_MT_POSITION touchpoints report the bounding
- * box and that causes jumps. So we simply pretend it's a single
- * touch touchpad with the BTN_TOOL bits.
- * See https://bugzilla.redhat.com/show_bug.cgi?id=1235175 for an
- * explanation.
+ * box and that causes jumps. See https://bugzilla.redhat.com/1235175
+ * Elantech:
+ * On three-finger taps/clicks, one slot doesn't get a coordinate
+ * assigned. See https://bugs.freedesktop.org/show_bug.cgi?id=93583
+ * Alps:
+ * If three fingers are set down in the same frame, one slot has the
+ * coordinates 0/0 and may not get updated for several frames.
+ * See https://bugzilla.redhat.com/show_bug.cgi?id=1295073
*/
- if (tp->semi_mt &&
- (device->model_flags &
- (EVDEV_MODEL_JUMPING_SEMI_MT|EVDEV_MODEL_ELANTECH_TOUCHPAD))) {
+ if (tp->semi_mt) {
tp->num_slots = 1;
tp->slot = 0;
tp->has_mt = false;
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index eb6702d3..8564a103 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -130,11 +130,12 @@ enum tp_edge_scroll_touch_state {
EDGE_SCROLL_TOUCH_STATE_AREA,
};
-enum tp_gesture_2fg_state {
- GESTURE_2FG_STATE_NONE,
- GESTURE_2FG_STATE_UNKNOWN,
- GESTURE_2FG_STATE_SCROLL,
- GESTURE_2FG_STATE_PINCH,
+enum tp_gesture_state {
+ GESTURE_STATE_NONE,
+ GESTURE_STATE_UNKNOWN,
+ GESTURE_STATE_SCROLL,
+ GESTURE_STATE_PINCH,
+ GESTURE_STATE_SWIPE,
};
enum tp_thumb_state {
@@ -251,7 +252,7 @@ struct tp_dispatch {
unsigned int finger_count;
unsigned int finger_count_pending;
struct libinput_timer finger_count_switch_timer;
- enum tp_gesture_2fg_state twofinger_state;
+ enum tp_gesture_state state;
struct tp_touch *touches[2];
uint64_t initial_time;
double initial_distance;
@@ -311,7 +312,6 @@ struct tp_dispatch {
struct libinput_timer timer;
enum tp_tap_state state;
uint32_t buttons_pressed;
- uint64_t multitap_last_time;
bool drag_lock_enabled;
} tap;
@@ -357,7 +357,7 @@ struct tp_dispatch {
for (unsigned int _i = 0; _i < (_tp)->ntouches && (_t = &(_tp)->touches[_i]); _i++)
static inline struct libinput*
-tp_libinput_context(struct tp_dispatch *tp)
+tp_libinput_context(const struct tp_dispatch *tp)
{
return tp->device->base.seat->libinput;
}
@@ -402,7 +402,7 @@ tp_filter_motion_unaccelerated(struct tp_dispatch *tp,
uint64_t time);
int
-tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t);
+tp_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t);
int
tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time);
@@ -440,10 +440,12 @@ int
tp_button_handle_state(struct tp_dispatch *tp, uint64_t time);
int
-tp_button_touch_active(struct tp_dispatch *tp, struct tp_touch *t);
+tp_button_touch_active(const struct tp_dispatch *tp,
+ const struct tp_touch *t);
bool
-tp_button_is_inside_softbutton_area(struct tp_dispatch *tp, struct tp_touch *t);
+tp_button_is_inside_softbutton_area(const struct tp_dispatch *tp,
+ const struct tp_touch *t);
void
tp_release_all_taps(struct tp_dispatch *tp,
@@ -456,7 +458,7 @@ void
tp_tap_resume(struct tp_dispatch *tp, uint64_t time);
bool
-tp_tap_dragging(struct tp_dispatch *tp);
+tp_tap_dragging(const struct tp_dispatch *tp);
int
tp_edge_scroll_init(struct tp_dispatch *tp, struct evdev_device *device);
@@ -474,10 +476,11 @@ void
tp_edge_scroll_stop_events(struct tp_dispatch *tp, uint64_t time);
int
-tp_edge_scroll_touch_active(struct tp_dispatch *tp, struct tp_touch *t);
+tp_edge_scroll_touch_active(const struct tp_dispatch *tp,
+ const struct tp_touch *t);
uint32_t
-tp_touch_get_edge(struct tp_dispatch *tp, struct tp_touch *t);
+tp_touch_get_edge(const struct tp_dispatch *tp, const struct tp_touch *t);
int
tp_init_gesture(struct tp_dispatch *tp);
@@ -501,6 +504,6 @@ 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);
+tp_palm_tap_is_palm(const struct tp_dispatch *tp, const struct tp_touch *t);
#endif
diff --git a/src/libinput-private.h b/src/libinput-private.h
index d78be646..3dff3a84 100644
--- a/src/libinput-private.h
+++ b/src/libinput-private.h
@@ -484,6 +484,7 @@ void
gesture_notify_pinch(struct libinput_device *device,
uint64_t time,
enum libinput_event_type type,
+ int finger_count,
const struct normalized_coords *delta,
const struct normalized_coords *unaccel,
double scale,
@@ -492,6 +493,7 @@ gesture_notify_pinch(struct libinput_device *device,
void
gesture_notify_pinch_end(struct libinput_device *device,
uint64_t time,
+ int finger_count,
double scale,
int cancelled);
diff --git a/src/libinput.c b/src/libinput.c
index a0dc6d68..aaeff9a3 100644
--- a/src/libinput.c
+++ b/src/libinput.c
@@ -2402,25 +2402,27 @@ void
gesture_notify_pinch(struct libinput_device *device,
uint64_t time,
enum libinput_event_type type,
+ int finger_count,
const struct normalized_coords *delta,
const struct normalized_coords *unaccel,
double scale,
double angle)
{
- gesture_notify(device, time, type, 2, 0, delta, unaccel,
- scale, angle);
+ gesture_notify(device, time, type, finger_count, 0,
+ delta, unaccel, scale, angle);
}
void
gesture_notify_pinch_end(struct libinput_device *device,
uint64_t time,
+ int finger_count,
double scale,
int cancelled)
{
const struct normalized_coords zero = { 0.0, 0.0 };
gesture_notify(device, time, LIBINPUT_EVENT_GESTURE_PINCH_END,
- 2, cancelled, &zero, &zero, scale, 0.0);
+ finger_count, cancelled, &zero, &zero, scale, 0.0);
}
static inline const char *
diff --git a/test/gestures.c b/test/gestures.c
index 9fc73b97..3f7ee832 100644
--- a/test/gestures.c
+++ b/test/gestures.c
@@ -34,7 +34,7 @@ START_TEST(gestures_cap)
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
- if (litest_is_synaptics_semi_mt(dev))
+ if (libevdev_has_property(dev->evdev, INPUT_PROP_SEMI_MT))
ck_assert(!libinput_device_has_capability(device,
LIBINPUT_DEVICE_CAP_GESTURE));
else
@@ -82,13 +82,13 @@ START_TEST(gestures_swipe_3fg)
litest_drain_events(li);
litest_touch_down(dev, 0, 40, 40);
- litest_touch_down(dev, 1, 40, 50);
- litest_touch_down(dev, 2, 40, 60);
+ litest_touch_down(dev, 1, 50, 40);
+ litest_touch_down(dev, 2, 60, 40);
libinput_dispatch(li);
litest_touch_move_three_touches(dev,
40, 40,
- 40, 50,
- 40, 60,
+ 50, 40,
+ 60, 40,
dir_x, dir_y,
10, 2);
libinput_dispatch(li);
@@ -110,7 +110,6 @@ START_TEST(gestures_swipe_3fg)
dx = libinput_event_gesture_get_dx(gevent);
dy = libinput_event_gesture_get_dy(gevent);
- debug_trace("delta: %.2f/%.2f\n", dx, dy);
if (dir_x == 0.0)
ck_assert(dx == 0.0);
else if (dir_x < 0.0)
@@ -157,6 +156,345 @@ START_TEST(gestures_swipe_3fg)
}
END_TEST
+START_TEST(gestures_swipe_3fg_btntool)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_gesture *gevent;
+ double dx, dy;
+ int cardinal = _i; /* ranged test */
+ double dir_x, dir_y;
+ int cardinals[8][2] = {
+ { 0, 30 },
+ { 30, 30 },
+ { 30, 0 },
+ { 30, -30 },
+ { 0, -30 },
+ { -30, -30 },
+ { -30, 0 },
+ { -30, 30 },
+ };
+
+ if (libevdev_get_num_slots(dev->evdev) > 2 ||
+ !libinput_device_has_capability(dev->libinput_device,
+ LIBINPUT_DEVICE_CAP_GESTURE))
+ return;
+
+ dir_x = cardinals[cardinal][0];
+ dir_y = cardinals[cardinal][1];
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 40, 40);
+ litest_touch_down(dev, 1, 50, 40);
+ litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
+ litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 1);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+
+ libinput_dispatch(li);
+ litest_touch_move_two_touches(dev,
+ 40, 40,
+ 50, 40,
+ dir_x, dir_y,
+ 10, 2);
+ libinput_dispatch(li);
+
+ event = libinput_get_event(li);
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
+ 3);
+ dx = libinput_event_gesture_get_dx(gevent);
+ dy = libinput_event_gesture_get_dy(gevent);
+ ck_assert(dx == 0.0);
+ ck_assert(dy == 0.0);
+ libinput_event_destroy(event);
+
+ while ((event = libinput_get_event(li)) != NULL) {
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
+ 3);
+
+ dx = libinput_event_gesture_get_dx(gevent);
+ dy = libinput_event_gesture_get_dy(gevent);
+ if (dir_x == 0.0)
+ ck_assert(dx == 0.0);
+ else if (dir_x < 0.0)
+ ck_assert(dx < 0.0);
+ else if (dir_x > 0.0)
+ ck_assert(dx > 0.0);
+
+ if (dir_y == 0.0)
+ ck_assert(dy == 0.0);
+ else if (dir_y < 0.0)
+ ck_assert(dy < 0.0);
+ else if (dir_y > 0.0)
+ ck_assert(dy > 0.0);
+
+ dx = libinput_event_gesture_get_dx_unaccelerated(gevent);
+ dy = libinput_event_gesture_get_dy_unaccelerated(gevent);
+ if (dir_x == 0.0)
+ ck_assert(dx == 0.0);
+ else if (dir_x < 0.0)
+ ck_assert(dx < 0.0);
+ else if (dir_x > 0.0)
+ ck_assert(dx > 0.0);
+
+ if (dir_y == 0.0)
+ ck_assert(dy == 0.0);
+ else if (dir_y < 0.0)
+ ck_assert(dy < 0.0);
+ else if (dir_y > 0.0)
+ ck_assert(dy > 0.0);
+
+ libinput_event_destroy(event);
+ }
+
+ litest_touch_up(dev, 0);
+ litest_touch_up(dev, 1);
+ libinput_dispatch(li);
+ event = libinput_get_event(li);
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_SWIPE_END,
+ 3);
+ ck_assert(!libinput_event_gesture_get_cancelled(gevent));
+ libinput_event_destroy(event);
+}
+END_TEST
+
+START_TEST(gestures_swipe_4fg)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_gesture *gevent;
+ double dx, dy;
+ int cardinal = _i; /* ranged test */
+ double dir_x, dir_y;
+ int cardinals[8][2] = {
+ { 0, 3 },
+ { 3, 3 },
+ { 3, 0 },
+ { 3, -3 },
+ { 0, -3 },
+ { -3, -3 },
+ { -3, 0 },
+ { -3, 3 },
+ };
+ int i;
+
+ if (libevdev_get_num_slots(dev->evdev) < 4)
+ return;
+
+ dir_x = cardinals[cardinal][0];
+ dir_y = cardinals[cardinal][1];
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 40, 40);
+ litest_touch_down(dev, 1, 50, 40);
+ litest_touch_down(dev, 2, 60, 40);
+ litest_touch_down(dev, 3, 70, 40);
+ libinput_dispatch(li);
+
+ for (i = 0; i < 8; i++) {
+ litest_push_event_frame(dev);
+
+ dir_x += cardinals[cardinal][0];
+ dir_y += cardinals[cardinal][1];
+
+ litest_touch_move(dev,
+ 0,
+ 40 + dir_x,
+ 40 + dir_y);
+ litest_touch_move(dev,
+ 1,
+ 50 + dir_x,
+ 40 + dir_y);
+ litest_touch_move(dev,
+ 2,
+ 60 + dir_x,
+ 40 + dir_y);
+ litest_touch_move(dev,
+ 3,
+ 70 + dir_x,
+ 40 + dir_y);
+ litest_pop_event_frame(dev);
+ libinput_dispatch(li);
+ }
+
+ libinput_dispatch(li);
+
+ event = libinput_get_event(li);
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
+ 4);
+ dx = libinput_event_gesture_get_dx(gevent);
+ dy = libinput_event_gesture_get_dy(gevent);
+ ck_assert(dx == 0.0);
+ ck_assert(dy == 0.0);
+ libinput_event_destroy(event);
+
+ while ((event = libinput_get_event(li)) != NULL) {
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
+ 4);
+
+ dx = libinput_event_gesture_get_dx(gevent);
+ dy = libinput_event_gesture_get_dy(gevent);
+ if (dir_x == 0.0)
+ ck_assert(dx == 0.0);
+ else if (dir_x < 0.0)
+ ck_assert(dx < 0.0);
+ else if (dir_x > 0.0)
+ ck_assert(dx > 0.0);
+
+ if (dir_y == 0.0)
+ ck_assert(dy == 0.0);
+ else if (dir_y < 0.0)
+ ck_assert(dy < 0.0);
+ else if (dir_y > 0.0)
+ ck_assert(dy > 0.0);
+
+ dx = libinput_event_gesture_get_dx_unaccelerated(gevent);
+ dy = libinput_event_gesture_get_dy_unaccelerated(gevent);
+ if (dir_x == 0.0)
+ ck_assert(dx == 0.0);
+ else if (dir_x < 0.0)
+ ck_assert(dx < 0.0);
+ else if (dir_x > 0.0)
+ ck_assert(dx > 0.0);
+
+ if (dir_y == 0.0)
+ ck_assert(dy == 0.0);
+ else if (dir_y < 0.0)
+ ck_assert(dy < 0.0);
+ else if (dir_y > 0.0)
+ ck_assert(dy > 0.0);
+
+ libinput_event_destroy(event);
+ }
+
+ litest_touch_up(dev, 0);
+ litest_touch_up(dev, 1);
+ litest_touch_up(dev, 2);
+ litest_touch_up(dev, 3);
+ libinput_dispatch(li);
+ event = libinput_get_event(li);
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_SWIPE_END,
+ 4);
+ ck_assert(!libinput_event_gesture_get_cancelled(gevent));
+ libinput_event_destroy(event);
+}
+END_TEST
+
+START_TEST(gestures_swipe_4fg_btntool)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_gesture *gevent;
+ double dx, dy;
+ int cardinal = _i; /* ranged test */
+ double dir_x, dir_y;
+ int cardinals[8][2] = {
+ { 0, 30 },
+ { 30, 30 },
+ { 30, 0 },
+ { 30, -30 },
+ { 0, -30 },
+ { -30, -30 },
+ { -30, 0 },
+ { -30, 30 },
+ };
+
+ if (libevdev_get_num_slots(dev->evdev) > 2 ||
+ !libinput_device_has_capability(dev->libinput_device,
+ LIBINPUT_DEVICE_CAP_GESTURE))
+ return;
+
+ dir_x = cardinals[cardinal][0];
+ dir_y = cardinals[cardinal][1];
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 40, 40);
+ litest_touch_down(dev, 1, 50, 40);
+ litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
+ litest_event(dev, EV_KEY, BTN_TOOL_QUADTAP, 1);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+
+ libinput_dispatch(li);
+ litest_touch_move_two_touches(dev,
+ 40, 40,
+ 50, 40,
+ dir_x, dir_y,
+ 10, 2);
+ libinput_dispatch(li);
+
+ event = libinput_get_event(li);
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
+ 4);
+ dx = libinput_event_gesture_get_dx(gevent);
+ dy = libinput_event_gesture_get_dy(gevent);
+ ck_assert(dx == 0.0);
+ ck_assert(dy == 0.0);
+ libinput_event_destroy(event);
+
+ while ((event = libinput_get_event(li)) != NULL) {
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
+ 4);
+
+ dx = libinput_event_gesture_get_dx(gevent);
+ dy = libinput_event_gesture_get_dy(gevent);
+ if (dir_x == 0.0)
+ ck_assert(dx == 0.0);
+ else if (dir_x < 0.0)
+ ck_assert(dx < 0.0);
+ else if (dir_x > 0.0)
+ ck_assert(dx > 0.0);
+
+ if (dir_y == 0.0)
+ ck_assert(dy == 0.0);
+ else if (dir_y < 0.0)
+ ck_assert(dy < 0.0);
+ else if (dir_y > 0.0)
+ ck_assert(dy > 0.0);
+
+ dx = libinput_event_gesture_get_dx_unaccelerated(gevent);
+ dy = libinput_event_gesture_get_dy_unaccelerated(gevent);
+ if (dir_x == 0.0)
+ ck_assert(dx == 0.0);
+ else if (dir_x < 0.0)
+ ck_assert(dx < 0.0);
+ else if (dir_x > 0.0)
+ ck_assert(dx > 0.0);
+
+ if (dir_y == 0.0)
+ ck_assert(dy == 0.0);
+ else if (dir_y < 0.0)
+ ck_assert(dy < 0.0);
+ else if (dir_y > 0.0)
+ ck_assert(dy > 0.0);
+
+ libinput_event_destroy(event);
+ }
+
+ litest_touch_up(dev, 0);
+ litest_touch_up(dev, 1);
+ libinput_dispatch(li);
+ event = libinput_get_event(li);
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_SWIPE_END,
+ 4);
+ ck_assert(!libinput_event_gesture_get_cancelled(gevent));
+ libinput_event_destroy(event);
+}
+END_TEST
+
START_TEST(gestures_pinch)
{
struct litest_device *dev = litest_current_device();
@@ -180,7 +518,9 @@ START_TEST(gestures_pinch)
{ -30, 30 },
};
- if (libevdev_get_num_slots(dev->evdev) < 3)
+ if (libevdev_get_num_slots(dev->evdev) < 2 ||
+ !libinput_device_has_capability(dev->libinput_device,
+ LIBINPUT_DEVICE_CAP_GESTURE))
return;
dir_x = cardinals[cardinal][0];
@@ -195,13 +535,13 @@ START_TEST(gestures_pinch)
for (i = 0; i < 8; i++) {
litest_push_event_frame(dev);
if (dir_x > 0.0)
- dir_x -= 3;
+ dir_x -= 2;
else if (dir_x < 0.0)
- dir_x += 3;
+ dir_x += 2;
if (dir_y > 0.0)
- dir_y -= 3;
+ dir_y -= 2;
else if (dir_y < 0.0)
- dir_y += 3;
+ dir_y += 2;
litest_touch_move(dev,
0,
50 + dir_x,
@@ -256,7 +596,7 @@ START_TEST(gestures_pinch)
}
END_TEST
-START_TEST(gestures_spread)
+START_TEST(gestures_pinch_3fg)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
@@ -269,14 +609,14 @@ START_TEST(gestures_spread)
double scale, oldscale;
double angle;
int cardinals[8][2] = {
- { 0, 1 },
- { 1, 1 },
- { 1, 0 },
- { 1, -1 },
- { 0, -1 },
- { -1, -1 },
- { -1, 0 },
- { -1, 1 },
+ { 0, 30 },
+ { 30, 30 },
+ { 30, 0 },
+ { 30, -30 },
+ { 0, -30 },
+ { -30, -30 },
+ { -30, 0 },
+ { -30, 30 },
};
if (libevdev_get_num_slots(dev->evdev) < 3)
@@ -289,6 +629,432 @@ START_TEST(gestures_spread)
litest_touch_down(dev, 0, 50 + dir_x, 50 + dir_y);
litest_touch_down(dev, 1, 50 - dir_x, 50 - dir_y);
+ litest_touch_down(dev, 2, 51 - dir_x, 51 - dir_y);
+ libinput_dispatch(li);
+
+ for (i = 0; i < 8; i++) {
+ litest_push_event_frame(dev);
+ if (dir_x > 0.0)
+ dir_x -= 2;
+ else if (dir_x < 0.0)
+ dir_x += 2;
+ if (dir_y > 0.0)
+ dir_y -= 2;
+ else if (dir_y < 0.0)
+ dir_y += 2;
+ litest_touch_move(dev,
+ 0,
+ 50 + dir_x,
+ 50 + dir_y);
+ litest_touch_move(dev,
+ 1,
+ 50 - dir_x,
+ 50 - dir_y);
+ litest_touch_move(dev,
+ 2,
+ 51 - dir_x,
+ 51 - dir_y);
+ litest_pop_event_frame(dev);
+ libinput_dispatch(li);
+ }
+
+ event = libinput_get_event(li);
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
+ 3);
+ dx = libinput_event_gesture_get_dx(gevent);
+ dy = libinput_event_gesture_get_dy(gevent);
+ scale = libinput_event_gesture_get_scale(gevent);
+ ck_assert(dx == 0.0);
+ ck_assert(dy == 0.0);
+ ck_assert(scale == 1.0);
+
+ libinput_event_destroy(event);
+
+ while ((event = libinput_get_event(li)) != NULL) {
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
+ 3);
+
+ oldscale = scale;
+ scale = libinput_event_gesture_get_scale(gevent);
+
+ ck_assert(scale < oldscale);
+
+ angle = libinput_event_gesture_get_angle_delta(gevent);
+ ck_assert_double_le(fabs(angle), 1.0);
+
+ libinput_event_destroy(event);
+ libinput_dispatch(li);
+ }
+
+ litest_touch_up(dev, 0);
+ litest_touch_up(dev, 1);
+ litest_touch_up(dev, 2);
+ libinput_dispatch(li);
+ event = libinput_get_event(li);
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_PINCH_END,
+ 3);
+ ck_assert(!libinput_event_gesture_get_cancelled(gevent));
+ libinput_event_destroy(event);
+}
+END_TEST
+
+START_TEST(gestures_pinch_3fg_btntool)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_gesture *gevent;
+ double dx, dy;
+ int cardinal = _i; /* ranged test */
+ double dir_x, dir_y;
+ int i;
+ double scale, oldscale;
+ double angle;
+ int cardinals[8][2] = {
+ { 0, 30 },
+ { 30, 30 },
+ { 30, 0 },
+ { 30, -30 },
+ { 0, -30 },
+ { -30, -30 },
+ { -30, 0 },
+ { -30, 30 },
+ };
+
+ if (libevdev_get_num_slots(dev->evdev) > 2 ||
+ !libinput_device_has_capability(dev->libinput_device,
+ LIBINPUT_DEVICE_CAP_GESTURE))
+ return;
+
+ dir_x = cardinals[cardinal][0];
+ dir_y = cardinals[cardinal][1];
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 50 + dir_x, 50 + dir_y);
+ litest_touch_down(dev, 1, 50 - dir_x, 50 - dir_y);
+ litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
+ litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 1);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+ libinput_dispatch(li);
+
+ for (i = 0; i < 8; i++) {
+ litest_push_event_frame(dev);
+ if (dir_x > 0.0)
+ dir_x -= 2;
+ else if (dir_x < 0.0)
+ dir_x += 2;
+ if (dir_y > 0.0)
+ dir_y -= 2;
+ else if (dir_y < 0.0)
+ dir_y += 2;
+ litest_touch_move(dev,
+ 0,
+ 50 + dir_x,
+ 50 + dir_y);
+ litest_touch_move(dev,
+ 1,
+ 50 - dir_x,
+ 50 - dir_y);
+ litest_pop_event_frame(dev);
+ libinput_dispatch(li);
+ }
+
+ event = libinput_get_event(li);
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
+ 3);
+ dx = libinput_event_gesture_get_dx(gevent);
+ dy = libinput_event_gesture_get_dy(gevent);
+ scale = libinput_event_gesture_get_scale(gevent);
+ ck_assert(dx == 0.0);
+ ck_assert(dy == 0.0);
+ ck_assert(scale == 1.0);
+
+ libinput_event_destroy(event);
+
+ while ((event = libinput_get_event(li)) != NULL) {
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
+ 3);
+
+ oldscale = scale;
+ scale = libinput_event_gesture_get_scale(gevent);
+
+ ck_assert(scale < oldscale);
+
+ angle = libinput_event_gesture_get_angle_delta(gevent);
+ ck_assert_double_le(fabs(angle), 1.0);
+
+ libinput_event_destroy(event);
+ libinput_dispatch(li);
+ }
+
+ litest_touch_up(dev, 0);
+ litest_touch_up(dev, 1);
+ libinput_dispatch(li);
+ event = libinput_get_event(li);
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_PINCH_END,
+ 3);
+ ck_assert(!libinput_event_gesture_get_cancelled(gevent));
+ libinput_event_destroy(event);
+}
+END_TEST
+
+START_TEST(gestures_pinch_4fg)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_gesture *gevent;
+ double dx, dy;
+ int cardinal = _i; /* ranged test */
+ double dir_x, dir_y;
+ int i;
+ double scale, oldscale;
+ double angle;
+ int cardinals[8][2] = {
+ { 0, 30 },
+ { 30, 30 },
+ { 30, 0 },
+ { 30, -30 },
+ { 0, -30 },
+ { -30, -30 },
+ { -30, 0 },
+ { -30, 30 },
+ };
+
+ if (libevdev_get_num_slots(dev->evdev) < 4)
+ return;
+
+ dir_x = cardinals[cardinal][0];
+ dir_y = cardinals[cardinal][1];
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 50 + dir_x, 50 + dir_y);
+ litest_touch_down(dev, 1, 50 - dir_x, 50 - dir_y);
+ litest_touch_down(dev, 2, 51 - dir_x, 51 - dir_y);
+ litest_touch_down(dev, 3, 52 - dir_x, 52 - dir_y);
+ libinput_dispatch(li);
+
+ for (i = 0; i < 8; i++) {
+ litest_push_event_frame(dev);
+ if (dir_x > 0.0)
+ dir_x -= 2;
+ else if (dir_x < 0.0)
+ dir_x += 2;
+ if (dir_y > 0.0)
+ dir_y -= 2;
+ else if (dir_y < 0.0)
+ dir_y += 2;
+ litest_touch_move(dev,
+ 0,
+ 50 + dir_x,
+ 50 + dir_y);
+ litest_touch_move(dev,
+ 1,
+ 50 - dir_x,
+ 50 - dir_y);
+ litest_touch_move(dev,
+ 2,
+ 51 - dir_x,
+ 51 - dir_y);
+ litest_touch_move(dev,
+ 3,
+ 52 - dir_x,
+ 52 - dir_y);
+ litest_pop_event_frame(dev);
+ libinput_dispatch(li);
+ }
+
+ event = libinput_get_event(li);
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
+ 4);
+ dx = libinput_event_gesture_get_dx(gevent);
+ dy = libinput_event_gesture_get_dy(gevent);
+ scale = libinput_event_gesture_get_scale(gevent);
+ ck_assert(dx == 0.0);
+ ck_assert(dy == 0.0);
+ ck_assert(scale == 1.0);
+
+ libinput_event_destroy(event);
+
+ while ((event = libinput_get_event(li)) != NULL) {
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
+ 4);
+
+ oldscale = scale;
+ scale = libinput_event_gesture_get_scale(gevent);
+
+ ck_assert(scale < oldscale);
+
+ angle = libinput_event_gesture_get_angle_delta(gevent);
+ ck_assert_double_le(fabs(angle), 1.0);
+
+ libinput_event_destroy(event);
+ libinput_dispatch(li);
+ }
+
+ litest_touch_up(dev, 0);
+ litest_touch_up(dev, 1);
+ litest_touch_up(dev, 2);
+ litest_touch_up(dev, 3);
+ libinput_dispatch(li);
+ event = libinput_get_event(li);
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_PINCH_END,
+ 4);
+ ck_assert(!libinput_event_gesture_get_cancelled(gevent));
+ libinput_event_destroy(event);
+}
+END_TEST
+
+START_TEST(gestures_pinch_4fg_btntool)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_gesture *gevent;
+ double dx, dy;
+ int cardinal = _i; /* ranged test */
+ double dir_x, dir_y;
+ int i;
+ double scale, oldscale;
+ double angle;
+ int cardinals[8][2] = {
+ { 0, 30 },
+ { 30, 30 },
+ { 30, 0 },
+ { 30, -30 },
+ { 0, -30 },
+ { -30, -30 },
+ { -30, 0 },
+ { -30, 30 },
+ };
+
+ if (libevdev_get_num_slots(dev->evdev) > 2 ||
+ !libinput_device_has_capability(dev->libinput_device,
+ LIBINPUT_DEVICE_CAP_GESTURE))
+ return;
+
+ dir_x = cardinals[cardinal][0];
+ dir_y = cardinals[cardinal][1];
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 50 + dir_x, 50 + dir_y);
+ litest_touch_down(dev, 1, 50 - dir_x, 50 - dir_y);
+ litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
+ litest_event(dev, EV_KEY, BTN_TOOL_QUADTAP, 1);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+ libinput_dispatch(li);
+
+ for (i = 0; i < 8; i++) {
+ litest_push_event_frame(dev);
+ if (dir_x > 0.0)
+ dir_x -= 2;
+ else if (dir_x < 0.0)
+ dir_x += 2;
+ if (dir_y > 0.0)
+ dir_y -= 2;
+ else if (dir_y < 0.0)
+ dir_y += 2;
+ litest_touch_move(dev,
+ 0,
+ 50 + dir_x,
+ 50 + dir_y);
+ litest_touch_move(dev,
+ 1,
+ 50 - dir_x,
+ 50 - dir_y);
+ litest_pop_event_frame(dev);
+ libinput_dispatch(li);
+ }
+
+ event = libinput_get_event(li);
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
+ 4);
+ dx = libinput_event_gesture_get_dx(gevent);
+ dy = libinput_event_gesture_get_dy(gevent);
+ scale = libinput_event_gesture_get_scale(gevent);
+ ck_assert(dx == 0.0);
+ ck_assert(dy == 0.0);
+ ck_assert(scale == 1.0);
+
+ libinput_event_destroy(event);
+
+ while ((event = libinput_get_event(li)) != NULL) {
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
+ 4);
+
+ oldscale = scale;
+ scale = libinput_event_gesture_get_scale(gevent);
+
+ ck_assert(scale < oldscale);
+
+ angle = libinput_event_gesture_get_angle_delta(gevent);
+ ck_assert_double_le(fabs(angle), 1.0);
+
+ libinput_event_destroy(event);
+ libinput_dispatch(li);
+ }
+
+ litest_touch_up(dev, 0);
+ litest_touch_up(dev, 1);
+ libinput_dispatch(li);
+ event = libinput_get_event(li);
+ gevent = litest_is_gesture_event(event,
+ LIBINPUT_EVENT_GESTURE_PINCH_END,
+ 4);
+ ck_assert(!libinput_event_gesture_get_cancelled(gevent));
+ libinput_event_destroy(event);
+}
+END_TEST
+
+START_TEST(gestures_spread)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ struct libinput_event_gesture *gevent;
+ double dx, dy;
+ int cardinal = _i; /* ranged test */
+ double dir_x, dir_y;
+ int i;
+ double scale, oldscale;
+ double angle;
+ int cardinals[8][2] = {
+ { 0, 30 },
+ { 30, 30 },
+ { 30, 0 },
+ { 30, -30 },
+ { 0, -30 },
+ { -30, -30 },
+ { -30, 0 },
+ { -30, 30 },
+ };
+
+ if (libevdev_get_num_slots(dev->evdev) < 2 ||
+ !libinput_device_has_capability(dev->libinput_device,
+ LIBINPUT_DEVICE_CAP_GESTURE))
+ return;
+
+ dir_x = cardinals[cardinal][0];
+ dir_y = cardinals[cardinal][1];
+
+ litest_drain_events(li);
+
+ litest_touch_down(dev, 0, 50 + dir_x, 50 + dir_y);
+ litest_touch_down(dev, 1, 50 - dir_x, 50 - dir_y);
libinput_dispatch(li);
for (i = 0; i < 15; i++) {
@@ -366,18 +1132,17 @@ START_TEST(gestures_time_usec)
litest_drain_events(li);
litest_touch_down(dev, 0, 40, 40);
- litest_touch_down(dev, 1, 40, 50);
- litest_touch_down(dev, 2, 40, 60);
+ litest_touch_down(dev, 1, 50, 40);
+ litest_touch_down(dev, 2, 60, 40);
libinput_dispatch(li);
litest_touch_move_three_touches(dev,
40, 40,
- 40, 50,
- 40, 60,
+ 50, 40,
+ 60, 40,
0, 30,
4, 2);
- litest_wait_for_event(li);
-
+ libinput_dispatch(li);
event = libinput_get_event(li);
gevent = litest_is_gesture_event(event,
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
@@ -398,7 +1163,14 @@ litest_setup_tests(void)
litest_add("gestures:cap", gestures_nocap, LITEST_ANY, LITEST_TOUCHPAD);
litest_add_ranged("gestures:swipe", gestures_swipe_3fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals);
+ litest_add_ranged("gestures:swipe", gestures_swipe_3fg_btntool, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals);
+ litest_add_ranged("gestures:swipe", gestures_swipe_4fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals);
+ litest_add_ranged("gestures:swipe", gestures_swipe_4fg_btntool, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals);
litest_add_ranged("gestures:pinch", gestures_pinch, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals);
+ litest_add_ranged("gestures:pinch", gestures_pinch_3fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals);
+ litest_add_ranged("gestures:pinch", gestures_pinch_3fg_btntool, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals);
+ litest_add_ranged("gestures:pinch", gestures_pinch_4fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals);
+ litest_add_ranged("gestures:pinch", gestures_pinch_4fg_btntool, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals);
litest_add_ranged("gestures:pinch", gestures_spread, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals);
litest_add("gesture:time", gestures_time_usec, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
diff --git a/test/litest-selftest.c b/test/litest-selftest.c
index 5016514d..47d5ef13 100644
--- a/test/litest-selftest.c
+++ b/test/litest-selftest.c
@@ -169,8 +169,8 @@ START_TEST(litest_ptr_eq_notrigger)
int v = 10;
int *a = &v;
int *b = &v;
- int c = NULL;
- int d = NULL;
+ int *c = NULL;
+ int *d = NULL;
litest_assert_ptr_eq(a, b);
litest_assert_ptr_eq(c, d);
diff --git a/test/litest.c b/test/litest.c
index 3da187cf..99bed061 100644
--- a/test/litest.c
+++ b/test/litest.c
@@ -1588,18 +1588,22 @@ litest_touch_move_two_touches(struct litest_device *d,
int steps, int sleep_ms)
{
for (int i = 0; i < steps - 1; i++) {
+ litest_push_event_frame(d);
litest_touch_move(d, 0, x0 + dx / steps * i,
y0 + dy / steps * i);
litest_touch_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_touch_move(d, 0, x0 + dx, y0 + dy);
litest_touch_move(d, 1, x1 + dx, y1 + dy);
+ litest_pop_event_frame(d);
}
void
diff --git a/test/litest.h b/test/litest.h
index a272fe3e..99f654af 100644
--- a/test/litest.h
+++ b/test/litest.h
@@ -596,16 +596,6 @@ litest_enable_buttonareas(struct litest_device *dev)
litest_assert_int_eq(status, expected);
}
-static inline int
-litest_is_synaptics_semi_mt(struct litest_device *dev)
-{
- struct libevdev *evdev = dev->evdev;
-
- return libevdev_has_property(evdev, INPUT_PROP_SEMI_MT) &&
- libevdev_get_id_vendor(evdev) == 0x2 &&
- libevdev_get_id_product(evdev) == 0x7;
-}
-
static inline void
litest_enable_drag_lock(struct libinput_device *device)
{
diff --git a/test/touchpad-tap.c b/test/touchpad-tap.c
index 4450ec35..7f23671f 100644
--- a/test/touchpad-tap.c
+++ b/test/touchpad-tap.c
@@ -241,7 +241,7 @@ START_TEST(touchpad_1fg_multitap_n_drag_2fg)
int range = _i,
ntaps;
- if (litest_is_synaptics_semi_mt(dev))
+ if (libevdev_has_property(dev->evdev, INPUT_PROP_SEMI_MT))
return;
litest_enable_tap(dev->libinput_device);
@@ -1782,60 +1782,60 @@ litest_setup_tests(void)
{
struct range multitap_range = {3, 8};
- 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_draglock, LITEST_TOUCHPAD, LITEST_ANY);
- litest_add("touchpad:tap", touchpad_1fg_tap_n_drag_draglock_tap, LITEST_TOUCHPAD, LITEST_ANY);
- litest_add("touchpad:tap", touchpad_1fg_tap_n_drag_draglock_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_n_hold_first, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
- litest_add("touchpad:tap", touchpad_2fg_tap_n_hold_second, 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);
-
- litest_add("touchpad:tap", touchpad_2fg_tap_click_apple, LITEST_APPLE_CLICKPAD, LITEST_ANY);
- litest_add("touchpad:tap", touchpad_no_2fg_tap_after_move, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
- litest_add("touchpad:tap", touchpad_no_2fg_tap_after_timeout, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
- litest_add("touchpad:tap", touchpad_no_first_fg_tap_after_move, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
- litest_add("touchpad:tap", touchpad_no_first_fg_tap_after_move, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
- 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);
+ litest_add("tap:1fg", touchpad_1fg_tap, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add("tap:1fg", touchpad_1fg_doubletap, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add_ranged("tap:1fg", touchpad_1fg_multitap, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range);
+ litest_add_ranged("tap:1fg", touchpad_1fg_multitap_n_drag_timeout, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range);
+ litest_add_ranged("tap:1fg", touchpad_1fg_multitap_n_drag_tap, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range);
+ litest_add_ranged("tap:1fg", touchpad_1fg_multitap_n_drag_move, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range);
+ litest_add_ranged("tap:1fg", touchpad_1fg_multitap_n_drag_2fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &multitap_range);
+ litest_add_ranged("tap:1fg", touchpad_1fg_multitap_n_drag_click, LITEST_CLICKPAD, LITEST_ANY, &multitap_range);
+ litest_add("tap:1fg", touchpad_1fg_tap_n_drag, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add("tap:1fg", touchpad_1fg_tap_n_drag_draglock, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add("tap:1fg", touchpad_1fg_tap_n_drag_draglock_tap, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add("tap:1fg", touchpad_1fg_tap_n_drag_draglock_timeout, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add("tap:2fg", touchpad_2fg_tap_n_drag, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+ litest_add("tap:2fg", touchpad_2fg_tap_n_drag_3fg_btntool, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_APPLE_CLICKPAD);
+ litest_add("tap:2fg", touchpad_2fg_tap_n_drag_3fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+ litest_add("tap:2fg", touchpad_2fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
+ litest_add("tap:2fg", touchpad_2fg_tap_inverted, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+ litest_add("tap:2fg", touchpad_2fg_tap_n_hold_first, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+ litest_add("tap:2fg", touchpad_2fg_tap_n_hold_second, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+ litest_add("tap:2fg", touchpad_2fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
+ litest_add("tap:2fg", touchpad_1fg_tap_click, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_CLICKPAD);
+ litest_add("tap:2fg", touchpad_2fg_tap_click, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_SINGLE_TOUCH|LITEST_CLICKPAD);
+
+ litest_add("tap:2fg", touchpad_2fg_tap_click_apple, LITEST_APPLE_CLICKPAD, LITEST_ANY);
+ litest_add("tap:2fg", touchpad_no_2fg_tap_after_move, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
+ litest_add("tap:2fg", touchpad_no_2fg_tap_after_timeout, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
+ litest_add("tap:2fg", touchpad_no_first_fg_tap_after_move, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+ litest_add("tap:2fg", touchpad_no_first_fg_tap_after_move, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+ litest_add("tap:3fg", touchpad_3fg_tap_btntool, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+ litest_add("tap:3fg", touchpad_3fg_tap_btntool_inverted, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+ litest_add("tap:3fg", touchpad_3fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+ litest_add("tap:3fg", touchpad_3fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+ litest_add("tap:4fg", touchpad_4fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
+ litest_add("tap:4fg", touchpad_4fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
+ litest_add("tap:5fg", touchpad_5fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
+ litest_add("tap:5fg", 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_ranged("touchpad:tap", touchpad_1fg_multitap_n_drag_tap_click, LITEST_CLICKPAD, LITEST_ANY, &multitap_range);
- litest_add("touchpad:tap", touchpad_1fg_tap_n_drag_draglock_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);
- litest_add("touchpad:tap", touchpad_tap_invalid, LITEST_TOUCHPAD, LITEST_ANY);
- litest_add("touchpad:tap", touchpad_tap_is_available, LITEST_TOUCHPAD, LITEST_ANY);
- litest_add("touchpad:tap", touchpad_tap_is_not_available, LITEST_ANY, LITEST_TOUCHPAD);
-
- litest_add("touchpad:tap", clickpad_1fg_tap_click, LITEST_CLICKPAD, LITEST_ANY);
- litest_add("touchpad:tap", clickpad_2fg_tap_click, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH|LITEST_APPLE_CLICKPAD);
-
- litest_add("touchpad:tap", touchpad_drag_lock_default_disabled, LITEST_TOUCHPAD, LITEST_ANY);
- litest_add("touchpad:tap", touchpad_drag_lock_default_unavailable, LITEST_ANY, LITEST_TOUCHPAD);
+ litest_add("tap:1fg", touchpad_1fg_double_tap_click, LITEST_CLICKPAD, LITEST_ANY);
+ litest_add("tap:1fg", touchpad_1fg_tap_n_drag_click, LITEST_CLICKPAD, LITEST_ANY);
+ litest_add_ranged("tap:1fg", touchpad_1fg_multitap_n_drag_tap_click, LITEST_CLICKPAD, LITEST_ANY, &multitap_range);
+ litest_add("tap:1fg", touchpad_1fg_tap_n_drag_draglock_tap_click, LITEST_CLICKPAD, LITEST_ANY);
+
+ litest_add("tap:config", touchpad_tap_default_disabled, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_ANY);
+ litest_add("tap:config", touchpad_tap_default_enabled, LITEST_TOUCHPAD, LITEST_BUTTON);
+ litest_add("tap:config", touchpad_tap_invalid, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add("tap:config", touchpad_tap_is_available, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add("tap:config", touchpad_tap_is_not_available, LITEST_ANY, LITEST_TOUCHPAD);
+
+ litest_add("tap:1fg", clickpad_1fg_tap_click, LITEST_CLICKPAD, LITEST_ANY);
+ litest_add("tap:2fg", clickpad_2fg_tap_click, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH|LITEST_APPLE_CLICKPAD);
+
+ litest_add("tap:draglock", touchpad_drag_lock_default_disabled, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add("tap:draglock", touchpad_drag_lock_default_unavailable, LITEST_ANY, LITEST_TOUCHPAD);
}
diff --git a/test/touchpad.c b/test/touchpad.c
index 4eb94187..c1765493 100644
--- a/test/touchpad.c
+++ b/test/touchpad.c
@@ -289,11 +289,11 @@ START_TEST(touchpad_2fg_scroll_semi_mt)
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_touch_move_two_touches(dev,
+ 20, 20,
+ 30, 20,
+ 30, 40,
+ 10, 1);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS);
}