Merge branch 'master' into tablet-support

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer 2015-07-08 13:50:24 +10:00
commit 95089b77d4
27 changed files with 3251 additions and 66 deletions

View file

@ -62,6 +62,26 @@
useit(c);
}
- do not mix function invocations and variable definitions.
wrong:
{
int a = foo();
int b = 7;
}
right:
{
int a;
int b = 7;
a = foo();
}
There are exceptions here, e.g. tp_libinput_context(),
litest_current_device()
- if/else: { on the same line, no curly braces if both blocks are a single
statement. If either if or else block are multiple statements, both must
have curly braces.
@ -88,3 +108,9 @@
#include <libevdev/libevdev.h>
#include "libinput-private.h"
- goto jumps only to the end of the function, and only for good reasons
(usually cleanup). goto never jumps backwards
- Use stdbool.h's bool for booleans within the library (instead of 'int').
Exception: the public API uses int, not bool.

View file

@ -14,6 +14,7 @@ header_files = \
$(srcdir)/clickpad-softbuttons.dox \
$(srcdir)/device-configuration-via-udev.dox \
$(srcdir)/faqs.dox \
$(srcdir)/gestures.dox \
$(srcdir)/normalization-of-relative-motion.dox \
$(srcdir)/palm-detection.dox \
$(srcdir)/scrolling.dox \
@ -34,8 +35,11 @@ diagram_files = \
$(srcdir)/svg/button-scrolling.svg \
$(srcdir)/svg/edge-scrolling.svg \
$(srcdir)/svg/palm-detection.svg \
$(srcdir)/svg/pinch-gestures.svg \
$(srcdir)/svg/swipe-gestures.svg \
$(srcdir)/svg/tap-n-drag.svg \
$(srcdir)/svg/top-software-buttons.svg \
$(srcdir)/svg/touchscreen-gestures.svg \
$(srcdir)/svg/twofinger-scrolling.svg
html/index.html: libinput.doxygen $(header_files) $(diagram_files)

View file

@ -3,6 +3,12 @@
Frequently asked questions about libinput.
@section faq_fast_mouse My mouse moves too fast, even at the slowest setting
This is a symptom of high-dpi mice (greater than 1000dpi). These devices
need a udev hwdb entry to normalize their motion. See @ref
motion_normalization for a detailed explanation.
@section faq_kinetic_scrolling Kinetic scrolling does not work
The X.Org synaptics driver implemented kinetic scrolling in the driver. It

91
doc/gestures.dox Normal file
View file

@ -0,0 +1,91 @@
/**
@page gestures Gestures
libinput supports basic gestures on touchpads and other indirect input
devices. Two types of gestures are supported: @ref gestures_pinch and @ref
gestures_swipe. Support for gestures depends on the hardware device, most
touchpads support both gestures and any device that may send gesture events
has the @ref LIBINPUT_DEVICE_CAP_GESTURE capability set.
Note that libinput **does not** support gestures on touchscreens, see
@ref gestures_touchscreens.
@section gestures_lifetime Lifetime of a gesture
A gesture's lifetime has three distinct stages: begin, update and end, each
with their own event types. Begin is sent when the fingers are first set
down or libinput decides that the gesture begins. For @ref gestures_pinch
this sets the initial scale. Any events changing properties of the gesture
are sent as update events. On termination of the gesture, an end event is
sent.
A gesture includes the finger count (see
libinput_event_gesture_get_finger_count()) and that finger count remains the
same for the lifetime of a gesture. Thus, if a user puts down a fourth
finger during a three-finger swipe gesture, libinput will end
the three-finger gesture and, if applicable, start a four-finger swipe
gesture. A caller may decide that those gestures are semantically identical
and continue the two gestures as one single gesture.
@see LIBINPUT_EVENT_GESTURE_PINCH_BEGIN
@see LIBINPUT_EVENT_GESTURE_PINCH_UPDATE
@see LIBINPUT_EVENT_GESTURE_PINCH_END
@see LIBINPUT_EVENT_GESTURE_PINCH_BEGIN
@see LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE
@see LIBINPUT_EVENT_GESTURE_SWIPE_END
@section gestures_pinch Pinch gestures
Pinch gestures are executed when two or more fingers are located on the
touchpad and are either changing the relative distance to each other
(pinching) or are changing the relative angle (rotate). Pinch gestures may
change both rotation and distance at the same time. For such gestures,
libinput calculates a logical center for the gestures and provides the
caller with the delta x/y coordinates of that center, the relative angle of
the fingers compared to the previous event, and the absolute scale compared
to the initial finger position.
@image html pinch-gestures.svg "The pinch and rotate gestures"
The illustration above shows a basic pinch in the left image and a rotate in
the right angle. Not shown is a movement of the logical center if the
fingers move unevenly. Such a movement is supported by libinput, it is
merely left out of the illustration.
Note that while position and angle is relative to the previous event, the
scale is always absolute and a multiplier of the initial finger position's
scale.
@section gestures_swipe Swipe gestures
Swipe gestures are executed when three or more fingers are moved
synchronously in the same direction. libinput provides x and y coordinates
in the gesture and thus allows swipe gestures in any direction, including
the tracing of complex paths. It is up to the caller to interpret the
gesture into an action or limit a gesture to specific directions only.
@image html swipe-gestures.svg "The swipe gestures"
The illustration above shows a vertical three-finger swipe. The coordinates
provided during the gesture are the movements of the logical center.
@section gestures_touchscreens Touchscreen gestures
Touchscreen gestures are **not** interpreted by libinput. Rather, any touch
point is passed to the caller and any interpretation of gestures is up to
the caller or, eventually, the X or Wayland client.
Interpreting gestures on a touchscreen requires context that libinput does
not have, such as the location of windows and other virtual objects on the
screen as well as the context of those virtual objects:
@image html touchscreen-gestures.svg "Context-sensitivity of touchscreen gestures"
In this example, the finger movements are identical but in the left case
both fingers are located within the same window, thus suggesting an attemp
to zoom. In the right case both fingers are located on a window border,
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.
*/

View file

@ -44,6 +44,9 @@ Devices usually do not advertise their resolution and libinput relies on
the udev property <b>MOUSE_DPI</b> for this information. This property is usually
set via the <a
href="http://cgit.freedesktop.org/systemd/systemd/tree/hwdb/70-mouse.hwdb">udev hwdb</a>.
The "mouse-dpi-tool" utility provided by <a
href="http://freedesktop.org/wiki/Software/libevdev/">libevdev</a> should be
used to measure a device's resolution.
The format of the property for single-resolution mice is:
@code

612
doc/svg/pinch-gestures.svg Normal file
View file

@ -0,0 +1,612 @@
<?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="193.91415mm"
height="71.032608mm"
viewBox="0 0 687.09741 251.69034"
id="svg4313"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="pinch-gestures.svg">
<defs
id="defs4315">
<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-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
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:isstock="true"
style="overflow:visible"
id="marker7250"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Mend"
inkscape:collect="always">
<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="path7252"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path4986"
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="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="Arrow1Mstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mstart"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path4983"
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="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:isstock="true"
style="overflow:visible"
id="marker6261"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend"
inkscape:collect="always">
<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="path6263"
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="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path4998"
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="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="0.93677625"
inkscape:cx="60.063165"
inkscape:cy="195.28138"
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
width="309.33386"
height="209.27208"
x="375.99207"
y="127.09696"
id="rect14688"
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" />
<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="g3663-9-5"
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,-40.638425,-251.35321)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path2820-6-6"
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="path2824-1-1"
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="path2824-7-1-4"
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.64805599,0.12052062,-0.12052062,0.64805599,87.872503,-305.95371)"
id="g4877">
<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="path4879"
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="path4881"
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="path4883"
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
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,-40.638425,-251.35321)"
id="g4889">
<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="path4891"
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="path4893"
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="path4895"
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="g4897"
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,87.872503,-305.95371)">
<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.64805599,0.12052062,-0.12052062,0.64805599,327.582,-251.35321)"
id="g4915">
<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="path4917"
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="path4919"
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="path4921"
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="g4923"
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,456.09293,-305.95371)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4925"
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="path4927"
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="path4929"
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
id="g4931"
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,327.582,-251.35321)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4933"
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="path4935"
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="path4937"
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.64805599,0.12052062,-0.12052062,0.64805599,456.09293,-305.95371)"
id="g4939">
<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="path4941"
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="path4943"
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="path4945"
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:1.31833494;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Lend)"
d="M 237.82329,186.65441 272.448,168.6762"
id="path4971"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path5363"
d="m 43.293248,279.41755 34.62471,-17.97821"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.31833494;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker6499)" />
<circle
style="opacity:0.92000002;fill:#ffdd55;fill-opacity:0.07734806;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path6880"
cx="158.49547"
cy="227.65926"
r="5.3374538" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.97016698px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#marker7356);marker-end:url(#marker7250)"
d="M 88.03522,236.61109 213.47708,179.47406"
id="path6882"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path6884"
d="M 25.998133,239.56832 257.65533,138.66416"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.97016698px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.50000095px;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="139.81439"
y="246.73911"
id="text6886"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan6888"
x="139.81439"
y="246.73911">logical</tspan><tspan
sodipodi:role="line"
x="139.81439"
y="262.36411"
id="tspan6890">center</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.50000095px;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="-0.65095264"
y="247.5675"
id="text6892"
sodipodi:linespacing="125%"
transform="matrix(0.90931568,-0.41610697,0.41610692,0.90931568,0,0)"><tspan
sodipodi:role="line"
id="tspan6894"
x="-0.65095264"
y="247.5675">initial scale (1.0)</tspan></text>
<text
transform="matrix(0.90931568,-0.41610697,0.41610692,0.90931568,0,0)"
sodipodi:linespacing="125%"
id="text6896"
y="225.43965"
x="-16.939013"
style="font-style:normal;font-weight:normal;font-size:12.50000095px;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"
xml:space="preserve"><tspan
y="225.43965"
x="-16.939013"
id="tspan6898"
sodipodi:role="line">current scale (&gt; 1.0)</tspan></text>
<circle
r="5.3374538"
cy="226.45061"
cx="529.7337"
id="circle7474"
style="opacity:0.92000002;fill:#ffdd55;fill-opacity:0.07734806;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text7476"
y="245.53046"
x="511.05258"
style="font-style:normal;font-weight:normal;font-size:12.50000095px;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"
xml:space="preserve"><tspan
y="245.53046"
x="511.05258"
id="tspan7478"
sodipodi:role="line">logical</tspan><tspan
id="tspan7480"
y="261.15546"
x="511.05258"
sodipodi:role="line">center</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker6261)"
d="m 470.41432,259.01526 c 18.14735,33.09222 51.23956,27.22102 51.23956,27.22102"
id="path7554"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path7692"
d="M 592.09959,192.40501 C 567.10018,164.13044 536.11794,177.15603 536.11794,177.15603"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker7694)" />
<path
style="fill:#ff0000;fill-rule:evenodd;stroke:#dd0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 469.81386,258.48153 636.94289,169.34604"
id="path9270"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 540.33497,130.38263 523.25512,286.23628"
id="path9272"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.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="575.56219"
y="156.53615"
id="text9274"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan9276"
x="575.56219"
y="156.53615">delta</tspan><tspan
sodipodi:role="line"
x="575.56219"
y="172.16115"
id="tspan9278">angle</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 34 KiB

