https://bugs.freedesktop.org/show_bug.cgi?id=92834

Signed-off-by: Peter Hutterer <[email protected]>
---
 doc/gestures.dox                       |  16 ++
 doc/svg/pinch-gestures-softbuttons.svg | 365 +++++++++++++++++++++++++++++++++
 src/evdev-mt-touchpad-gestures.c       | 203 ++++++++++--------
 src/evdev-mt-touchpad.h                |   1 +
 4 files changed, 504 insertions(+), 81 deletions(-)
 create mode 100644 doc/svg/pinch-gestures-softbuttons.svg

diff --git a/doc/gestures.dox b/doc/gestures.dox
index 02ef09a..8632b8e 100644
--- a/doc/gestures.dox
+++ b/doc/gestures.dox
@@ -88,4 +88,20 @@ 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.
+
 */
diff --git a/doc/svg/pinch-gestures-softbuttons.svg 
b/doc/svg/pinch-gestures-softbuttons.svg
new file mode 100644
index 0000000..959cb4f
--- /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-gestures.c b/src/evdev-mt-touchpad-gestures.c
index c676caa..e7b75ac 100644
--- a/src/evdev-mt-touchpad-gestures.c
+++ b/src/evdev-mt-touchpad-gestures.c
@@ -40,6 +40,7 @@ gesture_state_to_str(enum tp_gesture_state state)
        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;
 }
@@ -94,34 +95,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.state) {
-               case GESTURE_STATE_NONE:
-               case GESTURE_STATE_UNKNOWN:
-                       log_bug_libinput(libinput,
-                                        "%s in unknown gesture mode\n",
-                                        __func__);
-                       break;
-               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;
-               }
+       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;
 }
 
@@ -247,19 +244,54 @@ tp_gesture_set_scroll_buildup(struct tp_dispatch *tp)
 }
 
 static enum tp_gesture_state
-tp_gesture_twofinger_handle_state_none(struct tp_dispatch *tp, uint64_t time)
+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;
 
-       if (tp_gesture_get_active_touches(tp, tp->gesture.touches, 2) != 2)
+       ntouches = tp_gesture_get_active_touches(tp, touches, 4);
+       if (ntouches < 2)
                return GESTURE_STATE_NONE;
 
-       first = tp->gesture.touches[0];
-       second = tp->gesture.touches[1];
+       first = touches[0];
+       second = touches[1];
+
+       /* 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];
+               }
+
+               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;
 }
@@ -281,14 +313,16 @@ tp_gesture_same_directions(int dir1, int dir2)
 }
 
 static enum tp_gesture_state
-tp_gesture_twofinger_handle_state_unknown(struct tp_dispatch *tp, uint64_t 
time)
+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;
 
-       /* 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_STATE_SCROLL;
        }
@@ -299,10 +333,15 @@ tp_gesture_twofinger_handle_state_unknown(struct 
tp_dispatch *tp, uint64_t time)
        if (dir1 == UNDEFINED_DIRECTION || dir2 == UNDEFINED_DIRECTION)
                return GESTURE_STATE_UNKNOWN;
 
-       /* If both touches are moving in the same direction assume scroll */
+       /* If both touches are moving in the same direction assume
+        * scroll or swipe */
        if (tp_gesture_same_directions(dir1, dir2)) {
-               tp_gesture_set_scroll_buildup(tp);
-               return GESTURE_STATE_SCROLL;
+               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 if (tp->gesture.enabled) {
                tp_gesture_get_pinch_info(tp,
                                          &tp->gesture.initial_distance,
@@ -316,7 +355,7 @@ tp_gesture_twofinger_handle_state_unknown(struct 
tp_dispatch *tp, uint64_t time)
 }
 
 static enum tp_gesture_state
-tp_gesture_twofinger_handle_state_scroll(struct tp_dispatch *tp, uint64_t time)
+tp_gesture_handle_state_scroll(struct tp_dispatch *tp, uint64_t time)
 {
        struct normalized_coords delta;
 
@@ -350,7 +389,26 @@ tp_gesture_twofinger_handle_state_scroll(struct 
tp_dispatch *tp, uint64_t time)
 }
 
 static enum tp_gesture_state
-tp_gesture_twofinger_handle_state_pinch(struct tp_dispatch *tp, uint64_t time)
+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_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;
@@ -388,25 +446,29 @@ tp_gesture_twofinger_handle_state_pinch(struct 
tp_dispatch *tp, uint64_t time)
 }
 
 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_state oldstate = tp->gesture.state;
 
        if (tp->gesture.state == GESTURE_STATE_NONE)
                tp->gesture.state =
-                       tp_gesture_twofinger_handle_state_none(tp, time);
+                       tp_gesture_handle_state_none(tp, time);
 
        if (tp->gesture.state == GESTURE_STATE_UNKNOWN)
                tp->gesture.state =
-                       tp_gesture_twofinger_handle_state_unknown(tp, time);
+                       tp_gesture_handle_state_unknown(tp, time);
 
        if (tp->gesture.state == GESTURE_STATE_SCROLL)
                tp->gesture.state =
-                       tp_gesture_twofinger_handle_state_scroll(tp, time);
+                       tp_gesture_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.state == GESTURE_STATE_PINCH)
                tp->gesture.state =
-                       tp_gesture_twofinger_handle_state_pinch(tp, time);
+                       tp_gesture_handle_state_pinch(tp, time);
 
        log_debug(tp_libinput_context(tp),
                  "gesture state: %s → %s\n",
@@ -414,23 +476,6 @@ tp_gesture_post_twofinger(struct tp_dispatch *tp, uint64_t 
time)
                  gesture_state_to_str(tp->gesture.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);
-       }
-}
-
 void
 tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time)
 {
@@ -453,11 +498,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;
        }
 }
@@ -484,32 +527,30 @@ tp_gesture_end(struct tp_dispatch *tp, uint64_t time, 
bool cancelled)
        if (!tp->gesture.started)
                return;
 
-       switch (tp->gesture.finger_count) {
-       case 2:
-               switch (state) {
-               case GESTURE_STATE_NONE:
-               case GESTURE_STATE_UNKNOWN:
-                       log_bug_libinput(libinput,
-                                        "%s in unknown gesture mode\n",
-                                        __func__);
-                       break;
-               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;
-               }
+       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;
 }
 
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index 690feb5..20456f5 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -135,6 +135,7 @@ enum tp_gesture_state {
        GESTURE_STATE_UNKNOWN,
        GESTURE_STATE_SCROLL,
        GESTURE_STATE_PINCH,
+       GESTURE_STATE_SWIPE,
 };
 
 enum tp_thumb_state {
-- 
2.5.0

_______________________________________________
wayland-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to