Merge branch 'master' into tablet-support

This commit is contained in:
Peter Hutterer 2015-06-04 12:49:11 +10:00
commit 99aa1f5dc3
47 changed files with 2977 additions and 228 deletions

79
CODING_STYLE Normal file
View file

@ -0,0 +1,79 @@
- Indentation in tabs, 8 characters wide, spaces after the tabs where
vertical alignment is required (see below)
- Max line width 80ch, do not break up printed strings though
- Break up long lines at logical groupings, one line for each logical group
int a = somelongname() +
someotherlongname();
if (a < 0 &&
(b > 20 & d < 10) &&
d != 0.0)
somelongfunctioncall(arg1,
arg2,
arg3);
- Function declarations: return type on separate line, {} on separate line,
arguments broken up as above.
static inline int
foobar(int a, int b)
{
}
void
somenamethatiswaytoolong(int a,
int b,
int c)
{
}
- /* comments only */, no // comments
- variable_name, not VariableName or variableName. same for functions.
- no typedefs of structs, enums, unions
- if it generates a compiler warning, it needs to be fixed
- if it generates a static checker warning, it needs to be fixed or
commented
- declare variables at the top, try to keep them as local as possible.
Exception: if the same variable is re-used in multiple blocks, declare it
at the top.
int a;
int c;
if (foo) {
int b;
c = get_value();
usevalue(c);
}
if (bar) {
c = get_value();
useit(c);
}
- public functions MUST be doxygen-commented, use doxygen's @foo rather than
\foo notation
- include "config.h" comes first, followed by system headers, followed by
external library headers, followed by internal headers.
sort alphabetically where it makes sense (specifically system headers)
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <libevdev/libevdev.h>
#include "libinput-private.h"

10
COPYING
View file

@ -4,7 +4,7 @@ Copyright © 2010-2012 Intel Corporation
Copyright © 2010-2011 Benjamin Franzke
Copyright © 2011-2012 Collabora, Ltd.
Copyright © 2013-2014 Jonas Ådahl
Copyright © 2013-2014 Red Hat, Inc.
Copyright © 2013-2015 Red Hat, Inc.
Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
@ -23,3 +23,11 @@ CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
libinput ships a copy of the GPL-licensed Linux kernel's linux/input.h
header file. [1] This does not make libinput GPL.
This copy is provided to provide consistent behavior regardless which kernel
version libinput is compiled against. The header is used during compilation
only, libinput does not link against GPL libraries.
[1] http://cgit.freedesktop.org/wayland/libinput/tree/include/linux/input.h

View file