512
doc/svg/swipe-gestures.svg Normal file
View file

@ -0,0 +1,512 @@
<?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="77.985535mm"
viewBox="0 0 315.09744 276.3267"
id="svg4313"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="swipe-gestures.svg">
<defs
id="defs4315">
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="marker13223"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path13225"
style="fill:#fb0000;fill-opacity:1;fill-rule:evenodd;stroke:#fb0000;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="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="marker11669"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path11671"
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="marker11171"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend"
inkscape:collect="always">
<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="path11173"
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-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
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="marker10193"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lstart">
<path
inkscape:connector-curvature="0"
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="path10195" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path4998"
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="path4986"
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="path4983"
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: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>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lstart-5"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
transform="matrix(0.8,0,0,0.8,10,0)"
id="path3707"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lend-9"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
id="path3710"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lstart-7"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
transform="matrix(0.8,0,0,0.8,10,0)"
id="path3707-7"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lend-7"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
id="path3710-9"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.3248017"
inkscape:cx="390.51795"
inkscape:cy="169.53168"
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(-6.8742073,-88.520861)">
<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="91.402641"
x="9.7559872"
height="209.27208"
width="309.33386" />
<g
id="g3663-9-5"
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,-24.296656,-262.41117)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path2820-6-6"
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="path2824-1-1"
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="path2824-7-1-4"
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.64805599,0.12052062,-0.12052062,0.64805599,18.050732,-296.51834)"
id="g9299">
<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="path9301"
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="path9303"
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="path9305"
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="g4897"
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,70.021073,-291.1216)">
<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>
<circle
style="opacity:0.92000002;fill:#ffdd55;fill-opacity:0.07734806;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path6880"
cx="158.49547"
cy="227.65924"
r="5.3374538" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.50000095px;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="157.93031"
y="147.10155"
id="text6886"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan6888"
x="157.93031"
y="147.10155">logical</tspan><tspan
sodipodi:role="line"
x="157.93031"
y="162.72655"
id="tspan6890">center</tspan></text>
<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 110.34988,168.32728 0,55.85742"
id="path11149"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path11169"
d="m 152.62036,133.6051 0,55.85742"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker11171)" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker11669)"
d="m 206.21329,138.13408 0,55.85742"
id="path11667"
inkscape:connector-curvature="0" />
<circle
r="5.3374538"
cy="173.65924"
cx="158.49547"
id="circle13213"
style="opacity:0.92000002;fill:#ffdd55;fill-opacity:0.07734806;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#fb0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker13223);stroke-miterlimit:4;stroke-dasharray:4,1;stroke-dashoffset:0"
d="m 158.659,172.10143 0,55.85742"
id="path13221"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -0,0 +1,440 @@
<?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="198.41673mm"
height="68.753075mm"
viewBox="0 0 703.05143 243.61326"
id="svg4313"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="touchscreen-gestures.svg">
<defs
id="defs4315">
<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:isstock="true"
style="overflow:visible"
id="marker6261"
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="path6263"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lstart"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<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: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="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path4998"
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="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="0.93677625"
inkscape:cx="610.35826"
inkscape:cy="81.237892"
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(-21.856644,-132.29226)">
<rect
style="opacity:0.92000002;fill:#000000;fill-opacity:0.07734806;stroke:#000000;stroke-width:1.22726846;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4905"
width="333.6037"
height="240.70985"
x="22.470278"
y="132.9059" />
<g
id="g4885"
style="fill:#ffeeaa"
transform="matrix(0.65916749,0,0,0.65916749,7.4494548,128.12082)">
<rect
y="53.624214"
x="49.833439"
height="221.76169"
width="360.3331"
id="rect4873"
style="opacity:0.92000002;fill:#ffeeaa;fill-opacity:0.2983426;stroke:#000000;stroke-width:0.66787815;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
style="opacity:0.92000002;fill:#ffeeaa;fill-opacity:0.2983426;stroke:#000000;stroke-width:0.70335335;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4875"
width="360.29761"
height="245.96988"
x="49.851177"
y="29.398293" />
</g>
<g
id="g3663-9-5"
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,-40.638425,-251.35321)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path2820-6-6"
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="path2824-1-1"
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="path2824-7-1-4"
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.64805599,0.12052062,-0.12052062,0.64805599,87.872503,-305.95371)"
id="g4877">
<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="path4879"
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="path4881"
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="path4883"
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
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,-40.638425,-251.35321)"
id="g4889">
<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="path4891"
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="path4893"
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="path4895"
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="g4897"
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,87.872503,-305.95371)">
<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
y="132.9059"
x="390.6907"
height="240.70985"
width="333.6037"
id="rect4907"
style="opacity:0.92000002;fill:#000000;fill-opacity:0.07734806;stroke:#000000;stroke-width:1.22726846;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<g
id="g4951"
transform="matrix(0.65916749,0,0,0.65916749,136.45794,-94.79591)">
<rect
style="opacity:0.92000002;fill:#ffeeaa;fill-opacity:0.2983426;stroke:#000000;stroke-width:0.39509788;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4911"
width="240.39772"
height="116.3256"
x="390.11847"
y="546.44226" />
<rect
y="522.21863"
x="390.13852"
height="141.18213"
width="240.35757"
id="rect4913"
style="opacity:0.92000002;fill:#ffeeaa;fill-opacity:0.2983426;stroke:#000000;stroke-width:0.43523186;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
</g>
<g
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,327.582,-251.35321)"
id="g4915">
<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="path4917"
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="path4919"
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="path4921"
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="g4923"
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,456.09293,-305.95371)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4925"
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="path4927"
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="path4929"
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
id="g4931"
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,327.582,-251.35321)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4933"
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="path4935"
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="path4937"
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
id="g4955"
transform="matrix(0.65916749,0,0,0.65916749,296.60088,-154.71595)">
<rect
y="546.44226"
x="390.11847"
height="116.3256"
width="240.39772"
id="rect4957"
style="opacity:0.92000002;fill:#ffeeaa;fill-opacity:0.2983426;stroke:#000000;stroke-width:0.39509788;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
style="opacity:0.92000002;fill:#ffeeaa;fill-opacity:0.2983426;stroke:#000000;stroke-width:0.43523186;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4959"
width="240.35757"
height="141.18213"
x="390.13852"
y="522.21863" />
</g>
<g
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,456.09293,-305.95371)"
id="g4939">
<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="path4941"
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="path4943"
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="path4945"
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:1.31833494;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Lend)"
d="m 113.99435,244.83266 34.62471,-17.97821"
id="path4971"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path5363"
d="m 173.52713,216.96933 34.62471,-17.97821"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.31833494;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker6499)" />
<path
inkscape:connector-curvature="0"
id="path6257"
d="m 481.80981,244.83266 34.62471,-17.97821"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.31833494;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker6261)" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.31833494;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow2Lstart)"
d="M 541.34259,216.96933 575.9673,198.99112"
id="path6259"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 28 KiB

View file

