diff options
author | Peter Hutterer <peter.hutterer@who-t.net> | 2016-01-25 15:29:11 +1000 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2016-01-25 15:29:11 +1000 |
commit | de3f1fa6fa23cebef17640dc1eb4ea8df270ecd9 (patch) | |
tree | 0eb7ae087e9dd10fc2b962a8879d6906773a674a | |
parent | 6920a42fd4ec8dfa633ef5bc1865f994afc63693 (diff) | |
parent | b6f59d0e3b2e38abd0c79e36a5971dc7ea1e2b16 (diff) |
Merge branch 'master' into tablet-supporttablet-support
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | doc/gestures.dox | 38 | ||||
-rw-r--r-- | doc/svg/gesture-2fg-ambiguity.svg | 496 | ||||
-rw-r--r-- | doc/svg/pinch-gestures-softbuttons.svg | 365 | ||||
-rw-r--r-- | src/evdev-mt-touchpad-buttons.c | 27 | ||||
-rw-r--r-- | src/evdev-mt-touchpad-edge-scroll.c | 5 | ||||
-rw-r--r-- | src/evdev-mt-touchpad-gestures.c | 394 | ||||
-rw-r--r-- | src/evdev-mt-touchpad-tap.c | 3 | ||||
-rw-r--r-- | src/evdev-mt-touchpad.c | 36 | ||||
-rw-r--r-- | src/evdev-mt-touchpad.h | 33 | ||||
-rw-r--r-- | src/libinput-private.h | 2 | ||||
-rw-r--r-- | src/libinput.c | 8 | ||||
-rw-r--r-- | test/gestures.c | 824 | ||||
-rw-r--r-- | test/litest-selftest.c | 4 | ||||
-rw-r--r-- | test/litest.c | 4 | ||||
-rw-r--r-- | test/litest.h | 10 | ||||
-rw-r--r-- | test/touchpad-tap.c | 106 | ||||
-rw-r--r-- | test/touchpad.c | 10 |
18 files changed, 2057 insertions, 309 deletions
@@ -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); } |