@ -9,22 +9,78 @@ applications that need to directly deal with input devices.
It provides device detection, device handling, input device event processing
and abstraction so minimize the amount of custom input code the user of
libinput need to provide the common set of functionality that users expect.
Input event processing includes scaling touch coordinates, generating
pointer events from touchpads, pointer acceleration, etc.
libinput originates from weston, the Wayland reference compositor.
libinput originates from
[weston](http://cgit.freedesktop.org/wayland/weston/), the Wayland reference
compositor.
Architecture
------------
libinput is not used directly by applications, rather it is used by the
xf86-input-libinput X.Org driver or wayland compositors. The typical
software stack for a system running Wayland is:
@dotfile libinput-stack-wayland.gv
Where the Wayland compositor may be Weston, mutter, KWin, etc. Note that
Wayland encourages the use of toolkits, so the Wayland client (your
application) does not usually talk directly to the compositor but rather
employs a toolkit (e.g. GTK) to do so.
The simplified software stack for a system running X.Org is:
@dotfile libinput-stack-xorg.gv
Again, on a modern system the application does not usually talk directly to
the X server using Xlib but rather employs a toolkit to do so.
Source code
-----------
The source code of libinput can be found at:
http://cgit.freedesktop.org/wayland/libinput
For more information, visit:
For a list of current and past releases visit:
http://www.freedesktop.org/wiki/Software/libinput/
Reporting Bugs
--------------
Bugs can be filed in the libinput component of Wayland:
https://bugs.freedesktop.org/enter_bug.cgi?product=Wayland&component=libinput
Online API documentation:
Where possible, please provide an
[evemu](http://www.freedesktop.org/wiki/Evemu/) recording of the input
device and/or the event sequence in question.
Documentation
-------------
Developer API documentation:
http://wayland.freedesktop.org/libinput/doc/latest/modules.html
High-level documentation about libinput's features:
http://wayland.freedesktop.org/libinput/doc/latest/pages.html
License
-------
libinput is licensed under the MIT license.
> Permission to use, copy, modify, distribute, and sell this software and its
> documentation for any purpose is hereby granted without fee, provided that
> the above copyright notice appear in all copies and that both that copyright
> notice and this permission notice appear in supporting documentation, and
> that the name of the copyright holders not be used in advertising or
> publicity pertaining to distribution of the software without specific,
> written prior permission. The copyright holders make no representations
> about the suitability of this software for any purpose. It is provided "as
> is" without express or implied warranty.
See the [COPYING](http://cgit.freedesktop.org/wayland/libinput/tree/COPYING)
file for the full license information.
*/

View file

@ -1,7 +1,7 @@
AC_PREREQ([2.64])
m4_define([libinput_major_version], [0])
m4_define([libinput_minor_version], [15])
m4_define([libinput_minor_version], [17])
m4_define([libinput_micro_version], [0])
m4_define([libinput_version],
[libinput_major_version.libinput_minor_version.libinput_micro_version])
@ -31,7 +31,7 @@ AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
# b) If interfaces have been changed or added, but binary compatibility has
# been preserved, change to C+1:0:A+1
# c) If the interface is the same as the previous version, change to C:R+1:A
LIBINPUT_LT_VERSION=12:0:2
LIBINPUT_LT_VERSION=12:2:2
AC_SUBST(LIBINPUT_LT_VERSION)
AM_SILENT_RULES([yes])

View file

@ -13,6 +13,7 @@ header_files = \
$(srcdir)/absolute-axes.dox \
$(srcdir)/clickpad-softbuttons.dox \
$(srcdir)/device-configuration-via-udev.dox \
$(srcdir)/faqs.dox \
$(srcdir)/normalization-of-relative-motion.dox \
$(srcdir)/palm-detection.dox \
$(srcdir)/scrolling.dox \
@ -25,9 +26,16 @@ header_files = \
diagram_files = \
$(srcdir)/dot/seats-sketch.gv \
$(srcdir)/dot/seats-sketch-libinput.gv \
$(srcdir)/dot/libinput-stack-wayland.gv \
$(srcdir)/dot/libinput-stack-xorg.gv \
$(srcdir)/dot/libinput-stack-gnome.gv \
$(srcdir)/svg/software-buttons.svg \
$(srcdir)/svg/clickfinger.svg \
$(srcdir)/svg/button-scrolling.svg \
$(srcdir)/svg/edge-scrolling.svg \
$(srcdir)/svg/palm-detection.svg \
$(srcdir)/svg/tap-n-drag.svg \
$(srcdir)/svg/top-software-buttons.svg \
$(srcdir)/svg/twofinger-scrolling.svg
html/index.html: libinput.doxygen $(header_files) $(diagram_files)

View file

@ -24,15 +24,6 @@ is split in the middle to generate left or right button events on click. The
height of the button area depends on the hardware but is usually around
10mm.
@dot
digraph G {
clickpad [
shape = "record";
label = "{\nMain\nArea\n\n|{LEFT|RIGHT}}";
]
}
@enddot
Left, right and middle button events can be triggered as follows:
- if a finger is in the main area or the left button area, a click generates
left button events.
@ -40,6 +31,8 @@ Left, right and middle button events can be triggered as follows:
- if there is a finger in both the left and right button area, a click
generates middle button events.
@image html software-buttons.svg "Left, right and middle-button click with software button areas"
If fingers are down in the main area in addition to fingers in the
left or right button area, those fingers are are ignored.
A release event always releases the buttons logically down, regardless of
@ -69,9 +62,18 @@ three fingers are held down on the touchpad when a physical click is
generated. The location of the fingers does not matter and there are no
software-defined button areas.
The Xorg synaptics driver uses 30% of the touchpad dimensions as threshold,
libinput does not have this restriction. If two fingers are on the pad
while clicking, that is a two-finger click.
@image html clickfinger.svg "One, two and three-finger click with Clickfinger behavior"
On some touchpads, libinput imposes a limit on how the fingers may be placed
on the touchpad. In the most common use-case this allows for a user to
trigger a click with the thumb while leaving the pointer-moving finger on
the touchpad.
@image html clickfinger-distance.svg "Illustration of the distance detection algorithm"
In the illustration above the red area marks the proximity area around the
first finger. Since the thumb is outside of that area libinput considers the
click a single-finger click rather than a two-finger click.
Clickfinger configuration can be enabled through the
libinput_device_config_click_set_method() call. If clickfingers are

View file

@ -0,0 +1,30 @@
digraph stack
{
compound=true;
rankdir="LR";
node [
shape="box";
]
gcc -> gsettings
xf86libinput -> libinput
subgraph cluster0 {
label="X.Org";
xf86libinput [label="xf86-input-libinput"];
xserver [label="X Server"];
xserver -> xf86libinput;
}
gcc [label="gnome-control-center"];
subgraph cluster3 {
gsettings
}
gsd [label="gnome-settings-daemon"];
gsd -> gsettings
gsd -> xserver
}

View file

@ -0,0 +1,17 @@
digraph stack
{
rankdir="LR";
node [
shape="box";
]
kernel [label="Kernel"];
libinput;
compositor [label="Wayland Compositor"];
client [label="Wayland Client"];
kernel -> libinput
libinput -> compositor
compositor -> client
}

View file

@ -0,0 +1,19 @@
digraph stack
{
rankdir="LR";
node [
shape="box";
]
kernel [label="Kernel"];
libinput;
xf86libinput [label="xf86-input-libinput"];
xserver [label="X Server"];
client [label="X11 client"];
kernel -> libinput
libinput -> xf86libinput
xf86libinput -> xserver
xserver -> client
}

51
doc/faqs.dox Normal file
View file

@ -0,0 +1,51 @@
/**
@page faq FAQs - Frequently Asked Questions
Frequently asked questions about libinput.
@section faq_kinetic_scrolling Kinetic scrolling does not work
The X.Org synaptics driver implemented kinetic scrolling in the driver. It
measures the scroll speed and once the finger leaves the touchpad the driver
keeps sending scroll events for a predetermined time. This effectively
provides for kinetic scrolling without client support but triggers an
unfixable [bug](https://bugs.freedesktop.org/show_bug.cgi?id=38909): the
client cannot know that the events are from a kinetic scroll source. Scroll
events in X are always sent to the current cursor position, a movement of the
cursor after lifting the finger will send the kinetic scroll events to the
new client, something the user does not usually expect. A key event during
the kinetic scroll procedure causes side-effects such as triggering zoom.
libinput does not implement kinetic scrolling for touchpads. Instead it
provides the libinput_event_pointer_get_axis_source() function that enables
callers to implement kinetic scrolling on a per-widget basis, see @ref
scroll_sources.
@section faq_gpl Is libinput GPL-licensed?
No, libinput is MIT licensed. The Linux kernel header file linux/input.h in
libinput's tree is provded to ensure the same behavior regardless of which
kernel version libinput is built on. It does not make libinput GPL-licensed.
@section faq_config_options Where is the configuration stored?
libinput does not store configuration options, it is up to the caller to
manage these and decide which configuration option to apply to each device.
This must be done at startup, after a resume and whenever a new device is
detected.
In a GNOME X.Org stack a user would usually toggle an option in
the gnome-control-center which adjusts a gsettings entry. That change is
picked up by gnome-settings-daemon and applied to the device by adjusting
input device properties that the xf86-input-libinput driver provides.
The input device property changes map to the respective libinput
configuration options.
@dotfile libinput-stack-gnome.gv
This has an effect on the availability of configuration options: if an
option is not exposed by the intermediary, it cannot be configured by the
client. Also some configuration options that are provided by the
intermediary may not be libinput-specific configuration options.
*/

View file

@ -74,8 +74,9 @@ Notable behaviors of libinput's disable-while-typing feature:
- Some keys do not trigger the timeout, specifically some modifier keys
(Ctrl, Alt, Shift, and Fn). Actions such as Ctrl + click thus stay
responsive.
- Touches started while the touchpad is disabled do not control the cursor,
it is thus possible to rest the palm on the touchpad while typing.
- Touches started while typing do not control the cursor even after typing
has stopped, it is thus possible to rest the palm on the touchpad while
typing.
- Physical buttons work even while the touchpad is disabled. This includes
software-emulated buttons.

View file

@ -63,4 +63,23 @@ the motion events. Cross-device scrolling is not supported but
for one exception: libinput's @ref t440_support enables the use of the middle
button for button scrolling (even when the touchpad is disabled).
@section scroll_sources Scroll sources
libinput provides a pointer axis *source* for each scroll event. The
source can be obtained with the libinput_event_pointer_get_axis_source()
function and is one of **wheel**, **finger**, or **continuous**. The source
information lets a caller decide when to implement kinetic scrolling.
Usually, a caller will process events of source wheel as they come in.
For events of source finger a caller should calculate the velocity of the
scroll motion and upon finger release start a kinetic scrolling motion (i.e.
continue executing a scroll according to some friction factor).
libinput expects the caller to be in charge of widget handling, the source
information is thus enough to provide kinetic scrolling on a per-widget
basis. A caller should cancel kinetic scrolling when the pointer leaves the
current widget or when a key is pressed.
See the libinput_event_pointer_get_axis_source() for details on the
behavior of each scroll source.
See also http://who-t.blogspot.com.au/2015/03/libinput-scroll-sources.html
*/

View file

@ -0,0 +1,106 @@
<?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="89.829216mm"
height="59.06765mm"
viewBox="0 0 318.2925 209.29482"
id="svg4184"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="clickfinger-distance.svg">
<defs
id="defs4186" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="235.68795"
inkscape:cy="163.39995"
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="metadata4189">
<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(-257.99662,-299.41313)">
<rect
width="313.09872"
height="167.89594"
x="260.59351"
y="302.01001"
id="rect2858-0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.19376326;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" />
<rect
style="opacity:0.92000002;fill:#7b0000;fill-opacity:0.2983426;stroke:#000000;stroke-width:1.00100005;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4788"
width="188.57143"
height="79.285713"
x="355"
y="309.50507" />
<g
transform="matrix(0.79657897,0.11742288,-0.14814182,0.631399,276.6631,-158.96703)"
id="g3663-9-5">
<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="path2820-6-6"
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="path2824-1-1"
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="path2824-7-1-4"
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
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:1.00100005;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path2824-1-1-3"
d="m 353.70196,495.15765 c -24.01774,-7.29937 -29.0012,-10.10221 -30.51977,-10.54973 -10.67294,-3.14527 -18.27051,-5.54063 -23.77758,-13.4704 -5.50707,-7.92977 -5.34967,-20.78347 8.87612,-26.31604 14.2258,-5.53257 39.34351,8.79597 60.13061,16.16341 20.7871,7.36744 33.04563,11.44545 39.33422,13.87551 -8.10022,18.05041 -7.22129,21.15857 -10.11054,33.34117 -0.0481,0.20261 -17.87459,-5.12433 -43.93306,-13.04392 z"
sodipodi:nodetypes="sszzzcss" />
<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-3"
d="m 324.44991,483.39364 c -10.67294,-1.94747 -17.88441,-5.64478 -21.62691,-8.75386 -8.11652,-9.03765 -6.31775,-15.03428 -3.3272,-13.99784 8.90495,-0.9097 30.20384,9.01528 33.86042,10.17935 -5.80268,11.37909 -1.08919,13.70271 -8.90631,12.57235 z"
sodipodi:nodetypes="ccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

207
doc/svg/clickfinger.svg Normal file
View file

@ -0,0 +1,207 @@
<?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"
version="1.1"
width="1084.4291"
height="218.18733"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="clickfinger.svg">
<metadata
id="metadata4314">
<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>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1136"
id="namedview4312"
showgrid="false"
inkscape:zoom="0.81739538"
inkscape:cx="347.81182"
inkscape:cy="125.36322"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-page="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0">
<sodipodi:guide
position="563.61797,339.58481"
orientation="0,1"
id="guide4473" />
</sodipodi:namedview>
<defs
id="defs4" />
<g
id="g4405"
transform="matrix(0.81023703,0,0,0.6422249,-2.3181067e-6,0.47063383)">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:7.20000076;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect2858-0"
y="3.6000037"
x="3.6000032"
height="261.42856"
width="386.42856" />
<g
id="g3663-9-5"
transform="matrix(0.98314313,0.18283763,-0.18283763,0.98314313,-4.7772181,-625.20496)">
<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>
<g
id="g4394"
transform="matrix(0.81023703,0,0,0.6422249,-2.3181067e-6,0.47063383)">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:7.20000076;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect2858"
y="3.6000218"
x="475.52567"
height="261.42856"
width="386.42856" />
<g
id="g3663"
transform="matrix(0.98196551,0.12493315,-0.14261338,1.1209308,508.26203,-717.12108)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:0.94553083px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path2820"
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.00189106;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path2824"
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"
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="g3663-9"
transform="matrix(0.98314313,0.18283763,-0.18283763,0.98314313,458.58466,-619.69966)">
<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"
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"
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"
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>
<rect
width="313.09872"
height="167.89594"
x="768.41345"
y="2.3120098"
id="rect2858-7"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.19376326;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" />
<g
transform="matrix(0.79562482,0.08023518,-0.11555064,0.71988967,731.4982,-458.98163)"
id="g3663-3">
<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="path2820-0"
style="fill:none;stroke:#000000;stroke-width:0.94553083px;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="path2824-9"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.00189106;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="path2824-7-8"
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.79657897,0.11742288,-0.14814182,0.631399,697.24775,-392.41517)"
id="g3663-9-8">
<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="path2820-6-7"
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="path2824-1-6"
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="path2824-7-1-7"
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.79562482,0.08023518,-0.11555064,0.71988967,793.15605,-458.77793)"
id="g3663-3-0">
<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="path2820-0-5"
style="fill:none;stroke:#000000;stroke-width:0.94553083px;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="path2824-9-3"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.00189106;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="path2824-7-8-8"
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>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,175 @@
<?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"
version="1.1"
width="1083.7891"
height="270.02051"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="software-buttons.svg">
<metadata
id="metadata4314">
<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>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1136"
id="namedview4312"
showgrid="false"
inkscape:zoom="0.57798581"
inkscape:cx="1134.9723"
inkscape:cy="-71.183873"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-page="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0">
<sodipodi:guide
position="563.298,391.13313"
orientation="0,1"
id="guide4473" />
</sodipodi:namedview>
<defs
id="defs4" />
<g
id="g4598"
transform="translate(-0.31997204,0.28487182)">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.19376326;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect2858-0"
y="2.7826459"
x="2.9168537"
height="167.89594"
width="313.09872" />
<g
id="g3663-9-5"
transform="matrix(0.79657897,0.11742288,-0.14814182,0.631399,-118.87011,-352.11563)">
<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>
<rect
width="313.09872"
height="167.89594"
x="384.96854"
y="3.0675292"
id="rect2858"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.19376326;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" />
<rect
width="313.09872"
height="167.89594"
x="768.09351"
y="2.5968816"
id="rect2858-7"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.19376326;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" />
<g
transform="matrix(0.79657897,0.11742288,-0.14814182,0.631399,665.11943,-345.64117)"
id="g3663-9-8">
<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="path2820-6-7"
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="path2824-1-6"
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="path2824-7-1-7"
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="g4656"
transform="translate(-0.31997204,0.28487182)">
<g
id="g4639">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:0.72135597px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path2820-0-5"
d="m 970.15914,263.93369 32.58666,-97.47985 c 0,0 12.2603,-46.32869 38.5506,-33.925 20.7457,9.78778 17.4301,24.72594 16.4399,28.75425 -10.1846,41.43257 -30.105,105.84033 -30.105,105.84033 l -0.428,-0.37827 z"
sodipodi:nodetypes="ccssccc" />
<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.00144271;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path2824-9-3"
d="m 987.16367,214.84092 c 8.97014,-26.84686 16.75933,-50.19953 17.30923,-51.89481 3.8651,-11.91483 10.3862,-22.76212 16.5622,-27.54951 6.8496,-5.30946 13.8243,-5.75615 21.9108,-1.40323 3.7999,2.04543 6.0302,3.61208 8.2265,5.77836 2.5214,2.487 3.6881,4.17002 5.1008,7.35828 1.3655,3.08181 1.9391,7.11725 1.5051,10.58923 -0.597,4.77663 -10.2821,40.41668 -20.9931,77.25236 -7.7256,26.56907 -9.371,31.11182 -9.5644,31.10964 -0.1479,-0.002 -55.70067,-1.83937 -56.08626,-2.18017 -0.16723,-0.1478 6.29681,-19.93218 16.02913,-49.06015 z"
sodipodi:nodetypes="scsssscsccs" />
<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-8-8"
d="m 1008.3595,164.57667 c 3.8651,-11.91483 7.6606,-19.35039 13.8366,-24.13778 6.8495,-5.30946 12.0833,-5.57765 20.1698,-1.22474 9.3061,4.73331 12.9905,9.62271 11.9094,19.03362 -6.3459,19.3209 -6.9054,22.12042 -9.2168,26.32727 -0.1479,-0.002 -33.6651,-14.70742 -35.0296,-15.23839 -1.4035,-0.54616 -1.8884,-3.70289 -1.6694,-4.75998 z" />
</g>
</g>
<g
transform="translate(-386.56163,2.2570367)"
id="g4656-3">
<g
id="g4639-2">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:0.72135597px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path2820-0-5-6"
d="m 970.15914,263.93369 32.58666,-97.47985 c 0,0 12.2603,-46.32869 38.5506,-33.925 20.7457,9.78778 17.4301,24.72594 16.4399,28.75425 -10.1846,41.43257 -30.105,105.84033 -30.105,105.84033 l -0.428,-0.37827 z"
sodipodi:nodetypes="ccssccc" />
<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.00144271;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path2824-9-3-6"
d="m 987.16367,214.84092 c 8.97014,-26.84686 16.75933,-50.19953 17.30923,-51.89481 3.8651,-11.91483 10.3862,-22.76212 16.5622,-27.54951 6.8496,-5.30946 13.8243,-5.75615 21.9108,-1.40323 3.7999,2.04543 6.0302,3.61208 8.2265,5.77836 2.5214,2.487 3.6881,4.17002 5.1008,7.35828 1.3655,3.08181 1.9391,7.11725 1.5051,10.58923 -0.597,4.77663 -10.2821,40.41668 -20.9931,77.25236 -7.7256,26.56907 -9.371,31.11182 -9.5644,31.10964 -0.1479,-0.002 -55.70067,-1.83937 -56.08626,-2.18017 -0.16723,-0.1478 6.29681,-19.93218 16.02913,-49.06015 z"
sodipodi:nodetypes="scsssscsccs" />
<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-8-8-4"
d="m 1008.3595,164.57667 c 3.8651,-11.91483 7.6606,-19.35039 13.8366,-24.13778 6.8495,-5.30946 12.0833,-5.57765 20.1698,-1.22474 9.3061,4.73331 12.9905,9.62271 11.9094,19.03362 -6.3459,19.3209 -6.9054,22.12042 -9.2168,26.32727 -0.1479,-0.002 -33.6651,-14.70742 -35.0296,-15.23839 -1.4035,-0.54616 -1.8884,-3.70289 -1.6694,-4.75998 z" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

810
doc/svg/tap-n-drag.svg Normal file
View file

@ -0,0 +1,810 @@
<?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"
version="1.1"
width="753.05255"
height="737.14532"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="tap-n-drag.svg">
<metadata
id="metadata4314">
<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>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1136"
id="namedview4312"
showgrid="false"
inkscape:zoom="0.57798582"
inkscape:cx="926.00645"
inkscape:cy="627.63785"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-page="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0">
<sodipodi:guide
position="600.67806,858.83692"
orientation="0,1"
id="guide4473" />
</sodipodi:namedview>
<defs
id="defs4">
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker6136"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend">
<path
inkscape:connector-curvature="0"
transform="matrix(-0.8,0,0,-0.8,-10,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="path6138" />
</marker>
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="marker6132"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path6134"
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)" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker6111"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend">
<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="path6113" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="marker5983"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path5985"
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="marker5979"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5981"
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="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5429"
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="Arrow1Mstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5414"
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="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="marker5963"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5965"
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="marker5954"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5956"
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="marker5950"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:collect="always">
<path
transform="matrix(-0.8,0,0,-0.8,-10,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="path5952"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="marker5935"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5937"
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="marker5931"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend">
<path
transform="matrix(-0.8,0,0,-0.8,-10,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="path5933"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker5873"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend">
<path
transform="matrix(-0.8,0,0,-0.8,-10,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="path5875"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker5869"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lstart">
<path
transform="matrix(0.8,0,0,0.8,10,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="path5871"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="marker5772"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path5774"
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="marker5743"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:collect="always">
<path
transform="matrix(-0.8,0,0,-0.8,-10,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="path5745"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker5739"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lstart">
<path
transform="matrix(0.8,0,0,0.8,10,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="path5741"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="marker5682"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path5684"
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="marker5678"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5680"
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="marker5674"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path5676"
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="marker5668"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5670"
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="path5411"
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="path5408"
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>
<g
id="g6226"
transform="translate(5.049491e-5,0)">
<rect
transform="matrix(1,0,-0.3959173,0.91828617,0,0)"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.41992331;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect2858-0"
y="2.7099617"
x="77.243813"
height="182.83618"
width="313.09872" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:0.2983426;fill-rule:nonzero;stroke:none;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path5690"
d="m 187.13717,141.62175 c -7.23616,-20.04776 -13.54188,-37.48106 -14.01271,-38.74063 -3.30915,-8.852639 -3.7924,-18.06951 -1.22734,-23.40863 2.84478,-5.921356 8.47454,-8.88606 17.68557,-9.313448 4.32826,-0.20083 7.07051,-0.09252 10.1144,0.399498 3.49454,0.564856 5.4048,1.155131 8.35172,2.580712 2.84854,1.377989 5.55537,3.650555 7.10286,5.96335 2.12899,3.181858 13.60803,28.938928 24.88164,55.830298 8.13151,19.39637 18.05518,43.64654 17.89104,43.71977 -0.12552,0.056 -56.74536,-0.47345 -57.25799,-0.53541 -0.22232,-0.0269 -5.6782,-14.74433 -13.52919,-36.49551 z" />
<g
id="g3663-9-5"
transform="matrix(0.79657897,0.11742288,-0.14814182,0.631399,28.295757,-432.54234)">
<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>
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path5672"
d="m 166.69233,71.697127 0,37.313633"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#marker5682);marker-end:url(#marker5674)" />
</g>
<g
id="g5747"
transform="translate(37.060081,-0.29412562)">
<rect
width="313.09872"
height="182.83618"
x="400.31058"
y="3.0302601"
id="rect5718"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.41992331;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
transform="matrix(1,0,-0.3959173,0.91828617,0,0)" />
<path
d="m 510.07714,141.91588 c -7.23616,-20.04776 -13.54188,-37.48106 -14.01271,-38.74063 -3.30915,-8.852643 -3.7924,-18.069514 -1.22734,-23.408634 2.84478,-5.921356 8.47454,-8.88606 17.68557,-9.313448 4.32826,-0.20083 7.07051,-0.09252 10.1144,0.399498 3.49454,0.564856 5.4048,1.155131 8.35172,2.580712 2.84854,1.377989 5.55537,3.650555 7.10286,5.96335 2.12899,3.181858 13.60803,28.938932 24.88164,55.830302 8.13151,19.39637 18.05518,43.64654 17.89104,43.71977 -0.12552,0.056 -56.74536,-0.47345 -57.25799,-0.53541 -0.22232,-0.0269 -5.6782,-14.74433 -13.52919,-36.49551 z"
id="path5720"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:0.2983426;fill-rule:nonzero;stroke:none;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<g
transform="matrix(0.79717939,0.09917984,-0.14825348,0.53330368,349.25624,-338.55557)"
id="g5722">
<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="path5724"
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="path5726"
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="path5728"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker5743)"
d="m 489.6323,71.991253 0,37.313637"
id="path5730"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
<g
id="g6193"
transform="translate(-719.99994,240)">
<rect
width="313.09872"
height="182.83618"
x="1157.2438"
y="2.7099617"
id="rect6064"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.41992331;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
transform="matrix(1,0,-0.3959173,0.91828617,0,0)" />
<path
d="m 1204.1322,161.80782 c -7.2362,-20.04776 -13.5419,-37.48106 -14.0127,-38.74063 -3.3092,-8.85264 -3.7925,-18.06951 -1.2274,-23.40863 2.8448,-5.921356 8.4746,-8.88606 17.6856,-9.313448 4.3282,-0.20083 7.0705,-0.09252 10.1144,0.399498 3.4945,0.564856 5.4048,1.155131 8.3517,2.580712 2.8485,1.377989 5.5554,3.650555 7.1029,5.96335 2.129,3.181858 13.608,28.938928 24.8816,55.830298 8.1315,19.39637 18.0552,43.64654 17.891,43.71977 -0.1255,0.056 -56.7453,-0.47345 -57.258,-0.53541 -0.2223,-0.0269 -5.6781,-14.74433 -13.5291,-36.49551 z"
id="path6066"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:0.2983426;fill-rule:nonzero;stroke:none;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<g
transform="matrix(0.79717939,0.09917984,-0.14825348,0.53330368,1043.3113,-318.66363)"
id="g6068">
<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="path6070"
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="path6072"
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="path6074"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker6136)"
d="m 1360.3559,35.196081 -121.004,62.192402"
id="path6076"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:0.2983426;fill-rule:nonzero;stroke:none;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path6080"
d="m 1204.1322,161.80782 c -7.2362,-20.04776 -13.5419,-37.48106 -14.0127,-38.74063 -3.3092,-8.85264 -3.7925,-18.06951 -1.2274,-23.40863 2.8448,-5.921356 8.4746,-8.88606 17.6856,-9.313448 4.3282,-0.20083 7.0705,-0.09252 10.1144,0.399498 3.4945,0.564856 5.4048,1.155131 8.3517,2.580712 2.8485,1.377989 5.5554,3.650555 7.1029,5.96335 2.129,3.181858 13.608,28.938928 24.8816,55.830298 8.1315,19.39637 18.0552,43.64654 17.891,43.71977 -0.1255,0.056 -56.7453,-0.47345 -57.258,-0.53541 -0.2223,-0.0269 -5.6781,-14.74433 -13.5291,-36.49551 z" />
<g
id="g6082"
transform="matrix(0.79717939,0.09917984,-0.14825348,0.53330368,1043.3113,-318.66363)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path6084"
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="path6086"
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="path6088"
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
</g>
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path6092"
d="M 1238.5753,97.34219 C 1220.4088,41.977511 1322.8368,-6.8249094 1359.1699,33.833528"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1, 2;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker5983)" />
<g
transform="translate(397.06003,-0.29412562)"
id="g5801">
<g
id="g5776">
<rect
transform="matrix(1,0,-0.3959173,0.91828617,0,0)"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.41992331;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect5758"
y="3.0302601"
x="400.31058"
height="182.83618"
width="313.09872" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:0.2983426;fill-rule:nonzero;stroke:none;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path5760"
d="m 447.07213,162.10195 c -7.23616,-20.04776 -13.54188,-37.48106 -14.01271,-38.74063 -3.30915,-8.85264 -3.7924,-18.06951 -1.22734,-23.408634 2.84478,-5.921356 8.47454,-8.88606 17.68557,-9.313448 4.32826,-0.20083 7.07051,-0.09252 10.1144,0.399498 3.49454,0.564856 5.4048,1.155131 8.35172,2.580712 2.84854,1.377989 5.55537,3.650555 7.10286,5.96335 2.12899,3.181862 13.60803,28.938932 24.88164,55.830302 8.13151,19.39637 18.05518,43.64654 17.89104,43.71977 -0.12552,0.056 -56.74536,-0.47345 -57.25799,-0.53541 -0.22232,-0.0269 -5.6782,-14.74433 -13.52919,-36.49551 z" />
<g
id="g5762"
transform="matrix(0.79717939,0.09917984,-0.14825348,0.53330368,286.25123,-318.3695)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path5764"
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="path5766"
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="path5768"
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
</g>
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path5770"
d="M 534.17392,70.357054 482.29191,97.682609"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker5772)" />
</g>
<g
id="g5785">
<path
d="m 447.07213,162.10195 c -7.23616,-20.04776 -13.54188,-37.48106 -14.01271,-38.74063 -3.30915,-8.85264 -3.7924,-18.06951 -1.22734,-23.408634 2.84478,-5.921356 8.47454,-8.88606 17.68557,-9.313448 4.32826,-0.20083 7.07051,-0.09252 10.1144,0.399498 3.49454,0.564856 5.4048,1.155131 8.35172,2.580712 2.84854,1.377989 5.55537,3.650555 7.10286,5.96335 2.12899,3.181862 13.60803,28.938932 24.88164,55.830302 8.13151,19.39637 18.05518,43.64654 17.89104,43.71977 -0.12552,0.056 -56.74536,-0.47345 -57.25799,-0.53541 -0.22232,-0.0269 -5.6782,-14.74433 -13.52919,-36.49551 z"
id="path5789"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:0.2983426;fill-rule:nonzero;stroke:none;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<g
transform="matrix(0.79717939,0.09917984,-0.14825348,0.53330368,286.25123,-318.3695)"
id="g5791">
<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="path5793"
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="path5795"
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="path5797"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker5950)"
d="M 534.17392,70.357054 482.29191,97.682609"
id="path5799"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
</g>
</g>
<g
id="g6251"
transform="translate(-2.4752545e-5,40)">
<g
id="g6042"
transform="translate(-1042.9399,459.70587)">
<rect
width="313.09872"
height="182.83618"
x="1120.3105"
y="3.0302601"
id="rect6044"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.41992331;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
transform="matrix(1,0,-0.3959173,0.91828617,0,0)" />
<path
d="m 1167.0721,162.10195 c -7.2361,-20.04776 -13.5418,-37.48106 -14.0127,-38.74063 -3.3091,-8.85264 -3.7924,-18.06951 -1.2273,-23.408634 2.8448,-5.921356 8.4745,-8.88606 17.6856,-9.313448 4.3282,-0.20083 7.0705,-0.09252 10.1144,0.399498 3.4945,0.564856 5.4048,1.155131 8.3517,2.580712 2.8485,1.377989 5.5553,3.650555 7.1028,5.96335 2.129,3.181862 13.6081,28.938932 24.8817,55.830302 8.1315,19.39637 18.0552,43.64654 17.891,43.71977 -0.1255,0.056 -56.7453,-0.47345 -57.258,-0.53541 -0.2223,-0.0269 -5.6782,-14.74433 -13.5292,-36.49551 z"
id="path6046"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:0.2983426;fill-rule:nonzero;stroke:none;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<g
transform="matrix(0.79657897,0.11742288,-0.14814182,0.631399,1011.2357,-412.24821)"
id="g6048">
<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="path6050"
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="path6052"
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="path6054"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#marker5935)"
d="m 1149.6323,91.991253 0,37.313637"
id="path6056"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
<g
transform="translate(360.00008,460)"
id="g6235">
<rect
transform="matrix(1,0,-0.3959173,0.91828617,0,0)"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.41992331;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect6237"
y="2.7099617"
x="77.243813"
height="182.83618"
width="313.09872" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:0.2983426;fill-rule:nonzero;stroke:none;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path6239"
d="m 187.13717,141.62175 c -7.23616,-20.04776 -13.54188,-37.48106 -14.01271,-38.74063 -3.30915,-8.852639 -3.7924,-18.06951 -1.22734,-23.40863 2.84478,-5.921356 8.47454,-8.88606 17.68557,-9.313448 4.32826,-0.20083 7.07051,-0.09252 10.1144,0.399498 3.49454,0.564856 5.4048,1.155131 8.35172,2.580712 2.84854,1.377989 5.55537,3.650555 7.10286,5.96335 2.12899,3.181858 13.60803,28.938928 24.88164,55.830298 8.13151,19.39637 18.05518,43.64654 17.89104,43.71977 -0.12552,0.056 -56.74536,-0.47345 -57.25799,-0.53541 -0.22232,-0.0269 -5.6782,-14.74433 -13.52919,-36.49551 z" />
<g
id="g6241"
transform="matrix(0.79657897,0.11742288,-0.14814182,0.631399,28.295757,-432.54234)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path6243"
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="path6245"
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="path6247"
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
</g>
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path6249"
d="m 166.69233,71.697127 0,37.313633"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#marker5682);marker-end:url(#marker5674)" />
</g>
</g>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;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="138.85577"
y="212.46516"
id="text6269"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan6271"
x="138.85577"
y="212.46516"
style="font-size:22.5px">a) Tap</tspan></text>
<text
sodipodi:linespacing="125%"
id="text6273"
y="212.46516"
x="462.85577"
style="font-style:normal;font-weight:normal;font-size:40px;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
style="font-size:22.5px"
y="212.46516"
x="462.85577"
id="tspan6275"
sodipodi:role="line">b) Hold down</tspan></text>
<text
sodipodi:linespacing="125%"
id="text6277"
y="472.46515"
x="136.85577"
style="font-style:normal;font-weight:normal;font-size:40px;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
style="font-size:22.5px"
y="472.46515"
x="136.85577"
id="tspan6279"
sodipodi:role="line">c) Move</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;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="378.85577"
y="472.46515"
id="text6281"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan6283"
x="378.85577"
y="472.46515"
style="font-size:22.5px">d) Reset finger, move (optional)</tspan></text>
<text
sodipodi:linespacing="125%"
id="text6285"
y="732.46515"
x="436.85577"
style="font-style:normal;font-weight:normal;font-size:40px;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
style="font-size:22.5px"
y="732.46515"
x="436.85577"
id="tspan6287"
sodipodi:role="line">f) Tap to end (optional)</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;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="122.85577"
y="732.46515"
id="text6289"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan6291"
x="122.85577"
y="732.46515"
style="font-size:22.5px">e) Release</tspan></text>
</svg>