@ -832,12 +832,10 @@ tp_check_clickfinger_distance(struct tp_dispatch *tp,
x = abs(t1->point.x - t2->point.x);
y = abs(t1->point.y - t2->point.y);
/* no resolution, so let's assume they're close enough together */
/* no resolution, so let's assume they're close enough together if
they're within 30% of the touchpad width or height */
if (tp->device->abs.fake_resolution) {
int w, h;
/* Use a maximum of 30% of the touchpad width or height if
* we dont' have resolution. */
w = tp->device->abs.dimensions.x;
h = tp->device->abs.dimensions.y;

View file

@ -23,7 +23,6 @@
#include "config.h"
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <limits.h>
@ -31,6 +30,7 @@
#include "evdev-mt-touchpad.h"
#define DEFAULT_GESTURE_SWITCH_TIMEOUT 100 /* ms */
#define DEFAULT_GESTURE_2FG_SCROLL_TIMEOUT 1000 /* ms */
static struct normalized_coords
tp_get_touches_delta(struct tp_dispatch *tp, bool average)
@ -76,12 +76,37 @@ tp_get_average_touches_delta(struct tp_dispatch *tp)
static void
tp_gesture_start(struct tp_dispatch *tp, uint64_t time)
{
struct libinput *libinput = tp->device->base.seat->libinput;
const struct normalized_coords zero = { 0.0, 0.0 };
if (tp->gesture.started)
return;
switch (tp->gesture.finger_count) {
case 2:
/* NOP */
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;
}
break;
case 3:
case 4:
gesture_notify_swipe(&tp->device->base, time,
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
tp->gesture.finger_count,
&zero, &zero);
break;
}
tp->gesture.started = true;
@ -110,19 +135,191 @@ tp_gesture_post_pointer_motion(struct tp_dispatch *tp, uint64_t time)
}
}
static unsigned int
tp_gesture_get_active_touches(struct tp_dispatch *tp,
struct tp_touch **touches,
unsigned int count)
{
unsigned int i, n = 0;
struct tp_touch *t;
memset(touches, 0, count * sizeof(struct tp_touch *));
for (i = 0; i < tp->num_slots; i++) {
t = &tp->touches[i];
if (tp_touch_active(tp, t)) {
touches[n++] = t;
if (n == count)
return count;
}
}
/*
* This can happen when the user does .e.g:
* 1) Put down 1st finger in center (so active)
* 2) Put down 2nd finger in a button area (so inactive)
* 3) Put down 3th finger somewhere, gets reported as a fake finger,
* so gets same coordinates as 1st -> active
*
* We could avoid this by looking at all touches, be we really only
* want to look at real touches.
*/
return n;
}
static int
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.
*/
if (tp->semi_mt)
move_threshold = TP_MM_TO_DPI_NORMALIZED(4);
else
move_threshold = TP_MM_TO_DPI_NORMALIZED(3);
delta = device_delta(touch->point, touch->gesture.initial);
normalized = tp_normalize_delta(tp, delta);
if (normalized_length(normalized) < move_threshold)
return UNDEFINED_DIRECTION;
return normalized_get_direction(normalized);
}
static void
tp_gesture_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
tp_gesture_get_pinch_info(struct tp_dispatch *tp,
double *distance,
double *angle,
struct device_float_coords *center)
{
struct normalized_coords normalized;
struct device_float_coords delta;
struct tp_touch *first = tp->gesture.touches[0],
*second = tp->gesture.touches[1];
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;
*center = device_average(first->point, second->point);
}
static void
tp_gesture_set_scroll_buildup(struct tp_dispatch *tp)
{
struct device_float_coords d0, d1;
struct device_float_coords average;
struct tp_touch *first = tp->gesture.touches[0],
*second = tp->gesture.touches[1];
d0 = device_delta(first->point, first->gesture.initial);
d1 = device_delta(second->point, second->gesture.initial);
average = device_float_average(d0, d1);
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)
{
struct tp_touch *first, *second;
if (tp_gesture_get_active_touches(tp, tp->gesture.touches, 2) != 2)
return GESTURE_2FG_STATE_NONE;
first = tp->gesture.touches[0];
second = tp->gesture.touches[1];
tp->gesture.initial_time = time;
first->gesture.initial = first->point;
second->gesture.initial = second->point;
return GESTURE_2FG_STATE_UNKNOWN;
}
static enum tp_gesture_2fg_state
tp_gesture_twofinger_handle_state_unknown(struct tp_dispatch *tp, uint64_t time)
{
struct normalized_coords normalized;
struct device_float_coords delta;
struct tp_touch *first = tp->gesture.touches[0],
*second = tp->gesture.touches[1];
int dir1, dir2;
delta = device_delta(first->point, second->point);
normalized = tp_normalize_delta(tp, delta);
/* If fingers are further than 3 cm apart assume pinch */
if (normalized_length(normalized) > TP_MM_TO_DPI_NORMALIZED(30)) {
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;
}
/* Elif fingers have been close together for a while, scroll */
if (time > (tp->gesture.initial_time + DEFAULT_GESTURE_2FG_SCROLL_TIMEOUT)) {
tp_gesture_set_scroll_buildup(tp);
return GESTURE_2FG_STATE_SCROLL;
}
/* 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 {
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;
}
}
static enum tp_gesture_2fg_state
tp_gesture_twofinger_handle_state_scroll(struct tp_dispatch *tp, uint64_t time)
{
struct normalized_coords delta;
if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG)
return;
return GESTURE_2FG_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;
return GESTURE_2FG_STATE_SCROLL;
delta = tp_get_delta(&tp->touches[0]);
} else {
@ -132,13 +329,89 @@ tp_gesture_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
delta = tp_filter_motion(tp, &delta, time);
if (normalized_is_zero(delta))
return;
return GESTURE_2FG_STATE_SCROLL;
tp_gesture_start(tp, time);
evdev_post_scroll(tp->device,
time,
LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
&delta);
return GESTURE_2FG_STATE_SCROLL;
}
static enum tp_gesture_2fg_state
tp_gesture_twofinger_handle_state_pinch(struct tp_dispatch *tp, uint64_t time)
{
double angle, angle_delta, distance, scale;
struct device_float_coords center, fdelta;
struct normalized_coords delta, unaccel;
tp_gesture_get_pinch_info(tp, &distance, &angle, &center);
scale = distance / tp->gesture.initial_distance;
angle_delta = angle - tp->gesture.angle;
tp->gesture.angle = angle;
if (angle_delta > 180.0)
angle_delta -= 360.0;
else if (angle_delta < -180.0)
angle_delta += 360.0;
fdelta = device_float_delta(center, tp->gesture.center);
tp->gesture.center = center;
unaccel = tp_normalize_delta(tp, fdelta);
delta = tp_filter_motion(tp, &unaccel, time);
if (normalized_is_zero(delta) && normalized_is_zero(unaccel) &&
scale == tp->gesture.prev_scale && angle_delta == 0.0)
return GESTURE_2FG_STATE_PINCH;
tp_gesture_start(tp, time);
gesture_notify_pinch(&tp->device->base, time,
LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
&delta, &unaccel, scale, angle_delta);
tp->gesture.prev_scale = scale;
return GESTURE_2FG_STATE_PINCH;
}
static void
tp_gesture_post_twofinger(struct tp_dispatch *tp, uint64_t 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.twofinger_state == GESTURE_2FG_STATE_UNKNOWN)
tp->gesture.twofinger_state =
tp_gesture_twofinger_handle_state_unknown(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.twofinger_state == GESTURE_2FG_STATE_PINCH)
tp->gesture.twofinger_state =
tp_gesture_twofinger_handle_state_pinch(tp, time);
}
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
@ -149,7 +422,7 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time)
/* When tap-and-dragging, or a clickpad is clicked force 1fg mode */
if (tp_tap_dragging(tp) || (tp->buttons.is_clickpad && tp->buttons.state)) {
tp_gesture_stop(tp, time);
tp_gesture_cancel(tp, time);
tp->gesture.finger_count = 1;
tp->gesture.finger_count_pending = 0;
}
@ -163,7 +436,11 @@ 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_scroll(tp, time);
tp_gesture_post_twofinger(tp, time);
break;
case 3:
case 4:
tp_gesture_post_swipe(tp, time);
break;
}
}
@ -179,20 +456,57 @@ tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
}
void
tp_gesture_stop(struct tp_dispatch *tp, uint64_t time)
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;
tp->gesture.twofinger_state = GESTURE_2FG_STATE_NONE;
if (!tp->gesture.started)
return;
switch (tp->gesture.finger_count) {
case 2:
tp_gesture_stop_twofinger_scroll(tp, time);
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;
}
break;
case 3:
case 4:
gesture_notify_swipe_end(&tp->device->base, time,
tp->gesture.finger_count, cancelled);
break;
}
tp->gesture.started = false;
}
void
tp_gesture_cancel(struct tp_dispatch *tp, uint64_t time)
{
tp_gesture_end(tp, time, true);
}
void
tp_gesture_stop(struct tp_dispatch *tp, uint64_t time)
{
tp_gesture_end(tp, time, false);
}
static void
tp_gesture_finger_count_switch_timeout(uint64_t now, void *data)
{
@ -201,7 +515,7 @@ tp_gesture_finger_count_switch_timeout(uint64_t now, void *data)
if (!tp->gesture.finger_count_pending)
return;
tp_gesture_stop(tp, now); /* End current gesture */
tp_gesture_cancel(tp, now); /* End current gesture */
tp->gesture.finger_count = tp->gesture.finger_count_pending;
tp->gesture.finger_count_pending = 0;
}
@ -240,6 +554,8 @@ tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time)
int
tp_init_gesture(struct tp_dispatch *tp)
{
tp->gesture.twofinger_state = GESTURE_2FG_STATE_NONE;
libinput_timer_init(&tp->gesture.finger_count_switch_timer,
tp->device->base.seat->libinput,
tp_gesture_finger_count_switch_timeout, tp);

View file

@ -797,7 +797,7 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time)
tp->palm.trackpoint_active ||
tp->dwt.keyboard_active) {
tp_edge_scroll_stop_events(tp, time);
tp_gesture_stop(tp, time);
tp_gesture_cancel(tp, time);
return;
}
@ -976,7 +976,7 @@ tp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data)
if (!tp->palm.trackpoint_active) {
tp_edge_scroll_stop_events(tp, time);
tp_gesture_stop(tp, time);
tp_gesture_cancel(tp, time);
tp_tap_suspend(tp, time);
tp->palm.trackpoint_active = true;
}
@ -1053,7 +1053,7 @@ tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data)
if (!tp->dwt.keyboard_active) {
tp_edge_scroll_stop_events(tp, time);
tp_gesture_stop(tp, time);
tp_gesture_cancel(tp, time);
tp_tap_suspend(tp, time);
tp->dwt.keyboard_active = true;
timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1;

View file

@ -129,6 +129,13 @@ 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,
};
struct tp_touch {
struct tp_dispatch *tp;
enum touch_state state;
@ -181,6 +188,10 @@ struct tp_touch {
struct device_coords first; /* first coordinates if is_palm == true */
uint32_t time; /* first timestamp if is_palm == true */
} palm;
struct {
struct device_coords initial;
} gesture;
};
struct tp_dispatch {
@ -216,6 +227,13 @@ 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;
struct tp_touch *touches[2];
uint64_t initial_time;
double initial_distance;
double prev_scale;
double angle;
struct device_float_coords center;
} gesture;
struct {
@ -431,6 +449,9 @@ tp_remove_gesture(struct tp_dispatch *tp);
void
tp_gesture_stop(struct tp_dispatch *tp, uint64_t time);
void
tp_gesture_cancel(struct tp_dispatch *tp, uint64_t time);
void
tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time);