After

Width:  |  Height:  |  Size: 46 KiB

View file

@ -0,0 +1,213 @@
<?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"
version="1.1"
width="956.69696"
height="207.31395"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="top-software-buttons.svg">
<metadata
id="metadata4314">
<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>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1136"
id="namedview4312"
showgrid="false"
inkscape:zoom="1.1559716"
inkscape:cx="527.43768"
inkscape:cy="31.469935"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-page="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0">
<sodipodi:guide
position="126.62171,304.81443"
orientation="0,1"
id="guide4473" />
<sodipodi:guide
position="-59.334811,222.84675"
orientation="1,0"
id="guide4774" />
</sodipodi:namedview>
<defs
id="defs4" />
<g
id="g4776"
transform="translate(0,33.753604)">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.26503992;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect2858-0"
y="-28.093496"
x="2.63252"
height="199.02132"
width="271.43192" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4718"
d="m 12.201667,-12.062569 97.876283,0.09937"
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.42133546;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.77900552" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.42133546;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.77900552"
d="m 166.20167,-12.062569 97.87628,0.09937"
id="path4768"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
inkscape:connector-curvature="0"
id="path4772"
d="m 117.44622,-11.972768 41.59554,0"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.5999999;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2.6, 5.2;stroke-dashoffset:0;stroke-opacity:1" />
</g>
<g
id="g4800"
transform="translate(340,33.753604)">
<rect
width="271.43192"
height="199.02132"
x="2.63252"
y="-28.093496"
id="rect4802"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.26503992;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.42133546;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.77900552"
d="m 12.201667,-12.062569 97.876283,0.09937"
id="path4804"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4806"
d="m 166.20167,-12.062569 97.87628,0.09937"
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.42133546;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.77900552" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.5999999;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2.6, 5.2;stroke-dashoffset:0;stroke-opacity:1"
d="m 117.44622,-11.972768 41.59554,0"
id="path4808"
inkscape:connector-curvature="0" />
</g>
<g
id="g4835"
transform="matrix(0.73822306,-0.67455668,0.67455668,0.73822306,537.11787,65.759499)"
inkscape:transform-center-x="-34.439004"
inkscape:transform-center-y="45.87469">
<path
d="M 65.88204,110.10383 39.621684,21.31208 c 0,0 -15.199155,-40.682325 13.783835,-43.694695 22.870506,-2.377059 28.308474,10.4407 29.693939,13.9424019 C 97.349439,27.575979 124.86389,110.86612 124.86389,110.86612 l -0.56898,-0.0839 -58.41287,-0.67842 z"
id="path2820-6-6"
style="fill:none;stroke:#000000;stroke-width:0.72135597px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
d="M 53.156605,65.10762 C 45.92045,40.655669 39.614727,19.392552 39.143896,17.856259 35.834749,7.0588289 35.351503,-4.1828468 37.916556,-10.69489 c 2.844782,-7.222187 8.474545,-10.838191 17.685579,-11.35947 4.328257,-0.244949 7.070506,-0.112842 10.114398,0.487262 3.49454,0.688946 5.404793,1.408896 8.351719,3.147655 2.848538,1.680712 5.555366,4.452526 7.10286,7.273407 2.128991,3.8808629 13.608031,35.296377 24.881638,68.09537 8.13151,23.65746 18.05518,53.235016 17.89104,53.324326 -0.12552,0.0683 -56.745362,-0.57745 -57.257993,-0.65302 -0.222317,-0.0328 -5.678201,-17.983442 -13.529192,-44.51302 z"
id="path2824-1-1"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.00144271;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
d="M 43.316663,17.257819 C 40.007515,6.4603886 39.107932,-0.92832907 41.672985,-7.4403722 c 2.844782,-7.2221878 7.10625,-9.8851248 16.317283,-10.4064038 10.448053,-0.80798 16.246136,1.144839 20.520953,8.7461155 5.300002,17.54581 6.371244,19.9188585 6.742033,24.1763215 -0.125516,0.06829 -36.465311,4.743495 -37.907409,4.984858 -1.48335,0.248267 -3.63121,-1.903 -4.029182,-2.8027 z"
id="path2824-7-1-4"
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="translate(680,33.753604)"
id="g4810">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.26503992;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4812"
y="-28.093496"
x="2.63252"
height="199.02132"
width="271.43192" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4814"
d="m 12.201667,-12.062569 97.876283,0.09937"
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.42133546;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.77900552" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.42133546;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.77900552"
d="m 166.20167,-12.062569 97.87628,0.09937"
id="path4816"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
inkscape:connector-curvature="0"
id="path4818"
d="m 117.44622,-11.972768 41.59554,0"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.5999999;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2.6, 5.2;stroke-dashoffset:0;stroke-opacity:1" />
</g>
<g
id="g4835-3"
transform="matrix(0.85372372,-0.52072624,0.52072624,0.85372372,770.03163,57.741757)">
<path
d="M 65.88204,110.10383 39.621684,21.31208 c 0,0 -15.199155,-40.682325 13.783835,-43.694695 22.870506,-2.377059 28.308474,10.4407 29.693939,13.9424019 C 97.349439,27.575979 124.86389,110.86612 124.86389,110.86612 l -0.56898,-0.0839 -58.41287,-0.67842 z"
id="path2820-6-6-7"
style="fill:none;stroke:#000000;stroke-width:0.72135597px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
d="M 53.156605,65.10762 C 45.92045,40.655669 39.614727,19.392552 39.143896,17.856259 35.834749,7.0588289 35.351503,-4.1828468 37.916556,-10.69489 c 2.844782,-7.222187 8.474545,-10.838191 17.685579,-11.35947 4.328257,-0.244949 7.070506,-0.112842 10.114398,0.487262 3.49454,0.688946 5.404793,1.408896 8.351719,3.147655 2.848538,1.680712 5.555366,4.452526 7.10286,7.273407 2.128991,3.8808629 13.608031,35.296377 24.881638,68.09537 8.13151,23.65746 18.05518,53.235016 17.89104,53.324326 -0.12552,0.0683 -56.745362,-0.57745 -57.257993,-0.65302 -0.222317,-0.0328 -5.678201,-17.983442 -13.529192,-44.51302 z"
id="path2824-1-1-7"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.00144271;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
d="M 43.316663,17.257819 C 40.007515,6.4603886 39.107932,-0.92832907 41.672985,-7.4403722 c 2.844782,-7.2221878 7.10625,-9.8851248 16.317283,-10.4064038 10.448053,-0.80798 16.246136,1.144839 20.520953,8.7461155 5.300002,17.54581 6.371244,19.9188585 6.742033,24.1763215 -0.125516,0.06829 -36.465311,4.743495 -37.907409,4.984858 -1.48335,0.248267 -3.63121,-1.903 -4.029182,-2.8027 z"
id="path2824-7-1-4-9"
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="g4835-0"
transform="matrix(0.78868314,-0.61479989,0.61479989,0.78868314,7.9549192,61.170364)"
inkscape:transform-center-x="-31.290987"
inkscape:transform-center-y="47.742947">
<path
d="M 65.88204,110.10383 39.621684,21.31208 c 0,0 -15.199155,-40.682325 13.783835,-43.694695 22.870506,-2.377059 28.308474,10.4407 29.693939,13.9424019 C 97.349439,27.575979 124.86389,110.86612 124.86389,110.86612 l -0.56898,-0.0839 -58.41287,-0.67842 z"
id="path2820-6-6-1"
style="fill:none;stroke:#000000;stroke-width:0.72135597px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
d="M 53.156605,65.10762 C 45.92045,40.655669 39.614727,19.392552 39.143896,17.856259 35.834749,7.0588289 35.351503,-4.1828468 37.916556,-10.69489 c 2.844782,-7.222187 8.474545,-10.838191 17.685579,-11.35947 4.328257,-0.244949 7.070506,-0.112842 10.114398,0.487262 3.49454,0.688946 5.404793,1.408896 8.351719,3.147655 2.848538,1.680712 5.555366,4.452526 7.10286,7.273407 2.128991,3.8808629 13.608031,35.296377 24.881638,68.09537 8.13151,23.65746 18.05518,53.235016 17.89104,53.324326 -0.12552,0.0683 -56.745362,-0.57745 -57.257993,-0.65302 -0.222317,-0.0328 -5.678201,-17.983442 -13.529192,-44.51302 z"
id="path2824-1-1-0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.00144271;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
d="M 43.316663,17.257819 C 40.007515,6.4603886 39.107932,-0.92832907 41.672985,-7.4403722 c 2.844782,-7.2221878 7.10625,-9.8851248 16.317283,-10.4064038 10.448053,-0.80798 16.246136,1.144839 20.520953,8.7461155 5.300002,17.54581 6.371244,19.9188585 6.742033,24.1763215 -0.125516,0.06829 -36.465311,4.743495 -37.907409,4.984858 -1.48335,0.248267 -3.63121,-1.903 -4.029182,-2.8027 z"
id="path2824-7-1-4-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"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -11,21 +11,10 @@ laptops had a separate set of physical buttons for the
<a href="http://en.wikipedia.org/wiki/Pointing_stick">trackstick</a>. This
series removed these buttons, relying on a software emulation of the top
section of the touchpad. This is visually marked on the trackpad itself,
approximately like this:
and clicks can be triggered by pressing the touchpad down with a finger in
the respective area:
@dot
digraph G {
subgraph cluster_0 {
margin="0";
clickpad [
shape = "record";
color = "none";
label = "{{LLLLLLLLLL|MMMMM|RRRRRRRRR}|\n\n\n\n\n\n\n\n|{LLLLLLLL| |RRRRRRRR}}";
]
}
}
@enddot
@image html top-software-buttons.svg "Left, right and middle-button click with top software button areas"
This page only covers the top software buttons, the bottom button behavior
is covered in @ref clickpad_softbuttons "Clickpad software buttons".
@ -36,36 +25,77 @@ property.
@section t440_support_btn_size Size of the buttons
The size of the buttons matches the visual markings on this touchpad.
The width of the left and right buttons is approximately 42% of the
touchpad's width, the middle button is centered and assigned 16% of the
touchpad width.
The line of the buttons is 5mm from the top edge of the touchpad,
measurements of button presses showed that the size of the buttons needs to
be approximately 10mm high to work reliable (especially when using the
thumb to press the button).
The width of the left and right buttons is approximately 42% of the
touchpad's width, the middle button is centered and should be assigned
approximately 16% of the touchpad width.
@section t440_support_btn_behavior Button behavior
Movement in the top button area does not generate pointer movement. These
buttons are not replacement buttons for the bottom button area but have
their own behavior.
Semantically attached to the trackstick device, libinput re-routes events
from these buttons to appear through the trackstick device. The top button
areas work even if the touchpad is disabled but will be disabled when the
trackstick device is disabled.
their own behavior. Semantically attached to the trackstick device, libinput
re-routes events from these buttons to appear through the trackstick device.
If the finger starts inside the top area and moves outside the button area
the finger is treated as dead and must be lifted to generate future buttons.
Likewise, movement into the top button area does not trigger button events, a click
has to start inside this area to take effect.
@dot
digraph top_button_routing
{
rankdir="LR";
node [shape="box";]
trackstick [label="trackstick kernel device"];
touchpad [label="touchpad kernel device"];
subgraph cluster0 {
bgcolor = floralwhite
label = "libinput"
libinput_ts [label="trackstick libinput_device"
style=filled
fillcolor=white];
libinput_tp [label="touchpad libinput_device"
style=filled
fillcolor=white];
libinput_tp -> libinput_ts [constraint=false
color="red4"];
}
trackstick -> libinput_ts [arrowhead="none"]
touchpad -> libinput_tp [color="red4"]
events_tp [label="other touchpad events"];
events_topbutton [label="top sofware button events"];
libinput_tp -> events_tp [arrowhead="none"]
libinput_ts -> events_topbutton [color="red4"]
}
@enddot
The top button areas work even if the touchpad is disabled but will be
disabled when the trackstick device is disabled. If the finger starts inside
the top area and moves outside the button area the finger is treated as dead
and must be lifted to generate future buttons. Likewise, movement into the
top button area does not trigger button events, a click has to start inside
this area to take effect.
@section t440_support_identification Kernel support
The firmware on touchpads providing top software buttons is buggy and
announces wrong ranges. <a href="https://lkml.org/lkml/2014/3/7/722">Kernel
patches</a> are required; these fixes are available in kernels
3.14.1, 3.15 and later but each touchpad needs a separate fix.
The firmware on the first generation of touchpads providing top software
buttons is buggy and announces wrong ranges.
<a href="https://lkml.org/lkml/2014/3/7/722">Kernel patches</a> are required;
these fixes are available in kernels 3.14.1, 3.15 and later but each
touchpad needs a separate fix.
The October 2014 refresh of these laptops do not have this firmware bug
anymore and should work without per-device patches, though
<a href="http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=02e07492cdfae9c86e3bd21c0beec88dbcc1e9e8">this kernel commit</a> is required.
For a complete list of supported touchpads check <a
href="http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/input/mouse/synaptics.c">the

View file

@ -27,15 +27,22 @@ libinput_device_config_tap_set_enabled() for details.
libinput also supports "tap-and-drag" where a tap immediately followed by a
finger down and that finger being held down emulates a button press. Moving
the finger around can thus drag the selected item on the screen. Lifting the
finger and putting it back down immediately (i.e. within the timeout) will
continue the dragging process, so that multiple touchpad-widths of distance
can be covered easily. If two-fingers are supported by the hardware, a
second finger can be used to drag while the first is held in-place.
the finger around can thus drag the selected item on the screen.
An alternative method to end a drag process is to tap immediately after
lifting the finger. The full sequence is thus: tap, finger down, drag,
finger up, tap.
@image html tap-n-drag.svg "Tap-and-drag process"
The above diagram explains the process, a tap (a) followed by a finger held
down (b) starts the drag process and logically holds the left mouse button
down. A movement of the finger (c) will drag the selected item until the
finger is relased (e). If needed, the finger's position can be reset by
lifting and quickly setting it down again on the touchpad (d). This will be
interpreted as continuing move and is especially useful on small touchpads
or with slow pointer acceleration.
The release of the mouse buttons after the finger release (e) is triggered
by a timeout. To release the button immediately, simply tap again (f).
If two fingers are supported by the hardware, a second finger can be used to
drag while the first is held in-place.
@section tap_constraints Constraints while tapping

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2014 Red Hat, Inc.
* Copyright © 2014-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
@ -20,6 +20,8 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <stdint.h>
#include "evdev.h"

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2014 Red Hat, Inc.
* Copyright © 2014-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
@ -20,6 +20,8 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <errno.h>
#include <limits.h>
#include <math.h>
@ -392,7 +394,7 @@ tp_button_handle_event(struct tp_dispatch *tp,
enum button_event event,
uint64_t time)
{
struct libinput *libinput = tp->device->base.seat->libinput;
struct libinput *libinput = tp_libinput_context(tp);
enum button_state current = t->button.state;
switch(t->button.state) {
@ -478,7 +480,7 @@ tp_process_button(struct tp_dispatch *tp,
const struct input_event *e,
uint64_t time)
{
struct libinput *libinput = tp->device->base.seat->libinput;
struct libinput *libinput = tp_libinput_context(tp);
uint32_t mask = 1 << (e->code - BTN_LEFT);
/* Ignore other buttons on clickpads */
@ -680,7 +682,7 @@ int
tp_init_buttons(struct tp_dispatch *tp,
struct evdev_device *device)
{
struct libinput *libinput = tp->device->base.seat->libinput;
struct libinput *libinput = tp_libinput_context(tp);
struct tp_touch *t;
int width, height;
double diagonal;
@ -731,7 +733,7 @@ tp_init_buttons(struct tp_dispatch *tp,
tp_for_each_touch(tp, t) {
t->button.state = BUTTON_STATE_NONE;
libinput_timer_init(&t->button.timer,
tp->device->base.seat->libinput,
tp_libinput_context(tp),
tp_button_handle_timeout, t);
}
@ -782,6 +784,104 @@ tp_post_physical_buttons(struct tp_dispatch *tp, uint64_t time)
return 0;
}
static inline int
tp_check_clickfinger_distance(struct tp_dispatch *tp,
struct tp_touch *t1,
struct tp_touch *t2)
{
double x, y;
if (!t1 || !t2)
return 0;
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 */
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.absinfo_x->maximum -
tp->device->abs.absinfo_x->minimum;
h = tp->device->abs.absinfo_y->maximum -
tp->device->abs.absinfo_y->minimum;
return (x < w * 0.3 && y < h * 0.3) ? 1 : 0;
} else {
/* maximum spread is 40mm horiz, 20mm vert. Anything wider than that
* is probably a gesture. The y spread is small so we ignore clicks
* with thumbs at the bottom of the touchpad while the pointer
* moving finger is still on the pad */
x /= tp->device->abs.absinfo_x->resolution;
y /= tp->device->abs.absinfo_y->resolution;
return (x < 40 && y < 20) ? 1 : 0;
}
}
static uint32_t
tp_clickfinger_set_button(struct tp_dispatch *tp)
{
uint32_t button;
unsigned int nfingers = tp->nfingers_down;
struct tp_touch *t;
struct tp_touch *first = NULL,
*second = NULL,
*third = NULL;
uint32_t close_touches = 0;
if (nfingers < 2 || nfingers > 3)
goto out;
/* two or three fingers down on the touchpad. Check for distance
* between the fingers. */
tp_for_each_touch(tp, t) {
if (t->state != TOUCH_BEGIN && t->state != TOUCH_UPDATE)
continue;
if (!first)
first = t;
else if (!second)
second = t;
else if (!third) {
third = t;
break;
}
}
if (!first || !second) {
nfingers = 1;
goto out;
}
close_touches |= tp_check_clickfinger_distance(tp, first, second) << 0;
close_touches |= tp_check_clickfinger_distance(tp, second, third) << 1;
close_touches |= tp_check_clickfinger_distance(tp, first, third) << 2;
switch(__builtin_popcount(close_touches)) {
case 0: nfingers = 1; break;
case 1: nfingers = 2; break;
default: nfingers = 3; break;
}
out:
switch (nfingers) {
case 0:
case 1: button = BTN_LEFT; break;
case 2: button = BTN_RIGHT; break;
case 3: button = BTN_MIDDLE; break;
default:
button = 0;
break;
}
return button;
}
static int
tp_notify_clickpadbutton(struct tp_dispatch *tp,
uint64_t time,
@ -816,14 +916,7 @@ tp_notify_clickpadbutton(struct tp_dispatch *tp,
*/
if (tp->buttons.click_method == LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER &&
state == LIBINPUT_BUTTON_STATE_PRESSED) {
switch (tp->nfingers_down) {
case 0:
case 1: button = BTN_LEFT; break;
case 2: button = BTN_RIGHT; break;
case 3: button = BTN_MIDDLE; break;
default:
button = 0;
}
button = tp_clickfinger_set_button(tp);
tp->buttons.active = button;
if (!button)

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2014 Red Hat, Inc.
* Copyright © 2014-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
@ -20,6 +20,8 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <errno.h>
#include <limits.h>
#include <math.h>
@ -121,7 +123,7 @@ tp_edge_scroll_handle_none(struct tp_dispatch *tp,
struct tp_touch *t,
enum scroll_event event)
{
struct libinput *libinput = tp->device->base.seat->libinput;
struct libinput *libinput = tp_libinput_context(tp);
switch (event) {
case SCROLL_EVENT_TOUCH:
@ -149,7 +151,7 @@ tp_edge_scroll_handle_edge_new(struct tp_dispatch *tp,
struct tp_touch *t,
enum scroll_event event)
{
struct libinput *libinput = tp->device->base.seat->libinput;
struct libinput *libinput = tp_libinput_context(tp);
switch (event) {
case SCROLL_EVENT_TOUCH:
@ -178,7 +180,7 @@ tp_edge_scroll_handle_edge(struct tp_dispatch *tp,
struct tp_touch *t,
enum scroll_event event)
{
struct libinput *libinput = tp->device->base.seat->libinput;
struct libinput *libinput = tp_libinput_context(tp);
switch (event) {
case SCROLL_EVENT_TOUCH:
@ -209,7 +211,7 @@ tp_edge_scroll_handle_area(struct tp_dispatch *tp,
struct tp_touch *t,
enum scroll_event event)
{
struct libinput *libinput = tp->device->base.seat->libinput;
struct libinput *libinput = tp_libinput_context(tp);
switch (event) {
case SCROLL_EVENT_TOUCH:
@ -232,7 +234,7 @@ tp_edge_scroll_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum scroll_event event)
{
struct libinput *libinput = tp->device->base.seat->libinput;
struct libinput *libinput = tp_libinput_context(tp);
enum tp_edge_scroll_touch_state current = t->scroll.edge_state;
switch (current) {
@ -301,7 +303,7 @@ tp_edge_scroll_init(struct tp_dispatch *tp, struct evdev_device *device)
tp_for_each_touch(tp, t) {
t->scroll.direction = -1;
libinput_timer_init(&t->scroll.timer,
device->base.seat->libinput,
tp_libinput_context(tp),
tp_edge_scroll_handle_timeout, t);
}
@ -361,6 +363,9 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
if (!t->dirty)
continue;
if (t->palm.state != PALM_NONE)
continue;
switch (t->scroll.edge) {
case EDGE_NONE:
if (t->scroll.direction != -1) {
@ -438,6 +443,10 @@ tp_edge_scroll_stop_events(struct tp_dispatch *tp, uint64_t time)
&zero,
&zero_discrete);
t->scroll.direction = -1;
/* reset touch to area state, avoids loading the
* state machine with special case handling */
t->scroll.edge = EDGE_NONE;
t->scroll.edge_state = EDGE_SCROLL_TOUCH_STATE_AREA;
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2013 Red Hat, Inc.
* Copyright © 2013-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
@ -37,7 +37,7 @@
#define CASE_RETURN_STRING(a) case a: return #a
#define DEFAULT_TAP_TIMEOUT_PERIOD 180
#define DEFAULT_DRAG_TIMEOUT_PERIOD 500
#define DEFAULT_DRAG_TIMEOUT_PERIOD 300
#define DEFAULT_TAP_MOVE_THRESHOLD TP_MM_TO_DPI_NORMALIZED(3)
enum tap_event {
@ -147,7 +147,7 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event, uint64_t time)
{
struct libinput *libinput = tp->device->base.seat->libinput;
struct libinput *libinput = tp_libinput_context(tp);
switch (event) {
case TAP_EVENT_TOUCH:
@ -223,7 +223,7 @@ tp_tap_tapped_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event, uint64_t time)
{
struct libinput *libinput = tp->device->base.seat->libinput;
struct libinput *libinput = tp_libinput_context(tp);
switch (event) {
case TAP_EVENT_MOTION:
@ -483,7 +483,7 @@ tp_tap_multitap_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event, uint64_t time)
{
struct libinput *libinput = tp->device->base.seat->libinput;
struct libinput *libinput = tp_libinput_context(tp);
switch (event) {
case TAP_EVENT_RELEASE:
@ -576,7 +576,7 @@ tp_tap_handle_event(struct tp_dispatch *tp,
enum tap_event event,
uint64_t time)
{
struct libinput *libinput = tp->device->base.seat->libinput;
struct libinput *libinput = tp_libinput_context(tp);
enum tp_tap_state current;
current = tp->tap.state;
@ -857,7 +857,7 @@ tp_init_tap(struct tp_dispatch *tp)
tp->tap.enabled = tp_tap_default(tp->device);
libinput_timer_init(&tp->tap.timer,
tp->device->base.seat->libinput,
tp_libinput_context(tp),
tp_tap_handle_timeout, tp);
return 0;

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2014 Red Hat, Inc.
* Copyright © 2014-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
@ -237,6 +237,7 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
t->state = TOUCH_END;
t->pinned.is_pinned = false;
t->millis = time;
t->palm.time = 0;
assert(tp->nfingers_down >= 1);
tp->nfingers_down--;
tp->queued |= TOUCHPAD_EVENT_MOTION;
@ -310,6 +311,7 @@ tp_process_absolute(struct tp_dispatch *tp,
tp_new_touch(tp, t, time);
else
tp_end_sequence(tp, t, time);
break;
}
}
@ -475,12 +477,45 @@ tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t)
/* We're inside the left/right palm edge and in the northern half of
* the touchpad - this tap is a palm */
if (t->point.y < tp->palm.vert_center)
if (t->point.y < tp->palm.vert_center) {
log_debug(tp_libinput_context(tp),
"palm: palm-tap detected\n");
return true;
}
return false;
}
static int
tp_palm_detect_dwt(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
{
if (tp->dwt.keyboard_active &&
t->state == TOUCH_BEGIN) {
t->palm.state = PALM_TYPING;
t->palm.time = time;
t->palm.first = t->point;
return 1;
} else if (!tp->dwt.keyboard_active &&
t->state == TOUCH_UPDATE &&
t->palm.state == PALM_TYPING)
{
/* If a touch has started before the first or after the last
key press, release it on timeout. Benefit: a palm rested
while typing on the touchpad will be ignored, but a touch
started once we stop typing will be able to control the
pointer (alas not tap, etc.).
*/
if (t->palm.time == 0 ||
t->palm.time > tp->dwt.keyboard_last_press_time) {
t->palm.state = PALM_NONE;
log_debug(tp_libinput_context(tp),
"palm: touch released, timeout after typing\n");
}
}
return 0;
}
static void
tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
{
@ -489,13 +524,8 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
struct device_float_coords delta;
int dirs;
if (tp->sendevents.keyboard_active &&
t->state == TOUCH_BEGIN) {
t->palm.state = PALM_TYPING;
t->palm.time = time;
t->palm.first = t->point;
return;
}
if (tp_palm_detect_dwt(tp, t, time))
goto out;
/* If labelled a touch as palm, we unlabel as palm when
we move out of the palm edge zone within the timeout, provided
@ -509,6 +539,8 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_normalize_delta(tp, delta));
if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS)) {
t->palm.state = PALM_NONE;
log_debug(tp_libinput_context(tp),
"palm: touch released, out of edge zone\n");
}
}
return;
@ -530,6 +562,11 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
t->palm.state = PALM_EDGE;
t->palm.time = time;
t->palm.first = t->point;
out:
log_debug(tp_libinput_context(tp),
"palm: palm detected (%s)\n",
t->palm.state == PALM_EDGE ? "edge" : "typing");
}
static void
@ -717,7 +754,7 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time)
if (filter_motion ||
tp->sendevents.trackpoint_active ||
tp->sendevents.keyboard_active) {
tp->dwt.keyboard_active) {
tp_edge_scroll_stop_events(tp, time);
tp_gesture_stop(tp, time);
return;
@ -767,15 +804,15 @@ static void
tp_remove_sendevents(struct tp_dispatch *tp)
{
libinput_timer_cancel(&tp->sendevents.trackpoint_timer);
libinput_timer_cancel(&tp->sendevents.keyboard_timer);
libinput_timer_cancel(&tp->dwt.keyboard_timer);
if (tp->buttons.trackpoint)
libinput_device_remove_event_listener(
&tp->sendevents.trackpoint_listener);
if (tp->sendevents.keyboard)
if (tp->dwt.keyboard)
libinput_device_remove_event_listener(
&tp->sendevents.keyboard_listener);
&tp->dwt.keyboard_listener);
}
static void
@ -810,7 +847,7 @@ tp_release_fake_touches(struct tp_dispatch *tp)
static void
tp_clear_state(struct tp_dispatch *tp)
{
uint64_t now = libinput_now(tp->device->base.seat->libinput);
uint64_t now = libinput_now(tp_libinput_context(tp));
struct tp_touch *t;
/* Unroll the touchpad state.
@ -912,7 +949,41 @@ tp_keyboard_timeout(uint64_t now, void *data)
struct tp_dispatch *tp = data;
tp_tap_resume(tp, now);
tp->sendevents.keyboard_active = false;
tp->dwt.keyboard_active = false;
log_debug(tp_libinput_context(tp), "palm: keyboard timeout\n");
}
static inline bool
tp_key_ignore_for_dwt(unsigned int keycode)
{
switch (keycode) {
/* Ignore modifiers to be responsive to ctrl-click, alt-tab, etc. */
case KEY_LEFTCTRL:
case KEY_RIGHTCTRL:
case KEY_LEFTALT:
case KEY_RIGHTALT:
case KEY_LEFTSHIFT:
case KEY_RIGHTSHIFT:
case KEY_FN:
case KEY_CAPSLOCK:
case KEY_TAB:
case KEY_COMPOSE:
case KEY_RIGHTMETA:
case KEY_LEFTMETA:
return true;
default:
break;
}
/* Ignore keys not part of the "typewriter set", i.e. F-keys,
* multimedia keys, numpad, etc.
*/
if (keycode >= KEY_F1)
return true;
return false;
}
static void
@ -934,42 +1005,61 @@ tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data)
/* modifier keys don't trigger disable-while-typing so things like
* ctrl+zoom or ctrl+click are possible */
switch (libinput_event_keyboard_get_key(kbdev)) {
case KEY_LEFTCTRL:
case KEY_RIGHTCTRL:
case KEY_LEFTALT:
case KEY_RIGHTALT:
case KEY_LEFTSHIFT:
case KEY_RIGHTSHIFT:
case KEY_FN:
return;
default:
break;
}
if (tp_key_ignore_for_dwt(libinput_event_keyboard_get_key(kbdev)))
return;
if (!tp->sendevents.keyboard_active) {
if (!tp->dwt.keyboard_active) {
tp_edge_scroll_stop_events(tp, time);
tp_gesture_stop(tp, time);
tp_tap_suspend(tp, time);
tp->sendevents.keyboard_active = true;
tp->dwt.keyboard_active = true;
timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1;
} else {
timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2;
}
libinput_timer_set(&tp->sendevents.keyboard_timer,
tp->dwt.keyboard_last_press_time = time;
libinput_timer_set(&tp->dwt.keyboard_timer,
time + timeout);
}
static bool
tp_want_dwt(struct evdev_device *touchpad,
struct evdev_device *keyboard)
{
unsigned int bus_tp = libevdev_get_id_bustype(touchpad->evdev),
bus_kbd = libevdev_get_id_bustype(keyboard->evdev);
if (bus_tp == BUS_BLUETOOTH || bus_kbd == BUS_BLUETOOTH)
return false;
/* evemu will set the right bus type */
if (bus_tp == BUS_VIRTUAL || bus_kbd == BUS_VIRTUAL)
return false;
/* If the touchpad is on serio, the keyboard is too, so ignore any
other devices */
if (bus_tp == BUS_I8042 && bus_kbd != bus_tp)
return false;
/* Wacom makes touchpads, but not internal ones */
if (libevdev_get_id_vendor(touchpad->evdev) == VENDOR_ID_WACOM)
return false;
/* everything else we don't really know, so we have to assume
they go together */
return true;
}
static void
tp_interface_device_added(struct evdev_device *device,
struct evdev_device *added_device)
{
struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
unsigned int bus_tp = libevdev_get_id_bustype(device->evdev),
bus_trp = libevdev_get_id_bustype(added_device->evdev),
bus_kbd = libevdev_get_id_bustype(added_device->evdev);
bool tp_is_internal, trp_is_internal, kbd_is_internal;
bus_trp = libevdev_get_id_bustype(added_device->evdev);
bool tp_is_internal, trp_is_internal;
tp_is_internal = bus_tp != BUS_USB && bus_tp != BUS_BLUETOOTH;
trp_is_internal = bus_trp != BUS_USB && bus_trp != BUS_BLUETOOTH;
@ -985,16 +1075,19 @@ tp_interface_device_added(struct evdev_device *device,
tp_trackpoint_event, tp);
}
/* FIXME: detect external keyboard better */
kbd_is_internal = bus_tp != BUS_BLUETOOTH &&
bus_kbd == bus_tp;
if (tp_is_internal && kbd_is_internal &&
tp->sendevents.keyboard == NULL) {
if (added_device->tags & EVDEV_TAG_KEYBOARD &&
tp->dwt.keyboard == NULL &&
tp_want_dwt(device, added_device)) {
log_debug(tp_libinput_context(tp),
"palm: dwt activated with %s<->%s\n",
device->devname,
added_device->devname);
libinput_device_add_event_listener(&added_device->base,
&tp->sendevents.keyboard_listener,
&tp->dwt.keyboard_listener,
tp_keyboard_event, tp);
tp->sendevents.keyboard = added_device;
tp->sendevents.keyboard_active = false;
tp->dwt.keyboard = added_device;
tp->dwt.keyboard_active = false;
}
if (tp->sendevents.current_mode !=
@ -1023,10 +1116,10 @@ tp_interface_device_removed(struct evdev_device *device,
tp->buttons.trackpoint = NULL;
}
if (removed_device == tp->sendevents.keyboard) {
if (removed_device == tp->dwt.keyboard) {
libinput_device_remove_event_listener(
&tp->sendevents.keyboard_listener);
tp->sendevents.keyboard = NULL;
&tp->dwt.keyboard_listener);
tp->dwt.keyboard = NULL;
}
if (tp->sendevents.current_mode !=
@ -1044,9 +1137,9 @@ tp_interface_device_removed(struct evdev_device *device,
tp_resume(tp, device);
}
static void
tp_interface_tag_device(struct evdev_device *device,
struct udev_device *udev_device)
void
evdev_tag_touchpad(struct evdev_device *device,
struct udev_device *udev_device)
{
int bustype;
@ -1076,7 +1169,6 @@ static struct evdev_dispatch_interface tp_interface = {
tp_interface_device_removed,
tp_interface_device_removed, /* device_suspended, treat as remove */
tp_interface_device_added, /* device_resumed, treat as add */
tp_interface_tag_device,
NULL, /* post_added */
};
@ -1182,7 +1274,7 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal)
* and y resolution, so that a circle on the
* touchpad does not turn into an elipse on the screen.
*/
if (res_x > 1 && res_y > 1) {
if (!tp->device->abs.fake_resolution) {
tp->accel.x_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_x;
tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y;
} else {
@ -1231,7 +1323,7 @@ tp_scroll_config_scroll_method_set_method(struct libinput_device *device,
{
struct evdev_device *evdev = (struct evdev_device*)device;
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
uint64_t time = libinput_now(device->seat->libinput);
uint64_t time = libinput_now(tp_libinput_context(tp));
if (method == tp->scroll.method)
return LIBINPUT_CONFIG_STATUS_SUCCESS;
@ -1297,6 +1389,7 @@ tp_init_palmdetect(struct tp_dispatch *tp,
struct evdev_device *device)
{
int width, height;
unsigned int vendor_id;
tp->palm.right_edge = INT_MAX;
tp->palm.left_edge = INT_MIN;
@ -1307,8 +1400,13 @@ tp_init_palmdetect(struct tp_dispatch *tp,
height = abs(device->abs.absinfo_y->maximum -
device->abs.absinfo_y->minimum);
/* Apple touchpads are always big enough to warrant palm detection */
if (evdev_device_get_id_vendor(device) != VENDOR_ID_APPLE) {
vendor_id = evdev_device_get_id_vendor(device);
/* Wacom doesn't have internal touchpads,
* Apple touchpads are always big enough to warrant palm detection */
if (vendor_id == VENDOR_ID_WACOM) {
return 0;
} else if (vendor_id != VENDOR_ID_APPLE) {
/* We don't know how big the touchpad is */
if (device->abs.absinfo_x->resolution == 1)
return 0;
@ -1332,15 +1430,39 @@ tp_init_sendevents(struct tp_dispatch *tp,
struct evdev_device *device)
{
libinput_timer_init(&tp->sendevents.trackpoint_timer,
tp->device->base.seat->libinput,
tp_libinput_context(tp),
tp_trackpoint_timeout, tp);
libinput_timer_init(&tp->sendevents.keyboard_timer,
tp->device->base.seat->libinput,
libinput_timer_init(&tp->dwt.keyboard_timer,
tp_libinput_context(tp),
tp_keyboard_timeout, tp);
return 0;
}
static int
tp_sanity_check(struct tp_dispatch *tp,
struct evdev_device *device)
{
struct libevdev *evdev = device->evdev;
struct libinput *libinput = tp_libinput_context(tp);
if (!libevdev_has_event_code(evdev, EV_ABS, ABS_X))
goto error;
if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOUCH))
goto error;
if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_FINGER))
goto error;
return 0;
error:
log_bug_kernel(libinput,
"device %s failed touchpad sanity checks\n");
return -1;
}
static int
tp_init(struct tp_dispatch *tp,
struct evdev_device *device)
@ -1351,6 +1473,9 @@ tp_init(struct tp_dispatch *tp,
tp->base.interface = &tp_interface;
tp->device = device;
if (tp_sanity_check(tp, device) != 0)
return -1;
if (tp_init_slots(tp, device) != 0)
return -1;

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2014 Red Hat, Inc.
* Copyright © 2014-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
@ -32,8 +32,6 @@
#define TOUCHPAD_HISTORY_LENGTH 4
#define TOUCHPAD_MIN_SAMPLES 4
#define VENDOR_ID_APPLE 0x5ac
/* Convert mm to a distance normalized to DEFAULT_MOUSE_DPI */
#define TP_MM_TO_DPI_NORMALIZED(mm) (DEFAULT_MOUSE_DPI/25.4 * mm)
@ -282,17 +280,27 @@ struct tp_dispatch {
bool trackpoint_active;
struct libinput_event_listener trackpoint_listener;
struct libinput_timer trackpoint_timer;
} sendevents;
struct {
bool keyboard_active;
struct libinput_event_listener keyboard_listener;
struct libinput_timer keyboard_timer;
struct evdev_device *keyboard;
} sendevents;
uint64_t keyboard_last_press_time;
} dwt;
};
#define tp_for_each_touch(_tp, _t) \
for (unsigned int _i = 0; _i < (_tp)->ntouches && (_t = &(_tp)->touches[_i]); _i++)
static inline struct libinput*
tp_libinput_context(struct tp_dispatch *tp)
{
return tp->device->base.seat->libinput;
}
static inline struct normalized_coords
tp_normalize_delta(struct tp_dispatch *tp, struct device_float_coords delta)
{

View file

@ -1029,7 +1029,6 @@ static struct evdev_dispatch_interface tablet_interface = {
NULL, /* device_removed */
NULL, /* device_suspended */
NULL, /* device_resumed */
NULL, /* tag_device */
tablet_check_initial_proximity,
};

View file

@ -1,6 +1,7 @@
/*
* Copyright © 2010 Intel Corporation
* Copyright © 2013 Jonas Ådahl
* Copyright © 2013-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
@ -59,6 +60,7 @@ enum evdev_device_udev_tags {
EVDEV_UDEV_TAG_JOYSTICK = (1 << 6),
EVDEV_UDEV_TAG_ACCELEROMETER = (1 << 7),
EVDEV_UDEV_TAG_BUTTONSET = (1 << 8),
EVDEV_UDEV_TAG_POINTINGSTICK = (1 << 9),
};
struct evdev_udev_tag_match {
@ -77,6 +79,7 @@ static const struct evdev_udev_tag_match evdev_udev_tag_matches[] = {
{"ID_INPUT_TABLET_PAD", EVDEV_UDEV_TAG_BUTTONSET},
{"ID_INPUT_JOYSTICK", EVDEV_UDEV_TAG_JOYSTICK},
{"ID_INPUT_ACCELEROMETER", EVDEV_UDEV_TAG_ACCELEROMETER},
{"ID_INPUT_POINTINGSTICK", EVDEV_UDEV_TAG_POINTINGSTICK},
/* sentinel value */
{ 0 },
@ -439,6 +442,7 @@ evdev_button_scroll_button(struct evdev_device *device,
if (is_press) {
libinput_timer_set(&device->scroll.timer,
time + DEFAULT_MIDDLE_BUTTON_SCROLL_TIMEOUT);
device->scroll.button_down_time = time;
} else {
libinput_timer_cancel(&device->scroll.timer);
if (device->scroll.button_scroll_active) {
@ -448,7 +452,8 @@ evdev_button_scroll_button(struct evdev_device *device,
} else {
/* If the button is released quickly enough emit the
* button press/release events. */
evdev_pointer_notify_physical_button(device, time,
evdev_pointer_notify_physical_button(device,
device->scroll.button_down_time,
device->scroll.button,
LIBINPUT_BUTTON_STATE_PRESSED);
evdev_pointer_notify_physical_button(device, time,
@ -719,20 +724,40 @@ evdev_tag_external_mouse(struct evdev_device *device,
int bustype;
bustype = libevdev_get_id_bustype(device->evdev);
if (bustype == BUS_USB || bustype == BUS_BLUETOOTH) {
if (device->seat_caps & EVDEV_DEVICE_POINTER)
device->tags |= EVDEV_TAG_EXTERNAL_MOUSE;
}
if (bustype == BUS_USB || bustype == BUS_BLUETOOTH)
device->tags |= EVDEV_TAG_EXTERNAL_MOUSE;
}
static void
evdev_tag_trackpoint(struct evdev_device *device,
struct udev_device *udev_device)
{
if (libevdev_has_property(device->evdev, INPUT_PROP_POINTING_STICK))
if (libevdev_has_property(device->evdev,
INPUT_PROP_POINTING_STICK) ||
udev_device_get_property_value(udev_device,
"ID_INPUT_POINTINGSTICK"))
device->tags |= EVDEV_TAG_TRACKPOINT;
}
static void
evdev_tag_keyboard(struct evdev_device *device,
struct udev_device *udev_device)
{
int code;
if (!libevdev_has_event_type(device->evdev, EV_KEY))
return;
for (code = KEY_Q; code <= KEY_P; code++) {
if (!libevdev_has_event_code(device->evdev,
EV_KEY,
code))
return;
}
device->tags |= EVDEV_TAG_KEYBOARD;
}
static void
fallback_process(struct evdev_dispatch *dispatch,
struct evdev_device *device,
@ -825,14 +850,6 @@ fallback_destroy(struct evdev_dispatch *dispatch)
free(dispatch);
}
static void
fallback_tag_device(struct evdev_device *device,
struct udev_device *udev_device)
{
evdev_tag_external_mouse(device, udev_device);
evdev_tag_trackpoint(device, udev_device);
}
static int
evdev_calibration_has_matrix(struct libinput_device *libinput_device)
{
@ -883,7 +900,6 @@ struct evdev_dispatch_interface fallback_interface = {
NULL, /* device_removed */
NULL, /* device_suspended */
NULL, /* device_resumed */
fallback_tag_device,
NULL, /* post_added */
};
@ -1045,7 +1061,7 @@ evdev_scroll_get_default_method(struct libinput_device *device)
{
struct evdev_device *evdev = (struct evdev_device *)device;
if (libevdev_has_property(evdev->evdev, INPUT_PROP_POINTING_STICK))
if (evdev->tags & EVDEV_TAG_TRACKPOINT)
return LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
/* Mice without a scroll wheel but with middle button have on-button
@ -1085,14 +1101,7 @@ evdev_scroll_get_default_button(struct libinput_device *device)
{
struct evdev_device *evdev = (struct evdev_device *)device;
if (libevdev_has_property(evdev->evdev, INPUT_PROP_POINTING_STICK))
return BTN_MIDDLE;
/* A device that defaults to button scrolling defaults
to BTN_MIDDLE */
if (evdev_scroll_get_default_method(device) ==
LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN &&
libevdev_has_event_code(evdev->evdev, EV_KEY, BTN_MIDDLE))
if( libevdev_has_event_code(evdev->evdev, EV_KEY, BTN_MIDDLE))
return BTN_MIDDLE;
return 0;
@ -1412,14 +1421,6 @@ evdev_need_mtdev(struct evdev_device *device)
!libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT));
}
static void
evdev_tag_device(struct evdev_device *device)
{
if (device->dispatch->interface->tag_device)
device->dispatch->interface->tag_device(device,
device->udev_device);
}
static inline int
evdev_read_wheel_click_prop(struct evdev_device *device)
{
@ -1480,7 +1481,7 @@ evdev_read_dpi_prop(struct evdev_device *device)
* POINTINGSTICK_CONST_ACCEL value to compensate for sensitivity
* differences between models, we translate this to a fake dpi.
*/
if (libevdev_has_property(device->evdev, INPUT_PROP_POINTING_STICK))
if (device->tags & EVDEV_TAG_TRACKPOINT)
return evdev_get_trackpoint_dpi(device);
mouse_dpi = udev_device_get_property_value(device->udev_device,
@ -1820,13 +1821,14 @@ evdev_configure_device(struct evdev_device *device)
}
log_info(libinput,
"input device '%s', %s is tagged by udev as:%s%s%s%s%s%s%s%s\n",
"input device '%s', %s is tagged by udev as:%s%s%s%s%s%s%s%s%s\n",
device->devname, devnode,
udev_tags & EVDEV_UDEV_TAG_KEYBOARD ? " Keyboard" : "",
udev_tags & EVDEV_UDEV_TAG_MOUSE ? " Mouse" : "",
udev_tags & EVDEV_UDEV_TAG_TOUCHPAD ? " Touchpad" : "",
udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN ? " Touchscreen" : "",
udev_tags & EVDEV_UDEV_TAG_TABLET ? " Tablet" : "",
udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK ? " Pointingstick" : "",
udev_tags & EVDEV_UDEV_TAG_JOYSTICK ? " Joystick" : "",
udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER ? " Accelerometer" : "",
udev_tags & EVDEV_UDEV_TAG_BUTTONSET ? " Buttonset" : "");
@ -1897,10 +1899,13 @@ evdev_configure_device(struct evdev_device *device)
log_info(libinput,
"input device '%s', %s is a touchpad\n",
device->devname, devnode);
evdev_tag_touchpad(device, device->udev_device);
return device->dispatch == NULL ? -1 : 0;
}
if (udev_tags & EVDEV_UDEV_TAG_MOUSE) {
if (udev_tags & EVDEV_UDEV_TAG_MOUSE ||
udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK) {
if (libevdev_has_event_code(evdev, EV_REL, REL_X) &&
libevdev_has_event_code(evdev, EV_REL, REL_Y) &&
evdev_device_init_pointer_acceleration(
@ -1920,6 +1925,9 @@ evdev_configure_device(struct evdev_device *device)
device->scroll.natural_scrolling_enabled = true;
/* want button scrolling config option */
device->scroll.want_button = 1;
evdev_tag_external_mouse(device, device->udev_device);
evdev_tag_trackpoint(device, device->udev_device);
}
if (udev_tags & EVDEV_UDEV_TAG_KEYBOARD) {
@ -1934,6 +1942,8 @@ evdev_configure_device(struct evdev_device *device)
device->scroll.natural_scrolling_enabled = true;
device->seat_caps |= EVDEV_DEVICE_POINTER;
}
evdev_tag_keyboard(device, device->udev_device);
}
if (udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN) {
@ -2089,7 +2099,6 @@ evdev_device_create(struct libinput_seat *seat,
device->scroll.direction = 0;
device->scroll.wheel_click_angle =
evdev_read_wheel_click_prop(device);
device->dpi = evdev_read_dpi_prop(device);
device->model = evdev_read_model(device);
/* at most 5 SYN_DROPPED log-messages per 30s */
ratelimit_init(&device->syn_drop_limit, 30ULL * 1000, 5);
@ -2101,6 +2110,8 @@ evdev_device_create(struct libinput_seat *seat,
if (evdev_configure_device(device) == -1)
goto err;
device->dpi = evdev_read_dpi_prop(device);
if (device->seat_caps == 0) {
unhandled_device = 1;
goto err;
@ -2122,7 +2133,6 @@ evdev_device_create(struct libinput_seat *seat,
list_insert(seat->devices_list.prev, &device->base.link);
evdev_tag_device(device);
evdev_notify_added_device(device);
return device;

View file

@ -1,6 +1,7 @@
/*
* Copyright © 2011, 2012 Intel Corporation
* Copyright © 2013 Jonas Ådahl
* Copyright © 2013-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
@ -69,6 +70,7 @@ enum evdev_device_tags {
EVDEV_TAG_INTERNAL_TOUCHPAD = (1 << 1),
EVDEV_TAG_TRACKPOINT = (1 << 2),
EVDEV_TAG_TOUCHPAD_TRACKPOINT = (1 << 3),
EVDEV_TAG_KEYBOARD = (1 << 4),
};
enum evdev_middlebutton_state {
@ -149,6 +151,8 @@ struct evdev_device {
/* Currently enabled method, button */
enum libinput_config_scroll_method method;
uint32_t button;
uint64_t button_down_time;
/* set during device init, used at runtime to delay changes
* until all buttons are up */
enum libinput_config_scroll_method want_method;
@ -254,10 +258,6 @@ struct evdev_dispatch_interface {
void (*device_resumed)(struct evdev_device *device,
struct evdev_device *resumed_device);
/* Tag device with one of EVDEV_TAG */
void (*tag_device)(struct evdev_device *device,
struct udev_device *udev_device);
/* Called immediately after the LIBINPUT_EVENT_DEVICE_ADDED event
* was sent */
void (*post_added)(struct evdev_device *device,
@ -298,6 +298,10 @@ evdev_mt_touchpad_create(struct evdev_device *device);
struct evdev_dispatch *
evdev_tablet_create(struct evdev_device *device);
void
evdev_tag_touchpad(struct evdev_device *device,
struct udev_device *udev_device);
void
evdev_device_led_update(struct evdev_device *device, enum libinput_led leds);

View file

@ -1,6 +1,7 @@
/*
* Copyright © 2006-2009 Simon Thum
* Copyright © 2012 Jonas Ådahl
* Copyright © 2014-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
@ -77,7 +78,7 @@ filter_get_speed(struct motion_filter *filter)
*/
#define MAX_VELOCITY_DIFF 1.0 /* units/ms */
#define MOTION_TIMEOUT 300 /* (ms) */
#define MOTION_TIMEOUT 1000 /* (ms) */
#define NUM_POINTER_TRACKERS 16
struct pointer_tracker {
@ -143,6 +144,24 @@ calculate_tracker_velocity(struct pointer_tracker *tracker, uint64_t time)
return normalized_length(tracker->delta) / tdelta; /* units/ms */
}
static inline double
calculate_velocity_after_timeout(struct pointer_tracker *tracker)
{
/* First movement after timeout needs special handling.
*
* When we trigger the timeout, the last event is too far in the
* past to use it for velocity calculation across multiple tracker
* values.
*
* Use the motion timeout itself to calculate the speed rather than
* the last tracker time. This errs on the side of being too fast
* for really slow movements but provides much more useful initial
* movement in normal use-cases (pause, move, pause, move)
*/
return calculate_tracker_velocity(tracker,
tracker->time + MOTION_TIMEOUT);
}
static double
calculate_velocity(struct pointer_accelerator *accel, uint64_t time)
{
@ -162,15 +181,23 @@ calculate_velocity(struct pointer_accelerator *accel, uint64_t time)
/* Stop if too far away in time */
if (time - tracker->time > MOTION_TIMEOUT ||
tracker->time > time)
tracker->time > time) {
if (offset == 1)
result = calculate_velocity_after_timeout(tracker);
break;
}
velocity = calculate_tracker_velocity(tracker, time);
/* Stop if direction changed */
dir &= tracker->dir;
if (dir == 0)
if (dir == 0) {
/* First movement after dirchange - velocity is that
* of the last movement */
if (offset == 1)
result = velocity;
break;
velocity = calculate_tracker_velocity(tracker, time);
}
if (initial_velocity == 0.0) {
result = initial_velocity = velocity;
@ -196,17 +223,20 @@ acceleration_profile(struct pointer_accelerator *accel,
static double
calculate_acceleration(struct pointer_accelerator *accel,
void *data, double velocity, uint64_t time)
void *data,
double velocity,
double last_velocity,
uint64_t time)
{
double factor;
/* Use Simpson's rule to calculate the avarage acceleration between
* the previous motion and the most recent. */
factor = acceleration_profile(accel, data, velocity, time);
factor += acceleration_profile(accel, data, accel->last_velocity, time);
factor += acceleration_profile(accel, data, last_velocity, time);
factor += 4.0 *
acceleration_profile(accel, data,
(accel->last_velocity + velocity) / 2,
(last_velocity + velocity) / 2,
time);
factor = factor / 6.0;
@ -227,7 +257,11 @@ accelerator_filter(struct motion_filter *filter,
feed_trackers(accel, unaccelerated, time);
velocity = calculate_velocity(accel, time);
accel_value = calculate_acceleration(accel, data, velocity, time);
accel_value = calculate_acceleration(accel,
data,
velocity,
accel->last_velocity,
time);
accelerated.x = accel_value * unaccelerated->x;
accelerated.y = accel_value * unaccelerated->y;

View file

@ -1,5 +1,6 @@
/*
* Copyright © 2012 Jonas Ådahl
* Copyright © 2014-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided

View file

@ -1,5 +1,6 @@
/*
* Copyright © 2013 Jonas Ådahl
* Copyright © 2013-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided

View file

@ -1,6 +1,7 @@
/*
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2011 Intel Corporation
* Copyright © 2013-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that

View file

@ -1,5 +1,6 @@
/*
* Copyright © 2008 Kristian Høgsberg
* Copyright © 2013-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -25,11 +26,16 @@
#include <unistd.h>
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "libinput.h"
#define VENDOR_ID_APPLE 0x5ac
#define VENDOR_ID_WACOM 0x56a
void
set_logging_enabled(int enabled);
@ -250,6 +256,38 @@ matrix_to_farray6(const struct matrix *m, float out[6])
out[5] = m->val[1][2];
}
/**
* Simple wrapper for asprintf that ensures the passed in-pointer is set
* to NULL upon error.
* The standard asprintf() call does not guarantee the passed in pointer
* will be NULL'ed upon failure, whereas this wrapper does.
*
* @param strp pointer to set to newly allocated string.
* This pointer should be passed to free() to release when done.
* @param fmt the format string to use for printing.
* @return The number of bytes printed (excluding the null byte terminator)
* upon success or -1 upon failure. In the case of failure the pointer is set
* to NULL.
*/
static inline int
xasprintf(char **strp, const char *fmt, ...)
LIBINPUT_ATTRIBUTE_PRINTF(2, 3);
static inline int
xasprintf(char **strp, const char *fmt, ...)
{
int rc = 0;
va_list args;
va_start(args, fmt);
rc = vasprintf(strp, fmt, args);
va_end(args);
if ((rc == -1) && strp)
*strp = NULL;
return rc;
}
enum ratelimit_state {
RATELIMIT_EXCEEDED,
RATELIMIT_THRESHOLD,

View file

@ -1,5 +1,6 @@
/*
* Copyright © 2013 Jonas Ådahl
* Copyright © 2013-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided

View file

@ -1,5 +1,6 @@
/*
* Copyright © 2013 Jonas Ådahl
* Copyright © 2013-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2013 Red Hat, Inc.
* Copyright © 2013-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2014 Red Hat, Inc.
* Copyright © 2014-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
@ -20,6 +20,8 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <inttypes.h>

View file

@ -1,5 +1,6 @@
/*
* Copyright © 2013 Intel Corporation
* Copyright © 2013-2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided

View file

@ -69,7 +69,7 @@ START_TEST(device_sendevents_config_touchpad)
expected = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
/* The wacom devices in the test suite are external */
if (libevdev_get_id_vendor(dev->evdev) != 0x56a) /* wacom */
if (libevdev_get_id_vendor(dev->evdev) != VENDOR_ID_WACOM)
expected |=
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;

View file

@ -84,8 +84,10 @@ litest_backtrace_get_lineno(const char *executable,
char *s;
unsigned int i;
if (!cwd[0])
getcwd(cwd, sizeof(cwd));
if (!cwd[0]) {
if (getcwd(cwd, sizeof(cwd)) == NULL)
cwd[0] = 0; /* contents otherwise undefined. */
}
sprintf (buffer,
ADDR2LINE " -C -e %s -i %lx",
@ -383,7 +385,17 @@ static struct list all_tests;
static void
litest_reload_udev_rules(void)
{
system("udevadm control --reload-rules");
int ret = system("udevadm control --reload-rules");
if (ret == -1) {
litest_abort_msg("Failed to execute: udevadm");
} else if (WIFEXITED(ret)) {
if (WEXITSTATUS(ret))
litest_abort_msg("udevadm failed with %d",
WEXITSTATUS(ret));
} else if (WIFSIGNALED(ret)) {
litest_abort_msg("udevadm terminated with signal %d",
WTERMSIG(ret));
}
}
static int
@ -648,6 +660,7 @@ _litest_add_ranged_for_device(const char *name,
{
struct suite *s;
struct litest_test_device **dev = devices;
bool device_filtered = false;
assert(type < LITEST_NO_DEVICE);
@ -662,8 +675,10 @@ _litest_add_ranged_for_device(const char *name,
s = get_suite(name);
for (; *dev; dev++) {
if (filter_device &&
fnmatch(filter_device, (*dev)->shortname, 0) != 0)
fnmatch(filter_device, (*dev)->shortname, 0) != 0) {
device_filtered = true;
continue;
}
if ((*dev)->type == type) {
litest_add_tcase_for_device(s,
@ -675,7 +690,9 @@ _litest_add_ranged_for_device(const char *name,
}
}
litest_abort_msg("Invalid test device type");
/* only abort if no filter was set, that's a bug */
if (!device_filtered)
litest_abort_msg("Invalid test device type");
}
static int
@ -879,7 +896,7 @@ litest_init_udev_rules(struct litest_test_device *dev)
ck_abort_msg("Failed to create udev rules directory (%s)\n",
strerror(errno));
rc = asprintf(&path,
rc = xasprintf(&path,
"%s/%s%s.rules",
UDEV_RULES_D,
UDEV_RULE_PREFIX,
@ -985,6 +1002,32 @@ litest_restore_log_handler(struct libinput *libinput)
libinput_log_set_handler(libinput, litest_log_handler);
}
static inline void
litest_wait_for_udev(int fd)
{
struct udev *udev;
struct udev_device *device;
struct stat st;
int loop_count = 0;
litest_assert_int_ge(fstat(fd, &st), 0);
udev = udev_new();
device = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
litest_assert_ptr_notnull(device);
while (device && !udev_device_get_property_value(device, "ID_INPUT")) {
loop_count++;
litest_assert_int_lt(loop_count, 300);
udev_device_unref(device);
msleep(2);
device = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
}
udev_device_unref(device);
udev_unref(udev);
}
struct litest_device *
litest_add_device_with_overrides(struct libinput *libinput,
enum litest_device_type which,
@ -1012,6 +1055,8 @@ litest_add_device_with_overrides(struct libinput *libinput,
rc = libevdev_new_from_fd(fd, &d->evdev);
litest_assert_int_eq(rc, 0);
litest_wait_for_udev(fd);
d->libinput = libinput;
d->libinput_device = libinput_path_add_device(d->libinput, path);
litest_assert(d->libinput_device != NULL);
@ -1097,6 +1142,11 @@ litest_delete_device(struct litest_device *d)
free(d->private);
memset(d,0, sizeof(*d));
free(d);
/* zzz so udev can catch up with things, so we don't accidentally open
* an old device in the next test and then get all upset when things blow
* up */
msleep(10);
}
void
@ -2115,6 +2165,18 @@ litest_timeout_middlebutton(void)
msleep(70);
}
void
litest_timeout_dwt_short(void)
{
msleep(220);
}
void
litest_timeout_dwt_long(void)
{
msleep(520);
}
void
litest_push_event_frame(struct litest_device *dev)
{

View file

@ -416,6 +416,8 @@ void litest_timeout_buttonscroll(void);
void litest_timeout_edgescroll(void);
void litest_timeout_finger_switch(void);
void litest_timeout_middlebutton(void);
void litest_timeout_dwt_short(void);
void litest_timeout_dwt_long(void);
void litest_push_event_frame(struct litest_device *dev);
void litest_pop_event_frame(struct litest_device *dev);

View file

@ -586,7 +586,8 @@ START_TEST(touch_initial_state)
{
struct litest_device *dev;
struct libinput *libinput1, *libinput2;
struct libinput_event *ev1, *ev2;
struct libinput_event *ev1 = NULL;
struct libinput_event *ev2 = NULL;
struct libinput_event_touch *t1, *t2;
struct libinput_device *device1, *device2;
int axis = _i; /* looped test */

View file

@ -1808,6 +1808,51 @@ START_TEST(touchpad_2fg_clickfinger)
}
END_TEST
START_TEST(touchpad_2fg_clickfinger_distance)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
libinput_device_config_click_set_method(dev->libinput_device,
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
litest_drain_events(li);
litest_touch_down(dev, 0, 90, 50);
litest_touch_down(dev, 1, 10, 50);
litest_event(dev, EV_KEY, BTN_LEFT, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
litest_event(dev, EV_KEY, BTN_LEFT, 0);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
litest_touch_up(dev, 0);
litest_touch_up(dev, 1);
litest_assert_button_event(li,
BTN_LEFT,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_assert_button_event(li,
BTN_LEFT,
LIBINPUT_BUTTON_STATE_RELEASED);
litest_assert_empty_queue(li);
litest_touch_down(dev, 0, 50, 5);
litest_touch_down(dev, 1, 50, 95);
litest_event(dev, EV_KEY, BTN_LEFT, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
litest_event(dev, EV_KEY, BTN_LEFT, 0);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
litest_touch_up(dev, 0);
litest_touch_up(dev, 1);
litest_assert_button_event(li,
BTN_LEFT,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_assert_button_event(li,
BTN_LEFT,
LIBINPUT_BUTTON_STATE_RELEASED);
}
END_TEST
START_TEST(touchpad_clickfinger_to_area_method)
{
struct litest_device *dev = litest_current_device();
@ -2636,7 +2681,7 @@ START_TEST(clickpad_topsoftbuttons_clickfinger)
litest_assert_empty_queue(li);
litest_touch_down(dev, 0, 90, 5);
litest_touch_down(dev, 1, 10, 5);
litest_touch_down(dev, 1, 80, 5);
litest_event(dev, EV_KEY, BTN_LEFT, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
litest_event(dev, EV_KEY, BTN_LEFT, 0);
@ -2959,7 +3004,7 @@ START_TEST(touchpad_edge_scroll)
litest_touch_up(dev, 0);
libinput_dispatch(li);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 10);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 4);
litest_assert_empty_queue(li);
litest_touch_down(dev, 0, 99, 80);
@ -2967,7 +3012,7 @@ START_TEST(touchpad_edge_scroll)
litest_touch_up(dev, 0);
libinput_dispatch(li);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, -10);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, -4);
litest_assert_empty_queue(li);
litest_touch_down(dev, 0, 20, 99);
@ -2975,7 +3020,7 @@ START_TEST(touchpad_edge_scroll)
litest_touch_up(dev, 0);
libinput_dispatch(li);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, 10);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, 4);
litest_assert_empty_queue(li);
litest_touch_down(dev, 0, 70, 99);
@ -2983,7 +3028,7 @@ START_TEST(touchpad_edge_scroll)
litest_touch_up(dev, 0);
libinput_dispatch(li);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, -10);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, -4);
litest_assert_empty_queue(li);
}
END_TEST
@ -3065,7 +3110,7 @@ START_TEST(touchpad_edge_scroll_no_motion)
litest_touch_up(dev, 0);
libinput_dispatch(li);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 5);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 4);
litest_assert_empty_queue(li);
}
END_TEST
@ -3198,9 +3243,13 @@ static int
touchpad_has_palm_detect_size(struct litest_device *dev)
{
double width, height;
unsigned int vendor;
int rc;
if (libinput_device_get_id_vendor(dev->libinput_device) == 0x5ac) /* Apple */
vendor = libinput_device_get_id_vendor(dev->libinput_device);
if (vendor == VENDOR_ID_WACOM)
return 0;
if (vendor == VENDOR_ID_APPLE)
return 1;
rc = libinput_device_get_size(dev->libinput_device, &width, &height);
@ -4590,6 +4639,434 @@ START_TEST(touchpad_initial_state)
}
END_TEST
static inline bool
has_disable_while_typing(struct litest_device *device)
{
if (libevdev_get_id_vendor(device->evdev) == VENDOR_ID_WACOM)
return false;
return true;
}
START_TEST(touchpad_dwt)
{
struct litest_device *touchpad = litest_current_device();
struct litest_device *keyboard;
struct libinput *li = touchpad->libinput;
if (!has_disable_while_typing(touchpad))
return;
keyboard = litest_add_device(li, LITEST_KEYBOARD);
libinput_device_config_tap_set_enabled(touchpad->libinput_device,
LIBINPUT_CONFIG_TAP_DISABLED);
litest_drain_events(li);
litest_keyboard_key(keyboard, KEY_A, true);
litest_keyboard_key(keyboard, KEY_A, false);
libinput_dispatch(li);
litest_touch_down(touchpad, 0, 50, 50);
litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1);
litest_touch_up(touchpad, 0);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
/* within timeout - no events */
litest_touch_down(touchpad, 0, 50, 50);
litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1);
litest_touch_up(touchpad, 0);
litest_assert_empty_queue(li);
litest_timeout_dwt_short();
libinput_dispatch(li);
/* after timeout - motion events*/
litest_touch_down(touchpad, 0, 50, 50);
litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1);
litest_touch_up(touchpad, 0);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
litest_delete_device(keyboard);
}
END_TEST
START_TEST(touchpad_dwt_enable_touch)
{
struct litest_device *touchpad = litest_current_device();
struct litest_device *keyboard;
struct libinput *li = touchpad->libinput;
if (!has_disable_while_typing(touchpad))
return;
keyboard = litest_add_device(li, LITEST_KEYBOARD);
libinput_device_config_tap_set_enabled(touchpad->libinput_device,
LIBINPUT_CONFIG_TAP_DISABLED);
litest_drain_events(li);
litest_keyboard_key(keyboard, KEY_A, true);
litest_keyboard_key(keyboard, KEY_A, false);
libinput_dispatch(li);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
/* finger down after last key event, but
we're still within timeout - no events */
msleep(10);
litest_touch_down(touchpad, 0, 50, 50);
litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1);
litest_assert_empty_queue(li);
litest_timeout_dwt_short();
libinput_dispatch(li);
/* same touch after timeout - motion events*/
litest_touch_move_to(touchpad, 0, 70, 50, 50, 50, 10, 1);
litest_touch_up(touchpad, 0);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
litest_delete_device(keyboard);
}
END_TEST
START_TEST(touchpad_dwt_touch_hold)
{
struct litest_device *touchpad = litest_current_device();
struct litest_device *keyboard;
struct libinput *li = touchpad->libinput;
if (!has_disable_while_typing(touchpad))
return;
keyboard = litest_add_device(li, LITEST_KEYBOARD);
libinput_device_config_tap_set_enabled(touchpad->libinput_device,
LIBINPUT_CONFIG_TAP_DISABLED);
litest_drain_events(li);
litest_keyboard_key(keyboard, KEY_A, true);
msleep(1); /* make sure touch starts after key press */
litest_touch_down(touchpad, 0, 50, 50);
litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
/* touch still down - no events */
litest_keyboard_key(keyboard, KEY_A, false);
libinput_dispatch(li);
litest_touch_move_to(touchpad, 0, 70, 50, 30, 50, 5, 1);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
/* touch still down - no events */
litest_timeout_dwt_short();
libinput_dispatch(li);
litest_touch_move_to(touchpad, 0, 30, 50, 50, 50, 5, 1);
litest_touch_up(touchpad, 0);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
litest_delete_device(keyboard);
}
END_TEST
START_TEST(touchpad_dwt_key_hold)
{
struct litest_device *touchpad = litest_current_device();
struct litest_device *keyboard;
struct libinput *li = touchpad->libinput;
if (!has_disable_while_typing(touchpad))
return;
keyboard = litest_add_device(li, LITEST_KEYBOARD);
libinput_device_config_tap_set_enabled(touchpad->libinput_device,
LIBINPUT_CONFIG_TAP_DISABLED);
litest_drain_events(li);
litest_keyboard_key(keyboard, KEY_A, true);
libinput_dispatch(li);
litest_touch_down(touchpad, 0, 50, 50);
litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1);
litest_touch_up(touchpad, 0);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
litest_keyboard_key(keyboard, KEY_A, false);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
litest_delete_device(keyboard);
}
END_TEST
START_TEST(touchpad_dwt_type)
{
struct litest_device *touchpad = litest_current_device();
struct litest_device *keyboard;
struct libinput *li = touchpad->libinput;
int i;
if (!has_disable_while_typing(touchpad))
return;
keyboard = litest_add_device(li, LITEST_KEYBOARD);
libinput_device_config_tap_set_enabled(touchpad->libinput_device,
LIBINPUT_CONFIG_TAP_DISABLED);
litest_drain_events(li);
for (i = 0; i < 5; i++) {
litest_keyboard_key(keyboard, KEY_A, true);
litest_keyboard_key(keyboard, KEY_A, false);
libinput_dispatch(li);
}
litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
litest_touch_down(touchpad, 0, 50, 50);
litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1);
litest_touch_up(touchpad, 0);
litest_assert_empty_queue(li);
litest_timeout_dwt_long();
libinput_dispatch(li);
litest_touch_down(touchpad, 0, 50, 50);
litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1);
litest_touch_up(touchpad, 0);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
litest_delete_device(keyboard);
}
END_TEST
START_TEST(touchpad_dwt_type_short_timeout)
{
struct litest_device *touchpad = litest_current_device();
struct litest_device *keyboard;
struct libinput *li = touchpad->libinput;
int i;
if (!has_disable_while_typing(touchpad))
return;
keyboard = litest_add_device(li, LITEST_KEYBOARD);
libinput_device_config_tap_set_enabled(touchpad->libinput_device,
LIBINPUT_CONFIG_TAP_DISABLED);
litest_drain_events(li);
for (i = 0; i < 5; i++) {
litest_keyboard_key(keyboard, KEY_A, true);
litest_keyboard_key(keyboard, KEY_A, false);
libinput_dispatch(li);
}
litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
litest_touch_down(touchpad, 0, 50, 50);
litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1);
litest_touch_up(touchpad, 0);
litest_assert_empty_queue(li);
litest_timeout_dwt_short();
libinput_dispatch(li);
litest_touch_down(touchpad, 0, 50, 50);
litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1);
litest_touch_up(touchpad, 0);
litest_assert_empty_queue(li);
litest_delete_device(keyboard);
}
END_TEST
START_TEST(touchpad_dwt_tap)
{
struct litest_device *touchpad = litest_current_device();
struct litest_device *keyboard;
struct libinput *li = touchpad->libinput;
if (!has_disable_while_typing(touchpad))
return;
keyboard = litest_add_device(li, LITEST_KEYBOARD);
libinput_device_config_tap_set_enabled(touchpad->libinput_device,
LIBINPUT_CONFIG_TAP_ENABLED);
litest_drain_events(li);
litest_keyboard_key(keyboard, KEY_A, true);
libinput_dispatch(li);
litest_touch_down(touchpad, 0, 50, 50);
litest_touch_up(touchpad, 0);
litest_keyboard_key(keyboard, KEY_A, false);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
litest_timeout_dwt_short();
litest_touch_down(touchpad, 0, 50, 50);
litest_touch_up(touchpad, 0);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON);
litest_delete_device(keyboard);
}
END_TEST
START_TEST(touchpad_dwt_tap_drag)
{
struct litest_device *touchpad = litest_current_device();
struct litest_device *keyboard;
struct libinput *li = touchpad->libinput;
if (!has_disable_while_typing(touchpad))
return;
keyboard = litest_add_device(li, LITEST_KEYBOARD);
libinput_device_config_tap_set_enabled(touchpad->libinput_device,
LIBINPUT_CONFIG_TAP_ENABLED);
litest_drain_events(li);
litest_keyboard_key(keyboard, KEY_A, true);
libinput_dispatch(li);
msleep(1); /* make sure touch starts after key press */
litest_touch_down(touchpad, 0, 50, 50);
litest_touch_up(touchpad, 0);
litest_touch_down(touchpad, 0, 50, 50);
litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1);
litest_keyboard_key(keyboard, KEY_A, false);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
litest_timeout_dwt_short();
libinput_dispatch(li);
litest_touch_move_to(touchpad, 0, 70, 50, 50, 50, 5, 1);
litest_touch_up(touchpad, 0);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
litest_delete_device(keyboard);
}
END_TEST
START_TEST(touchpad_dwt_click)
{
struct litest_device *touchpad = litest_current_device();
struct litest_device *keyboard;
struct libinput *li = touchpad->libinput;
if (!has_disable_while_typing(touchpad))
return;
keyboard = litest_add_device(li, LITEST_KEYBOARD);
libinput_device_config_tap_set_enabled(touchpad->libinput_device,
LIBINPUT_CONFIG_TAP_DISABLED);
litest_drain_events(li);
litest_keyboard_key(keyboard, KEY_A, true);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
litest_touch_down(touchpad, 0, 50, 50);
litest_button_click(touchpad, BTN_LEFT, true);
litest_button_click(touchpad, BTN_LEFT, false);
libinput_dispatch(li);
litest_touch_up(touchpad, 0);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON);
litest_keyboard_key(keyboard, KEY_A, false);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
litest_delete_device(keyboard);
}
END_TEST
START_TEST(touchpad_dwt_edge_scroll)
{
struct litest_device *touchpad = litest_current_device();
struct litest_device *keyboard;
struct libinput *li = touchpad->libinput;
if (!has_disable_while_typing(touchpad))
return;
enable_edge_scroll(touchpad);
keyboard = litest_add_device(li, LITEST_KEYBOARD);
litest_drain_events(li);
litest_keyboard_key(keyboard, KEY_A, true);
litest_keyboard_key(keyboard, KEY_A, false);
litest_keyboard_key(keyboard, KEY_A, true);
litest_keyboard_key(keyboard, KEY_A, false);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
litest_touch_down(touchpad, 0, 99, 20);
libinput_dispatch(li);
litest_timeout_edgescroll();
libinput_dispatch(li);
litest_assert_empty_queue(li);
/* edge scroll timeout is 300ms atm, make sure we don't accidentally
exit the DWT timeout */
litest_keyboard_key(keyboard, KEY_A, true);
litest_keyboard_key(keyboard, KEY_A, false);
libinput_dispatch(li);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
litest_touch_move_to(touchpad, 0, 99, 20, 99, 80, 60, 10);
libinput_dispatch(li);
litest_assert_empty_queue(li);
litest_touch_move_to(touchpad, 0, 99, 80, 99, 20, 60, 10);
litest_touch_up(touchpad, 0);
libinput_dispatch(li);
litest_assert_empty_queue(li);
litest_delete_device(keyboard);
}
END_TEST
START_TEST(touchpad_dwt_edge_scroll_interrupt)
{
struct litest_device *touchpad = litest_current_device();
struct litest_device *keyboard;
struct libinput *li = touchpad->libinput;
struct libinput_event_pointer *stop_event;
if (!has_disable_while_typing(touchpad))
return;
enable_edge_scroll(touchpad);
keyboard = litest_add_device(li, LITEST_KEYBOARD);
litest_drain_events(li);
litest_touch_down(touchpad, 0, 99, 20);
libinput_dispatch(li);
litest_timeout_edgescroll();
litest_touch_move_to(touchpad, 0, 99, 20, 99, 30, 10, 10);
libinput_dispatch(li);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS);
litest_keyboard_key(keyboard, KEY_A, true);
litest_keyboard_key(keyboard, KEY_A, false);
litest_keyboard_key(keyboard, KEY_A, true);
litest_keyboard_key(keyboard, KEY_A, false);
/* scroll stop event */
litest_wait_for_event(li);
stop_event = litest_is_axis_event(libinput_get_event(li),
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
libinput_event_destroy(libinput_event_pointer_get_base_event(stop_event));
litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
litest_timeout_dwt_long();
/* Known bad behavior: a touch starting to edge-scroll before dwt
* kicks in will stop to scroll but be recognized as normal
* pointer-moving touch once the timeout expires. We'll fix that
* when we need to.
*/
litest_touch_move_to(touchpad, 0, 99, 30, 99, 80, 10, 5);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
litest_delete_device(keyboard);
}
END_TEST
void
litest_setup_tests(void)
{
@ -4652,6 +5129,7 @@ litest_setup_tests(void)
litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY);
litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger_no_touch, LITEST_CLICKPAD, LITEST_ANY);
litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY);
litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger_distance, LITEST_CLICKPAD, LITEST_ANY);
litest_add("touchpad:clickfinger", touchpad_clickfinger_to_area_method, LITEST_CLICKPAD, LITEST_ANY);
litest_add("touchpad:clickfinger",
touchpad_clickfinger_to_area_method_while_down, LITEST_CLICKPAD, LITEST_ANY);
@ -4738,4 +5216,16 @@ litest_setup_tests(void)
litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_no_trackpoint, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS);
litest_add_ranged("touchpad:state", touchpad_initial_state, LITEST_TOUCHPAD, LITEST_ANY, &axis_range);
litest_add("touchpad:dwt", touchpad_dwt, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:dwt", touchpad_dwt_enable_touch, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:dwt", touchpad_dwt_touch_hold, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:dwt", touchpad_dwt_key_hold, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:dwt", touchpad_dwt_type, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:dwt", touchpad_dwt_type_short_timeout, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:dwt", touchpad_dwt_tap, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:dwt", touchpad_dwt_tap_drag, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:dwt", touchpad_dwt_click, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:dwt", touchpad_dwt_edge_scroll, LITEST_TOUCHPAD, LITEST_CLICKPAD);
litest_add("touchpad:dwt", touchpad_dwt_edge_scroll_interrupt, LITEST_TOUCHPAD, LITEST_CLICKPAD);
}

View file

@ -35,15 +35,34 @@ START_TEST(trackpoint_middlebutton)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
struct libinput_event_pointer *ptrev;
uint64_t ptime, rtime;
litest_drain_events(li);
/* A quick middle button click should get reported normally */
litest_button_click(dev, BTN_MIDDLE, 1);
msleep(2);
litest_button_click(dev, BTN_MIDDLE, 0);
litest_assert_button_event(li, BTN_MIDDLE, 1);
litest_assert_button_event(li, BTN_MIDDLE, 0);
litest_wait_for_event(li);
event = libinput_get_event(li);
ptrev = litest_is_button_event(event,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
ptime = libinput_event_pointer_get_time(ptrev);
libinput_event_destroy(event);
event = libinput_get_event(li);
ptrev = litest_is_button_event(event,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
rtime = libinput_event_pointer_get_time(ptrev);
libinput_event_destroy(event);
ck_assert_int_lt(ptime, rtime);
litest_assert_empty_queue(li);
}

View file

@ -13,9 +13,9 @@ libshared_la_CFLAGS = $(LIBEVDEV_CFLAGS)
libshared_la_LIBADD = $(LIBEVDEV_LIBS)
event_debug_SOURCES = event-debug.c
event_debug_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS)
event_debug_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS) $(LIBEVDEV_LIBS)
event_debug_LDFLAGS = -no-install
event_debug_CFLAGS = $(LIBUDEV_CFLAGS)
event_debug_CFLAGS = $(LIBUDEV_CFLAGS) $(LIBEVDEV_CFLAGS)
ptraccel_debug_SOURCES = ptraccel-debug.c
ptraccel_debug_LDADD = ../src/libfilter.la

View file

@ -34,6 +34,7 @@
#include <sys/ioctl.h>
#include <libinput.h>
#include <libevdev/libevdev.h>
#include "shared.h"
@ -205,11 +206,17 @@ print_key_event(struct libinput_event *ev)
{
struct libinput_event_keyboard *k = libinput_event_get_keyboard_event(ev);
enum libinput_key_state state;
uint32_t key;
const char *keyname;
print_event_time(libinput_event_keyboard_get_time(k));
state = libinput_event_keyboard_get_key_state(k);
printf("%d %s\n",
libinput_event_keyboard_get_key(k),
key = libinput_event_keyboard_get_key(k);
keyname = libevdev_event_code_get_name(EV_KEY, key);
printf("%s (%d) %s\n",
keyname ? keyname : "???",
key,
state == LIBINPUT_KEY_STATE_PRESSED ? "pressed" : "released");
}

View file

@ -120,17 +120,17 @@ calibration_default(struct libinput_device *device)
float calibration[6];
if (!libinput_device_config_calibration_has_matrix(device)) {
asprintf(&str, "n/a");
xasprintf(&str, "n/a");
return str;
}
if (libinput_device_config_calibration_get_default_matrix(device,
calibration) == 0) {
asprintf(&str, "identity matrix");
xasprintf(&str, "identity matrix");
return str;
}
asprintf(&str,
xasprintf(&str,
"%.2f %.2f %.2f %.2f %.2f %.2f",
calibration[0],
calibration[1],
@ -150,13 +150,13 @@ scroll_defaults(struct libinput_device *device)
scroll_methods = libinput_device_config_scroll_get_methods(device);
if (scroll_methods == LIBINPUT_CONFIG_SCROLL_NO_SCROLL) {
asprintf(&str, "none");
xasprintf(&str, "none");
return str;
}
method = libinput_device_config_scroll_get_default_method(device);
asprintf(&str,
xasprintf(&str,
"%s%s%s%s%s%s",
(method == LIBINPUT_CONFIG_SCROLL_2FG) ? "*" : "",
(scroll_methods & LIBINPUT_CONFIG_SCROLL_2FG) ? "two-finger " : "",
@ -176,12 +176,12 @@ click_defaults(struct libinput_device *device)
click_methods = libinput_device_config_click_get_methods(device);
if (click_methods == LIBINPUT_CONFIG_CLICK_METHOD_NONE) {
asprintf(&str, "none");
xasprintf(&str, "none");
return str;
}
method = libinput_device_config_click_get_default_method(device);
asprintf(&str,
xasprintf(&str,
"%s%s%s%s",
(method == LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "*" : "",
(click_methods & LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "button-areas " : "",