View file

@ -1941,6 +1941,7 @@ evdev_configure_device(struct evdev_device *device)
if (udev_tags & EVDEV_UDEV_TAG_TOUCHPAD) {
device->dispatch = evdev_mt_touchpad_create(device);
device->seat_caps |= EVDEV_DEVICE_GESTURE;
log_info(libinput,
"input device '%s', %s is a touchpad\n",
device->devname, devnode);
@ -2308,6 +2309,8 @@ evdev_device_has_capability(struct evdev_device *device,
return !!(device->seat_caps & EVDEV_DEVICE_KEYBOARD);
case LIBINPUT_DEVICE_CAP_TOUCH:
return !!(device->seat_caps & EVDEV_DEVICE_TOUCH);
case LIBINPUT_DEVICE_CAP_GESTURE:
return !!(device->seat_caps & EVDEV_DEVICE_GESTURE);
case LIBINPUT_DEVICE_CAP_TABLET:
return !!(device->seat_caps & EVDEV_DEVICE_TABLET);
default:

View file

@ -61,6 +61,7 @@ enum evdev_device_seat_capability {
EVDEV_DEVICE_KEYBOARD = (1 << 1),
EVDEV_DEVICE_TOUCH = (1 << 2),
EVDEV_DEVICE_TABLET = (1 << 3),
EVDEV_DEVICE_GESTURE = (1 << 5),
};
enum evdev_device_tags {

View file

@ -380,6 +380,35 @@ touch_notify_touch_up(struct libinput_device *device,
int32_t slot,
int32_t seat_slot);
void
gesture_notify_swipe(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);
void
gesture_notify_swipe_end(struct libinput_device *device,
uint64_t time,
int finger_count,
int cancelled);
void
gesture_notify_pinch(struct libinput_device *device,
uint64_t time,
enum libinput_event_type type,
const struct normalized_coords *delta,
const struct normalized_coords *unaccel,
double scale,
double angle);
void
gesture_notify_pinch_end(struct libinput_device *device,
uint64_t time,
double scale,
int cancelled);
void
touch_notify_frame(struct libinput_device *device,
uint64_t time);
@ -433,6 +462,39 @@ device_delta(struct device_coords a, struct device_coords b)
return delta;
}
static inline struct device_float_coords
device_average(struct device_coords a, struct device_coords b)
{
struct device_float_coords average;
average.x = (a.x + b.x) / 2.0;
average.y = (a.y + b.y) / 2.0;
return average;
}
static inline struct device_float_coords
device_float_delta(struct device_float_coords a, struct device_float_coords b)
{
struct device_float_coords delta;
delta.x = a.x - b.x;
delta.y = a.y - b.y;
return delta;
}
static inline struct device_float_coords
device_float_average(struct device_float_coords a, struct device_float_coords b)
{
struct device_float_coords average;
average.x = (a.x + b.x) / 2.0;
average.y = (a.y + b.y) / 2.0;
return average;
}
static inline double
normalized_length(struct normalized_coords norm)
{

View file

@ -114,6 +114,17 @@ struct libinput_event_touch {
struct device_coords point;
};
struct libinput_event_gesture {
struct libinput_event base;
uint32_t time;
int finger_count;
int cancelled;
struct normalized_coords delta;
struct normalized_coords delta_unaccel;
double scale;
double angle;
};
struct libinput_event_tablet {
struct libinput_event base;
uint32_t button;
@ -250,6 +261,22 @@ libinput_event_get_touch_event(struct libinput_event *event)
return (struct libinput_event_touch *) event;
}
LIBINPUT_EXPORT struct libinput_event_gesture *
libinput_event_get_gesture_event(struct libinput_event *event)
{
require_event_type(libinput_event_get_context(event),
event->type,
NULL,
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
LIBINPUT_EVENT_GESTURE_SWIPE_END,
LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
LIBINPUT_EVENT_GESTURE_PINCH_END);
return (struct libinput_event_gesture *) event;
}
LIBINPUT_EXPORT struct libinput_event_tablet *
libinput_event_get_tablet_event(struct libinput_event *event)
{
@ -259,6 +286,7 @@ libinput_event_get_tablet_event(struct libinput_event *event)
LIBINPUT_EVENT_TABLET_AXIS,
LIBINPUT_EVENT_TABLET_PROXIMITY,
LIBINPUT_EVENT_TABLET_BUTTON);
return (struct libinput_event_tablet *) event;
}
@ -662,6 +690,142 @@ libinput_event_touch_get_y(struct libinput_event_touch *event)
return evdev_convert_to_mm(device->abs.absinfo_y, event->point.y);
}
LIBINPUT_EXPORT uint32_t
libinput_event_gesture_get_time(struct libinput_event_gesture *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0,
LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
LIBINPUT_EVENT_GESTURE_PINCH_END,
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
LIBINPUT_EVENT_GESTURE_SWIPE_END);
return event->time;
}
LIBINPUT_EXPORT int
libinput_event_gesture_get_finger_count(struct libinput_event_gesture *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0,
LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
LIBINPUT_EVENT_GESTURE_PINCH_END,
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
LIBINPUT_EVENT_GESTURE_SWIPE_END);
return event->finger_count;
}
LIBINPUT_EXPORT int
libinput_event_gesture_get_cancelled(struct libinput_event_gesture *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0,
LIBINPUT_EVENT_GESTURE_PINCH_END,
LIBINPUT_EVENT_GESTURE_SWIPE_END);
return event->cancelled;
}
LIBINPUT_EXPORT double
libinput_event_gesture_get_dx(struct libinput_event_gesture *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0.0,
LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
LIBINPUT_EVENT_GESTURE_PINCH_END,
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
LIBINPUT_EVENT_GESTURE_SWIPE_END);
return event->delta.x;
}
LIBINPUT_EXPORT double
libinput_event_gesture_get_dy(struct libinput_event_gesture *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0.0,
LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
LIBINPUT_EVENT_GESTURE_PINCH_END,
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
LIBINPUT_EVENT_GESTURE_SWIPE_END);
return event->delta.y;
}
LIBINPUT_EXPORT double
libinput_event_gesture_get_dx_unaccelerated(
struct libinput_event_gesture *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0.0,
LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
LIBINPUT_EVENT_GESTURE_PINCH_END,
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
LIBINPUT_EVENT_GESTURE_SWIPE_END);
return event->delta_unaccel.x;
}
LIBINPUT_EXPORT double
libinput_event_gesture_get_dy_unaccelerated(
struct libinput_event_gesture *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0.0,
LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
LIBINPUT_EVENT_GESTURE_PINCH_END,
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
LIBINPUT_EVENT_GESTURE_SWIPE_END);
return event->delta_unaccel.y;
}
LIBINPUT_EXPORT double
libinput_event_gesture_get_scale(struct libinput_event_gesture *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0.0,
LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
LIBINPUT_EVENT_GESTURE_PINCH_END);
return event->scale;
}
LIBINPUT_EXPORT double
libinput_event_gesture_get_angle_delta(struct libinput_event_gesture *event)
{
require_event_type(libinput_event_get_context(&event->base),
event->base.type,
0.0,
LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
LIBINPUT_EVENT_GESTURE_PINCH_END);
return event->angle;
}
LIBINPUT_EXPORT int
libinput_event_tablet_axis_has_changed(struct libinput_event_tablet *event,
enum libinput_tablet_axis axis)
@ -1347,6 +1511,9 @@ device_has_cap(struct libinput_device *device,
case LIBINPUT_DEVICE_CAP_TOUCH:
capability = "CAP_TOUCH";
break;
case LIBINPUT_DEVICE_CAP_GESTURE:
capability = "CAP_GESTURE";
break;
case LIBINPUT_DEVICE_CAP_TABLET:
capability = "CAP_TABLET";
break;
@ -1709,6 +1876,89 @@ tablet_notify_button(struct libinput_device *device,
&button_event->base);
}
static void
gesture_notify(struct libinput_device *device,
uint64_t time,
enum libinput_event_type type,
int finger_count,
int cancelled,
const struct normalized_coords *delta,
const struct normalized_coords *unaccel,
double scale,
double angle)
{
struct libinput_event_gesture *gesture_event;
if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_GESTURE))
return;
gesture_event = zalloc(sizeof *gesture_event);
if (!gesture_event)
return;
*gesture_event = (struct libinput_event_gesture) {
.time = time,
.finger_count = finger_count,
.cancelled = cancelled,
.delta = *delta,
.delta_unaccel = *unaccel,
.scale = scale,
.angle = angle,
};
post_device_event(device, time, type,
&gesture_event->base);
}
void
gesture_notify_swipe(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)
{
gesture_notify(device, time, type, finger_count, 0, delta, unaccel,
0.0, 0.0);
}
void
gesture_notify_swipe_end(struct libinput_device *device,
uint64_t time,
int finger_count,
int cancelled)
{
const struct normalized_coords zero = { 0.0, 0.0 };
gesture_notify(device, time, LIBINPUT_EVENT_GESTURE_SWIPE_END,
finger_count, cancelled, &zero, &zero, 0.0, 0.0);
}
void
gesture_notify_pinch(struct libinput_device *device,
uint64_t time,
enum libinput_event_type type,
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);
}
void
gesture_notify_pinch_end(struct libinput_device *device,
uint64_t time,
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);
}
static void
libinput_post_event(struct libinput *libinput,
struct libinput_event *event)
@ -1974,6 +2224,12 @@ libinput_event_touch_get_base_event(struct libinput_event_touch *event)
return &event->base;
}
LIBINPUT_EXPORT struct libinput_event *
libinput_event_gesture_get_base_event(struct libinput_event_gesture *event)
{
return &event->base;
}
LIBINPUT_EXPORT struct libinput_event *
libinput_event_tablet_get_base_event(struct libinput_event_tablet *event)
{

View file

@ -58,7 +58,8 @@ enum libinput_device_capability {
LIBINPUT_DEVICE_CAP_KEYBOARD = 0,
LIBINPUT_DEVICE_CAP_POINTER = 1,
LIBINPUT_DEVICE_CAP_TOUCH = 2,
LIBINPUT_DEVICE_CAP_TABLET = 3
LIBINPUT_DEVICE_CAP_TABLET = 3,
LIBINPUT_DEVICE_CAP_GESTURE = 5,
};
/**
@ -273,7 +274,14 @@ enum libinput_event_type {
* initial proximity out event.
*/
LIBINPUT_EVENT_TABLET_PROXIMITY,
LIBINPUT_EVENT_TABLET_BUTTON
LIBINPUT_EVENT_TABLET_BUTTON,
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN = 800,
LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
LIBINPUT_EVENT_GESTURE_SWIPE_END,
LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
LIBINPUT_EVENT_GESTURE_PINCH_END,
};
/**
@ -467,6 +475,17 @@ libinput_event_get_keyboard_event(struct libinput_event *event);
struct libinput_event_touch *
libinput_event_get_touch_event(struct libinput_event *event);
/**
* Return the gesture event that is this input event. If the event type does
* not match the gesture event types, this function returns NULL.
*
* The inverse of this function is libinput_event_gesture_get_base_event().
*
* @return A gesture event, or NULL for other events
*/
struct libinput_event_gesture *
libinput_event_get_gesture_event(struct libinput_event *event);
/**
* @ingroup event
*
@ -1044,6 +1063,194 @@ libinput_event_touch_get_y_transformed(struct libinput_event_touch *event,
struct libinput_event *
libinput_event_touch_get_base_event(struct libinput_event_touch *event);
/**
* @defgroup event_gesture Gesture events
*
* Gesture events are generated when a gesture is recognized on a touchpad.
*
* Gesture sequences always start with a LIBINPUT_EVENT_GESTURE_FOO_START
* event. All following gesture events will be of the
* LIBINPUT_EVENT_GESTURE_FOO_UPDATE type until a
* LIBINPUT_EVENT_GESTURE_FOO_END is generated which signals the end of the
* gesture.
*
* See @ref gestures for more information on gesture handling.
*/
/**
* @ingroup event_gesture
*
* @return The event time for this event
*/
uint32_t
libinput_event_gesture_get_time(struct libinput_event_gesture *event);
/**
* @ingroup event_gesture
*
* @return The generic libinput_event of this event
*/
struct libinput_event *
libinput_event_gesture_get_base_event(struct libinput_event_gesture *event);
/**
* @ingroup event_gesture
*
* Return the number of fingers used for a gesture. This can be used e.g.
* to differentiate between 3 or 4 finger swipes.
*
* This function can be called on all gesture events and the returned finger
* count value will not change during a sequence.
*
* @return the number of fingers used for a gesture
*/
int
libinput_event_gesture_get_finger_count(struct libinput_event_gesture *event);
/**
* @ingroup event_gesture
*
* Return if the gesture ended normally, or if it was cancelled.
* For gesture events that are not of type
* @ref LIBINPUT_EVENT_GESTURE_SWIPE_END or
* @ref LIBINPUT_EVENT_GESTURE_PINCH_END, this function returns 0.
*
* @note It is an application bug to call this function for events other than
* @ref LIBINPUT_EVENT_GESTURE_SWIPE_END or
* @ref LIBINPUT_EVENT_GESTURE_PINCH_END.
*
* @return 0 or 1, with 1 indicating that the gesture was cancelled.
*/
int
libinput_event_gesture_get_cancelled(struct libinput_event_gesture *event);
/**
* @ingroup event_gesture
*
* Return the delta between the last event and the current event. For gesture
* events that are not of type @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE or
* @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, this function returns 0.
*
* If a device employs pointer acceleration, the delta returned by this
* function is the accelerated delta.
*
* Relative motion deltas are normalized to represent those of a device with
* 1000dpi resolution. See @ref motion_normalization for more details.
*
* @return the relative x movement since the last event
*/
double
libinput_event_gesture_get_dx(struct libinput_event_gesture *event);
/**
* @ingroup event_gesture
*
* Return the delta between the last event and the current event. For gesture
* events that are not of type @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE or
* @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, this function returns 0.
*
* If a device employs pointer acceleration, the delta returned by this
* function is the accelerated delta.
*
* Relative motion deltas are normalized to represent those of a device with
* 1000dpi resolution. See @ref motion_normalization for more details.
*
* @return the relative y movement since the last event
*/
double
libinput_event_gesture_get_dy(struct libinput_event_gesture *event);
/**
* @ingroup event_gesture
*
* Return the relative delta of the unaccelerated motion vector of the
* current event. For gesture events that are not of type
* @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE or
* @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, this function returns 0.
*
* Relative unaccelerated motion deltas are normalized to represent those of a
* device with 1000dpi resolution. See @ref motion_normalization for more
* details. Note that unaccelerated events are not equivalent to 'raw' events
* as read from the device.
*
* @return the unaccelerated relative x movement since the last event
*/
double
libinput_event_gesture_get_dx_unaccelerated(
struct libinput_event_gesture *event);
/**
* @ingroup event_gesture
*
* Return the relative delta of the unaccelerated motion vector of the
* current event. For gesture events that are not of type
* @ref LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE or
* @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, this function returns 0.
*
* Relative unaccelerated motion deltas are normalized to represent those of a
* device with 1000dpi resolution. See @ref motion_normalization for more
* details. Note that unaccelerated events are not equivalent to 'raw' events
* as read from the device.
*
* @return the unaccelerated relative y movement since the last event
*/
double
libinput_event_gesture_get_dy_unaccelerated(
struct libinput_event_gesture *event);
/**
* @ingroup event_gesture
*
* Return the absolute scale of a pinch gesture, the scale is the division
* of the current distance between the fingers and the distance at the start
* of the gesture. The scale begins at 1.0, and if e.g. the fingers moved
* together by 50% then the scale will become 0.5, if they move twice as far
* apart as initially the scale becomes 2.0, etc.
*
* For gesture events that are of type @ref
* LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, this function returns 1.0.
*
* For gesture events that are of type @ref
* LIBINPUT_EVENT_GESTURE_PINCH_END, this function returns the scale value
* of the most recent @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE event (if
* any) or 1.0 otherwise.
*
* For all other events this function returns 0.
*
* @note It is an application bug to call this function for events other than
* @ref LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, @ref
* LIBINPUT_EVENT_GESTURE_PINCH_END or
* @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE.
*
* @return the absolute scale of a pinch gesture
*/
double
libinput_event_gesture_get_scale(struct libinput_event_gesture *event);
/**
* @ingroup event_gesture
*
* Return the angle delta in degrees between the last and the current @ref
* LIBINPUT_EVENT_GESTURE_PINCH_UPDATE event. For gesture events that
* are not of type @ref LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, this
* function returns 0.
*
* The angle delta is defined as the change in angle of the line formed by
* the 2 fingers of a pinch gesture. Clockwise rotation is represented
* by a postive delta, counter-clockwise by a negative delta. If e.g. the
* fingers are on the 12 and 6 location of a clock face plate and they move
* to the 1 resp. 7 location in a single event then the angle delta is
* 30 degrees.
*
* If more than two fingers are present, the angle represents the rotation
* around the center of gravity. The calculation of the center of gravity is
* implementation-dependent.
*
* @return the angle delta since the last event
*/
double
libinput_event_gesture_get_angle_delta(struct libinput_event_gesture *event);
/**
* @defgroup event_tablet Tablet events
*

View file

@ -149,6 +149,20 @@ global:
libinput_device_config_tap_get_default_drag_lock_enabled;
} LIBINPUT_0.15.0;
LIBINPUT_0.20.0 {
libinput_event_gesture_get_angle_delta;
libinput_event_gesture_get_base_event;
libinput_event_gesture_get_cancelled;
libinput_event_gesture_get_dx;
libinput_event_gesture_get_dx_unaccelerated;
libinput_event_gesture_get_dy;
libinput_event_gesture_get_dy_unaccelerated;
libinput_event_gesture_get_finger_count;
libinput_event_gesture_get_scale;
libinput_event_gesture_get_time;
libinput_event_get_gesture_event;
} LIBINPUT_0.19.0;
/* tablet APIs, they are not part of any stable API promise yet.
* keep them separate */
LIBINPUT_TABLET_SUPPORT {
@ -175,4 +189,4 @@ LIBINPUT_TABLET_SUPPORT {
libinput_tool_ref;
libinput_tool_set_user_data;
libinput_tool_unref;
} LIBINPUT_0.19.0;
} LIBINPUT_0.20.0;

View file

@ -21,6 +21,7 @@ liblitest_la_SOURCES = \
litest-keyboard.c \
litest-keyboard-razer-blackwidow.c \
litest-logitech-trackball.c \
litest-nexus4-touch-screen.c \
litest-mouse.c \
litest-mouse-roccat.c \
litest-mouse-low-dpi.c \
@ -33,6 +34,7 @@ liblitest_la_SOURCES = \
litest-synaptics-t440.c \
litest-synaptics-x1-carbon-3rd.c \
litest-trackpoint.c \
litest-touch-screen.c \
litest-wacom-bamboo-tablet.c \
litest-wacom-cintiq-tablet.c \
litest-wacom-intuos-tablet.c \

View file

@ -0,0 +1,99 @@
/*
* Copyright © 2015 Canonical, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "litest.h"
#include "litest-int.h"
static void litest_nexus4_setup(void)
{
struct litest_device *d =
litest_create_device(LITEST_NEXUS4_TOUCH_SCREEN);
litest_set_current_device(d);
}
static struct input_event down[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TOUCH_MAJOR, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static struct input_event move[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TOUCH_MAJOR, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static struct litest_device_interface interface = {
.touch_down_events = down,
.touch_move_events = move,
};
static struct input_absinfo absinfo[] = {
{ ABS_X, 0, 1500, 0, 0, 0 },
{ ABS_Y, 0, 2500, 0, 0, 0 },
{ ABS_MT_SLOT, 0, 9, 0, 0, 0 },
{ ABS_MT_POSITION_X, 0, 1500, 0, 0, 0 },
{ ABS_MT_POSITION_Y, 0, 2500, 0, 0, 0 },
{ ABS_MT_TOUCH_MAJOR, 0, 15, 1, 0, 0 },
{ ABS_MT_PRESSURE, 0, 255, 0, 0, 0 },
{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
{ .value = -1 },
};
static struct input_id input_id = {
.bustype = 0x1,
.vendor = 0x43e,
.product = 0x26,
};
static int events[] = {
EV_KEY, BTN_TOUCH,
INPUT_PROP_MAX, INPUT_PROP_DIRECT,
-1, -1
};
struct litest_test_device litest_nexus4_device = {
.type = LITEST_NEXUS4_TOUCH_SCREEN,
.features = LITEST_TOUCH|LITEST_ELLIPSE,
.shortname = "nexus4",
.setup = litest_nexus4_setup,
.interface = &interface,
.name = "Nexus 4 touch screen",
.id = &input_id,
.events = events,
.absinfo = absinfo,
};

View file

@ -289,6 +289,61 @@ START_TEST(litest_ptr_notnull_trigger_NULL)
}
END_TEST
START_TEST(ck_double_eq_and_ne)
{
ck_assert_double_eq(0.4,0.4);
ck_assert_double_eq(0.4,0.4 + 1E-6);
ck_assert_double_ne(0.4,0.4 + 1E-3);
}
END_TEST
START_TEST(ck_double_lt_gt)
{
ck_assert_double_lt(12.0,13.0);
ck_assert_double_gt(15.4,13.0);
ck_assert_double_le(12.0,12.0);
ck_assert_double_le(12.0,20.0);
ck_assert_double_ge(12.0,12.0);
ck_assert_double_ge(20.0,12.0);
}
END_TEST
START_TEST(ck_double_eq_fails)
{
ck_assert_double_eq(0.41,0.4);
}
END_TEST
START_TEST(ck_double_ne_fails)
{
ck_assert_double_ne(0.4 + 1E-7,0.4);
}
END_TEST
START_TEST(ck_double_lt_fails)
{
ck_assert_double_lt(6,5);
}
END_TEST
START_TEST(ck_double_gt_fails)
{
ck_assert_double_gt(5,6);
}
END_TEST
START_TEST(ck_double_le_fails)
{
ck_assert_double_le(6,5);
}
END_TEST
START_TEST(ck_double_ge_fails)
{
ck_assert_double_ge(5,6);
}
END_TEST
static Suite *
litest_assert_macros_suite(void)
{
@ -342,6 +397,17 @@ litest_assert_macros_suite(void)
tcase_add_test(tc, litest_ptr_notnull_notrigger);
suite_add_tcase(s, tc);
tc = tcase_create("double comparison ");
tcase_add_test(tc, ck_double_eq_and_ne);
tcase_add_test(tc, ck_double_lt_gt);
tcase_add_exit_test(tc, ck_double_eq_fails, 1);
tcase_add_exit_test(tc, ck_double_ne_fails, 1);
tcase_add_exit_test(tc, ck_double_lt_fails, 1);
tcase_add_exit_test(tc, ck_double_gt_fails, 1);
tcase_add_exit_test(tc, ck_double_le_fails, 1);
tcase_add_exit_test(tc, ck_double_ge_fails, 1);
suite_add_tcase(s, tc);
return s;
}

105
test/litest-touch-screen.c Normal file
View file

@ -0,0 +1,105 @@
/*
* Copyright © 2015 Canonical, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "litest.h"
#include "litest-int.h"
static void litest_generic_mt_setup(void)
{
struct litest_device *d =
litest_create_device(LITEST_GENERIC_MULTITOUCH_SCREEN);
litest_set_current_device(d);
}
static struct input_event down[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TOUCH_MAJOR, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TOUCH_MINOR, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_ORIENTATION, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static struct input_event move[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TOUCH_MAJOR, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TOUCH_MINOR, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_ORIENTATION, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static struct litest_device_interface interface = {
.touch_down_events = down,
.touch_move_events = move,
};
static struct input_absinfo absinfo[] = {
{ ABS_X, 0, 1500, 0, 0, 0 },
{ ABS_Y, 0, 2500, 0, 0, 0 },
{ ABS_MT_SLOT, 0, 9, 0, 0, 0 },
{ ABS_MT_POSITION_X, 0, 1500, 0, 0, 0 },
{ ABS_MT_POSITION_Y, 0, 2500, 0, 0, 0 },
{ ABS_MT_ORIENTATION, -256, 255, 0, 0, 0 },
{ ABS_MT_TOUCH_MAJOR, 0, 255, 1, 0, 0 },
{ ABS_MT_TOUCH_MINOR, 0, 255, 1, 0, 0 },
{ ABS_MT_PRESSURE, 0, 255, 0, 0, 0 },
{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
{ .value = -1 },
};
static struct input_id input_id = {
.bustype = 0x1,
.vendor = 0x0,
.product = 0x25,
};
static int events[] = {
EV_KEY, BTN_TOUCH,
INPUT_PROP_MAX, INPUT_PROP_DIRECT,
-1, -1
};
struct litest_test_device litest_generic_multitouch_screen_device = {
.type = LITEST_GENERIC_MULTITOUCH_SCREEN,
.features = LITEST_TOUCH|LITEST_ELLIPSE,
.shortname = "generic-mt",
.setup = litest_generic_mt_setup,
.interface = &interface,
.name = "generic-mt",
.id = &input_id,
.events = events,
.absinfo = absinfo,
};

View file

@ -358,6 +358,8 @@ extern struct litest_test_device litest_logitech_trackball_device;
extern struct litest_test_device litest_atmel_hover_device;
extern struct litest_test_device litest_alps_dualpoint_device;
extern struct litest_test_device litest_mouse_low_dpi_device;
extern struct litest_test_device litest_generic_multitouch_screen_device;
extern struct litest_test_device litest_nexus4_device;
extern struct litest_test_device litest_waltop_tablet_device;
struct litest_test_device* devices[] = {
@ -390,6 +392,8 @@ struct litest_test_device* devices[] = {
&litest_atmel_hover_device,
&litest_alps_dualpoint_device,
&litest_mouse_low_dpi_device,
&litest_generic_multitouch_screen_device,
&litest_nexus4_device,
&litest_waltop_tablet_device,
NULL,
};
@ -1218,10 +1222,26 @@ litest_event(struct litest_device *d, unsigned int type,
litest_assert_int_eq(ret, 0);
}
static int32_t
axis_replacement_value(struct axis_replacement *axes,
int32_t evcode)
{
struct axis_replacement *axis = axes;
while (axis->evcode != -1) {
if (axis->evcode == evcode)
return axis->value;
axis++;
}
return -1;
}
int
litest_auto_assign_value(struct litest_device *d,
const struct input_event *ev,
int slot, double x, double y,
struct axis_replacement *axes,
bool touching)
{
static int tracking_id;
@ -1248,6 +1268,10 @@ litest_auto_assign_value(struct litest_device *d,
case ABS_MT_DISTANCE:
value = touching ? 0 : 1;
break;
default:
if (axes)
value = axis_replacement_value(axes, ev->code);
break;
}
return value;
@ -1265,8 +1289,12 @@ send_btntool(struct litest_device *d)
}
static void
litest_slot_start(struct litest_device *d, unsigned int slot,
double x, double y, bool touching)
litest_slot_start(struct litest_device *d,
unsigned int slot,
double x,
double y,
struct axis_replacement *axes,
bool touching)
{
struct input_event *ev;
@ -1287,6 +1315,7 @@ litest_slot_start(struct litest_device *d, unsigned int slot,
slot,
x,
y,
axes,
touching);
litest_event(d, ev->type, ev->code, value);
@ -1295,10 +1324,22 @@ litest_slot_start(struct litest_device *d, unsigned int slot,
}
void
litest_touch_down(struct litest_device *d, unsigned int slot,
double x, double y)
litest_touch_down(struct litest_device *d,
unsigned int slot,
double x,
double y)
{
litest_slot_start(d, slot, x, y, 1);
litest_slot_start(d, slot, x, y, NULL, true);
}
void
litest_touch_down_extended(struct litest_device *d,
unsigned int slot,
double x,
double y,
struct axis_replacement *axes)
{
litest_slot_start(d, slot, x, y, axes, true);
}
void
@ -1331,6 +1372,7 @@ litest_touch_up(struct litest_device *d, unsigned int slot)
slot,
0,
0,
NULL,
false);
litest_event(d, ev->type, ev->code, value);
ev++;
@ -1338,8 +1380,12 @@ litest_touch_up(struct litest_device *d, unsigned int slot)
}
static void
litest_slot_move(struct litest_device *d, unsigned int slot,
double x, double y, bool touching)
litest_slot_move(struct litest_device *d,
unsigned int slot,
double x,
double y,
struct axis_replacement *axes,
bool touching)
{
struct input_event *ev;
@ -1355,6 +1401,7 @@ litest_slot_move(struct litest_device *d, unsigned int slot,
slot,
x,
y,
axes,
touching);
litest_event(d, ev->type, ev->code, value);
ev++;
@ -1362,10 +1409,22 @@ litest_slot_move(struct litest_device *d, unsigned int slot,
}
void
litest_touch_move(struct litest_device *d, unsigned int slot,
double x, double y)
litest_touch_move(struct litest_device *d,
unsigned int slot,
double x,
double y)
{
litest_slot_move(d, slot, x, y, true);
litest_slot_move(d, slot, x, y, NULL, true);
}
void
litest_touch_move_extended(struct litest_device *d,
unsigned int slot,
double x,
double y,
struct axis_replacement *axes)
{
litest_slot_move(d, slot, x, y, axes, true);
}
void
@ -1388,21 +1447,6 @@ litest_touch_move_to(struct litest_device *d,
litest_touch_move(d, slot, x_to, y_to);
}
static int32_t
axis_replacement_value(struct axis_replacement *axes,
int32_t evcode)
{
struct axis_replacement *axis = axes;
while (axis->evcode != -1) {
if (axis->evcode == evcode)
return axis->value;
axis++;
}
return -1;
}
static int
auto_assign_tablet_value(struct litest_device *d,
const struct input_event *ev,
@ -1500,10 +1544,12 @@ litest_touch_move_two_touches(struct litest_device *d,
}
void
litest_hover_start(struct litest_device *d, unsigned int slot,
double x, double y)
litest_hover_start(struct litest_device *d,
unsigned int slot,
double x,
double y)
{
litest_slot_start(d, slot, x, y, 0);
litest_slot_start(d, slot, x, y, NULL, 0);
}
void
@ -1532,7 +1578,7 @@ litest_hover_end(struct litest_device *d, unsigned int slot)
ev = up;
while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
int value = litest_auto_assign_value(d, ev, slot, 0, 0, 0);
int value = litest_auto_assign_value(d, ev, slot, 0, 0, NULL, false);
litest_event(d, ev->type, ev->code, value);
ev++;
}
@ -1542,7 +1588,7 @@ void
litest_hover_move(struct litest_device *d, unsigned int slot,
double x, double y)
{
litest_slot_move(d, slot, x, y, false);
litest_slot_move(d, slot, x, y, NULL, false);
}
void
@ -1754,6 +1800,24 @@ litest_event_type_str(struct libinput_event *event)
case LIBINPUT_EVENT_TOUCH_FRAME:
str = "TOUCH FRAME";
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
str = "GESTURE SWIPE START";
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
str = "GESTURE SWIPE UPDATE";
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_END:
str = "GESTURE SWIPE END";
break;
case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
str = "GESTURE PINCH START";
break;
case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
str = "GESTURE PINCH UPDATE";
break;
case LIBINPUT_EVENT_GESTURE_PINCH_END:
str = "GESTURE PINCH END";
break;
case LIBINPUT_EVENT_TABLET_AXIS:
str = "TABLET AXIS";
break;
@ -2314,11 +2378,11 @@ send_abs_xy(struct litest_device *d, double x, double y)
e.type = EV_ABS;
e.code = ABS_X;
e.value = LITEST_AUTO_ASSIGN;
val = litest_auto_assign_value(d, &e, 0, x, y, true);
val = litest_auto_assign_value(d, &e, 0, x, y, NULL, true);
litest_event(d, EV_ABS, ABS_X, val);
e.code = ABS_Y;
val = litest_auto_assign_value(d, &e, 0, x, y, true);
val = litest_auto_assign_value(d, &e, 0, x, y, NULL, true);
litest_event(d, EV_ABS, ABS_Y, val);
}
@ -2331,12 +2395,12 @@ send_abs_mt_xy(struct litest_device *d, double x, double y)
e.type = EV_ABS;
e.code = ABS_MT_POSITION_X;
e.value = LITEST_AUTO_ASSIGN;
val = litest_auto_assign_value(d, &e, 0, x, y, true);
val = litest_auto_assign_value(d, &e, 0, x, y, NULL, true);
litest_event(d, EV_ABS, ABS_MT_POSITION_X, val);
e.code = ABS_MT_POSITION_Y;
e.value = LITEST_AUTO_ASSIGN;
val = litest_auto_assign_value(d, &e, 0, x, y, true);
val = litest_auto_assign_value(d, &e, 0, x, y, NULL, true);
litest_event(d, EV_ABS, ABS_MT_POSITION_Y, val);
}

View file

@ -138,11 +138,13 @@ enum litest_device_type {
LITEST_ATMEL_HOVER = -24,
LITEST_ALPS_DUALPOINT = -25,
LITEST_MOUSE_LOW_DPI = -26,
LITEST_WACOM_BAMBOO = -27,
LITEST_WACOM_CINTIQ = -28,
LITEST_WACOM_INTUOS = -29,
LITEST_WACOM_ISDV4 = -30,
LITEST_WALTOP = -31,
LITEST_GENERIC_MULTITOUCH_SCREEN = -27,
LITEST_NEXUS4_TOUCH_SCREEN = -28,
LITEST_WACOM_BAMBOO = -29,
LITEST_WACOM_CINTIQ = -30,
LITEST_WACOM_INTUOS = -31,
LITEST_WACOM_ISDV4 = -32,
LITEST_WALTOP = -33,
};
enum litest_device_feature {
@ -164,9 +166,10 @@ enum litest_device_feature {
LITEST_ABSOLUTE = 1 << 13,
LITEST_PROTOCOL_A = 1 << 14,
LITEST_HOVER = 1 << 15,
LITEST_TABLET = 1 << 16,
LITEST_DISTANCE = 1 << 17,
LITEST_TOOL_SERIAL = 1 << 18,
LITEST_ELLIPSE = 1 << 16,
LITEST_TABLET = 1 << 17,
LITEST_DISTANCE = 1 << 18,
LITEST_TOOL_SERIAL = 1 << 19,
};
struct litest_device {
@ -298,16 +301,27 @@ void litest_event(struct litest_device *t,
int litest_auto_assign_value(struct litest_device *d,
const struct input_event *ev,
int slot, double x, double y,
struct axis_replacement *axes,
bool touching);
void litest_touch_up(struct litest_device *d, unsigned int slot);
void litest_touch_move(struct litest_device *d,
unsigned int slot,
double x,
double y);
void litest_touch_move_extended(struct litest_device *d,
unsigned int slot,
double x,
double y,
struct axis_replacement *axes);
void litest_touch_down(struct litest_device *d,
unsigned int slot,
double x,
double y);
void litest_touch_down_extended(struct litest_device *d,
unsigned int slot,
double x,
double y,
struct axis_replacement *axes);
void litest_touch_move_to(struct litest_device *d,
unsigned int slot,
double x_from, double y_from,
@ -481,4 +495,54 @@ litest_disable_tap(struct libinput_device *device)
litest_assert_int_eq(status, expected);
}
#define CK_DOUBLE_EQ_EPSILON 1E-3
#define ck_assert_double_eq(X,Y) \
do { \
double _ck_x = X; \
double _ck_y = Y; \
ck_assert_msg(fabs(_ck_x - _ck_y) < CK_DOUBLE_EQ_EPSILON, \
"Assertion '" #X " == " #Y \
"' failed: "#X"==%f, "#Y"==%f", \
_ck_x, \
_ck_y); \
} while (0)
#define ck_assert_double_ne(X,Y) \
do { \
double _ck_x = X; \
double _ck_y = Y; \
ck_assert_msg(fabs(_ck_x - _ck_y) > CK_DOUBLE_EQ_EPSILON, \
"Assertion '" #X " != " #Y \
"' failed: "#X"==%f, "#Y"==%f", \
_ck_x, \
_ck_y); \
} while (0)
#define _ck_assert_double_eq(X, OP, Y) \
do { \
double _ck_x = X; \
double _ck_y = Y; \
ck_assert_msg(_ck_x OP _ck_y || \
fabs(_ck_x - _ck_y) < CK_DOUBLE_EQ_EPSILON, \
"Assertion '" #X#OP#Y \
"' failed: "#X"==%f, "#Y"==%f", \
_ck_x, \
_ck_y); \
} while (0)
#define _ck_assert_double_ne(X, OP,Y) \
do { \
double _ck_x = X; \
double _ck_y = Y; \
ck_assert_msg(_ck_x OP _ck_y && \
fabs(_ck_x - _ck_y) > CK_DOUBLE_EQ_EPSILON, \
"Assertion '" #X#OP#Y \
"' failed: "#X"==%f, "#Y"==%f", \
_ck_x, \
_ck_y); \
} while (0)
#define ck_assert_double_lt(X, Y) _ck_assert_double_ne(X, <, Y)
#define ck_assert_double_le(X, Y) _ck_assert_double_eq(X, <=, Y)
#define ck_assert_double_gt(X, Y) _ck_assert_double_ne(X, >, Y)
#define ck_assert_double_ge(X, Y) _ck_assert_double_eq(X, >=, Y)
#endif /* LITEST_H */

View file

@ -1271,7 +1271,7 @@ START_TEST(touchpad_2fg_scroll_slow_distance)
y_move = 7.0 * y->resolution /
(y->maximum - y->minimum) * 100;
} else {
y_move = 10.0;
y_move = 20.0;
}
litest_drain_events(li);
@ -1320,7 +1320,7 @@ START_TEST(touchpad_2fg_scroll_source)
litest_drain_events(li);
test_2fg_scroll(dev, 0, 20, 0);
test_2fg_scroll(dev, 0, 30, 0);
litest_wait_for_event_of_type(li, LIBINPUT_EVENT_POINTER_AXIS, -1);
while ((event = libinput_get_event(li))) {

View file

@ -90,6 +90,24 @@ print_event_header(struct libinput_event *ev)
case LIBINPUT_EVENT_TOUCH_FRAME:
type = "TOUCH_FRAME";
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
type = "GESTURE_SWIPE_BEGIN";
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
type = "GESTURE_SWIPE_UPDATE";
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_END:
type = "GESTURE_SWIPE_END";
break;
case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
type = "GESTURE_PINCH_BEGIN";
break;
case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
type = "GESTURE_PINCH_UPDATE";
break;
case LIBINPUT_EVENT_GESTURE_PINCH_END:
type = "GESTURE_PINCH_END";
break;
case LIBINPUT_EVENT_TABLET_AXIS:
type = "TABLET_AXIS";
break;
@ -144,6 +162,9 @@ print_device_notify(struct libinput_event *ev)
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_TOUCH))
printf("t");
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_GESTURE))
printf("g");
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_TABLET))
printf("T");
@ -517,6 +538,47 @@ print_touch_event_with_coords(struct libinput_event *ev)
xmm, ymm);
}
static void
print_gesture_event_without_coords(struct libinput_event *ev)
{
struct libinput_event_gesture *t = libinput_event_get_gesture_event(ev);
int finger_count = libinput_event_gesture_get_finger_count(t);
int cancelled = 0;
enum libinput_event_type type;
type = libinput_event_get_type(ev);
if (type == LIBINPUT_EVENT_GESTURE_SWIPE_END ||
type == LIBINPUT_EVENT_GESTURE_PINCH_END)
cancelled = libinput_event_gesture_get_cancelled(t);
print_event_time(libinput_event_gesture_get_time(t));
printf("%d%s\n", finger_count, cancelled ? " cancelled" : "");
}
static void
print_gesture_event_with_coords(struct libinput_event *ev)
{
struct libinput_event_gesture *t = libinput_event_get_gesture_event(ev);
double dx = libinput_event_gesture_get_dx(t);
double dy = libinput_event_gesture_get_dy(t);
double dx_unaccel = libinput_event_gesture_get_dx_unaccelerated(t);
double dy_unaccel = libinput_event_gesture_get_dy_unaccelerated(t);
double scale = libinput_event_gesture_get_scale(t);
double angle = libinput_event_gesture_get_angle_delta(t);
print_event_time(libinput_event_gesture_get_time(t));
printf("%d %5.2f/%5.2f (%5.2f/%5.2f unaccelerated)",
libinput_event_gesture_get_finger_count(t),
dx, dy, dx_unaccel, dy_unaccel);
if (libinput_event_get_type(ev) == LIBINPUT_EVENT_GESTURE_PINCH_UPDATE)
printf(" %5.2f @ %5.2f\n", scale, angle);
else
printf("\n");
}
static int
handle_and_print_events(struct libinput *li)
{
@ -566,6 +628,24 @@ handle_and_print_events(struct libinput *li)
case LIBINPUT_EVENT_TOUCH_FRAME:
print_touch_event_without_coords(ev);
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
print_gesture_event_without_coords(ev);
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
print_gesture_event_with_coords(ev);
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_END:
print_gesture_event_without_coords(ev);
break;
case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
print_gesture_event_without_coords(ev);
break;
case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
print_gesture_event_with_coords(ev);
break;
case LIBINPUT_EVENT_GESTURE_PINCH_END:
print_gesture_event_without_coords(ev);
break;
case LIBINPUT_EVENT_TABLET_AXIS:
print_tablet_axis_event(ev);
break;

View file

@ -72,6 +72,19 @@ struct window {
/* l/m/r mouse buttons */
int l, m, r;
/* touchpad swipe */
struct {
int nfingers;
double x, y;
} swipe;
struct {
int nfingers;
double scale;
double angle;
double x, y;
} pinch;
struct libinput_device *devices[50];
};
@ -104,11 +117,48 @@ draw(GtkWidget *widget, cairo_t *cr, gpointer data)
{
struct window *w = data;
struct touch *t;
int i, offset;
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_rectangle(cr, 0, 0, w->width, w->height);
cairo_fill(cr);
/* swipe */
cairo_save(cr);
cairo_translate(cr, w->swipe.x, w->swipe.y);
for (i = 0; i < w->swipe.nfingers; i++) {
cairo_set_source_rgb(cr, .8, .8, .4);
cairo_arc(cr, (i - 2) * 40, 0, 20, 0, 2 * M_PI);
cairo_fill(cr);
}
for (i = 0; i < 4; i++) { /* 4 fg max */
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_arc(cr, (i - 2) * 40, 0, 20, 0, 2 * M_PI);
cairo_stroke(cr);
}
cairo_restore(cr);
/* pinch */
cairo_save(cr);
offset = w->pinch.scale * 100;
cairo_translate(cr, w->pinch.x, w->pinch.y);
cairo_rotate(cr, w->pinch.angle * M_PI/180.0);
if (w->pinch.nfingers > 0) {
cairo_set_source_rgb(cr, .4, .4, .8);
cairo_arc(cr, offset, -offset, 20, 0, 2 * M_PI);
cairo_arc(cr, -offset, offset, 20, 0, 2 * M_PI);
cairo_fill(cr);
}
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_arc(cr, offset, -offset, 20, 0, 2 * M_PI);
cairo_stroke(cr);
cairo_arc(cr, -offset, offset, 20, 0, 2 * M_PI);
cairo_stroke(cr);
cairo_restore(cr);
/* draw pointer sprite */
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_save(cr);
@ -186,6 +236,13 @@ map_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
w->hx = w->width/2;
w->hy = w->height/2;
w->swipe.x = w->width/2;
w->swipe.y = w->height/2;
w->pinch.scale = 1.0;
w->pinch.x = w->width/2;
w->pinch.y = w->height/2;
g_signal_connect(G_OBJECT(w->area), "draw", G_CALLBACK(draw), w);
window = gdk_event_get_window(event);
@ -428,6 +485,72 @@ handle_event_button(struct libinput_event *ev, struct window *w)
}
static void
handle_event_swipe(struct libinput_event *ev, struct window *w)
{
struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev);
int nfingers;
double dx, dy;
nfingers = libinput_event_gesture_get_finger_count(g);
switch (libinput_event_get_type(ev)) {
case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
w->swipe.nfingers = nfingers;
w->swipe.x = w->width/2;
w->swipe.y = w->height/2;
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
dx = libinput_event_gesture_get_dx(g);
dy = libinput_event_gesture_get_dy(g);
w->swipe.x += dx;
w->swipe.y += dy;
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_END:
w->swipe.nfingers = 0;
w->swipe.x = w->width/2;
w->swipe.y = w->height/2;
break;
default:
abort();
}
}
static void
handle_event_pinch(struct libinput_event *ev, struct window *w)
{
struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev);
int nfingers;
double dx, dy;
nfingers = libinput_event_gesture_get_finger_count(g);
switch (libinput_event_get_type(ev)) {
case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
w->pinch.nfingers = nfingers;
w->pinch.x = w->width/2;
w->pinch.y = w->height/2;
break;
case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
dx = libinput_event_gesture_get_dx(g);
dy = libinput_event_gesture_get_dy(g);
w->pinch.x += dx;
w->pinch.y += dy;
w->pinch.scale = libinput_event_gesture_get_scale(g);
w->pinch.angle += libinput_event_gesture_get_angle_delta(g);
break;
case LIBINPUT_EVENT_GESTURE_PINCH_END:
w->pinch.nfingers = 0;
w->pinch.x = w->width/2;
w->pinch.y = w->height/2;
w->pinch.angle = 0.0;
w->pinch.scale = 1.0;
break;
default:
abort();
}
}
static gboolean
handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data)
{
@ -473,6 +596,16 @@ handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data)
return FALSE;
}
break;
case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
case LIBINPUT_EVENT_GESTURE_SWIPE_END:
handle_event_swipe(ev, w);
break;
case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
case LIBINPUT_EVENT_GESTURE_PINCH_END:
handle_event_pinch(ev, w);
break;
case LIBINPUT_EVENT_TABLET_AXIS:
case LIBINPUT_EVENT_TABLET_PROXIMITY:
case LIBINPUT_EVENT_TABLET_BUTTON: