Merge branch 'master' into tablet-support

This commit is contained in:
Peter Hutterer 2015-07-24 10:56:05 +10:00
commit a7bd84a7ee
45 changed files with 11641 additions and 3808 deletions

View file

@ -1,7 +1,7 @@
AC_PREREQ([2.64])
m4_define([libinput_major_version], [0])
m4_define([libinput_minor_version], [19])
m4_define([libinput_minor_version], [20])
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=13:0:3
LIBINPUT_LT_VERSION=14:0:4
AC_SUBST(LIBINPUT_LT_VERSION)
AM_SILENT_RULES([yes])

View file

@ -17,12 +17,14 @@ header_files = \
$(srcdir)/gestures.dox \
$(srcdir)/normalization-of-relative-motion.dox \
$(srcdir)/palm-detection.dox \
$(srcdir)/page-hierarchy.dox \
$(srcdir)/scrolling.dox \
$(srcdir)/seats.dox \
$(srcdir)/t440-support.dox \
$(srcdir)/tablet-support.dox \
$(srcdir)/tapping.dox \
$(srcdir)/test-suite.dox
$(srcdir)/test-suite.dox \
$(srcdir)/tools.dox
diagram_files = \
$(srcdir)/dot/seats-sketch.gv \
@ -38,11 +40,18 @@ diagram_files = \
$(srcdir)/svg/pinch-gestures.svg \
$(srcdir)/svg/swipe-gestures.svg \
$(srcdir)/svg/tap-n-drag.svg \
$(srcdir)/svg/thumb-detection.svg \
$(srcdir)/svg/top-software-buttons.svg \
$(srcdir)/svg/touchscreen-gestures.svg \
$(srcdir)/svg/twofinger-scrolling.svg
html/index.html: libinput.doxygen $(header_files) $(diagram_files)
style_files = \
style/header.html \
style/footer.html \
style/customdoxygen.css \
style/bootstrap.css
html/index.html: libinput.doxygen $(header_files) $(diagram_files) $(style_files)
$(AM_V_GEN)(cat $<; \
echo "INPUT = $(header_files)"; \
) | $(DOXYGEN) -
@ -51,8 +60,11 @@ clean-local:
$(AM_V_at)rm -rf html
doc_src= $(shell find html -type f -printf "html/%P\n" 2>/dev/null)
EXTRA_DIST += $(builddir)/html/index.html $(doc_src) $(diagram_files) $(header_files)
EXTRA_DIST += $(builddir)/html/index.html \
$(doc_src) \
$(diagram_files) \
$(header_files) \
$(style_files)
endif
# make sure doc was built before running dist

File diff suppressed because it is too large Load diff

34
doc/page-hierarchy.dox Normal file
View file

@ -0,0 +1,34 @@
/**
@page touchpads Touchpads
- @subpage scrolling
- @subpage clickpad_softbuttons
- @subpage tapping
- @subpage gestures
- @subpage palm_detection
- @subpage t440_support
@page touchscreens Touchscreens
- @subpage absolute_axes
@page pointers Mice, Trackballs, etc.
- @subpage motion_normalization
@page general General setup
- @subpage udev_config
- @subpage seats
@page misc Users
- @subpage faq
- @subpage tools
@page developers Developers
- @subpage test-suite
- @subpage tools
*/

View file

@ -80,4 +80,32 @@ Notable behaviors of libinput's disable-while-typing feature:
- Physical buttons work even while the touchpad is disabled. This includes
software-emulated buttons.
@section thumb-detection Thumb detection
Many users rest their thumb on the touchpad while using the index finger to
move the finger around. For clicks, often the thumb is used rather than the
finger. The thumb should otherwise be ignored as a touch, i.e. it should not
count towards @ref clickfinger and it should not cause a single-finger
movement to trigger @ref twofinger_scrolling.
libinput uses two triggers for thumb detection: pressure and
location. A touch exceeding a pressure threshold is considered a thumb if it
is within the thumb detection zone.
@note "Pressure" on touchpads is synonymous with "contact area", a large
touch surface area has a higher pressure and thus hints at a thumb or palm
touching the surface.
Pressure readings are unreliable at the far bottom of the touchpad as a
thumb hanging mostly off the touchpad will have a small surface area.
libinput has a definitive thumb zone where any touch is considered a resting
thumb.
@image html thumb-detection.svg
The picture above shows the two detection areas. In the larger (light red)
area, a touch is labelled as thumb when it exceeds a device-specific
pressure threshold. In the lower (dark red) area, a touch is labelled as
thumb if it remains in that area for a time without moving outside.
*/

229
doc/style/LICENSE Normal file
View file

@ -0,0 +1,229 @@
These licenses apply to the doxygen documentation HTML style only. They do
not apply or affect libinput itself.
Apache: https://github.com/Velron/doxygen-bootstrapped/
MIT: https://bootswatch.com/paper/bootstrap.css
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
The MIT License (MIT)
Copyright (c) 2011-2015 Twitter, Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

7500
doc/style/bootstrap.css vendored Normal file

File diff suppressed because it is too large Load diff

255
doc/style/customdoxygen.css Normal file
View file

@ -0,0 +1,255 @@
h1, .h1, h2, .h2, h3, .h3{
font-weight: 200 !important;
}
#navrow1, #navrow2, #navrow3, #navrow4, #navrow5{
border-bottom: 1px solid #EEEEEE;
}
.adjust-right {
margin-left: 30px !important;
font-size: 1.15em !important;
}
.navbar{
border: 0px solid #222 !important;
}
/* Sticky footer styles
-------------------------------------------------- */
html,
body {
height: 100%;
/* The html and body elements cannot have any padding or margin. */
}
/* Wrapper for page content to push down footer */
#wrap {
min-height: 100%;
height: auto;
/* Negative indent footer by its height */
margin: 0 auto -60px;
/* Pad bottom by footer height */
padding: 0 0 60px;
}
/* Set the fixed height of the footer here */
#footer {
font-size: 0.9em;
padding: 8px 0px;
background-color: #f5f5f5;
}
.footer-row {
line-height: 44px;
}
#footer > .container {
padding-left: 15px;
padding-right: 15px;
}
.footer-follow-icon {
margin-left: 3px;
text-decoration: none !important;
}
.footer-follow-icon img {
width: 20px;
}
.footer-link {
padding-top: 5px;
display: inline-block;
color: #999999;
text-decoration: none;
}
.footer-copyright {
text-align: center;
}
@media (min-width: 992px) {
.footer-row {
text-align: left;
}
.footer-icons {
text-align: right;
}
}
@media (max-width: 991px) {
.footer-row {
text-align: center;
}
.footer-icons {
text-align: center;
}
}
/* DOXYGEN Code Styles
----------------------------------- */
a.qindex {
font-weight: bold;
}
a.qindexHL {
font-weight: bold;
background-color: #9CAFD4;
color: #ffffff;
border: 1px double #869DCA;
}
.contents a.qindexHL:visited {
color: #ffffff;
}
a.code, a.code:visited, a.line, a.line:visited {
color: #4665A2;
}
a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited {
color: #4665A2;
}
/* @end */
dl.el {
margin-left: -1cm;
}
pre.fragment {
border: 1px solid #C4CFE5;
background-color: #FBFCFD;
padding: 4px 6px;
margin: 4px 8px 4px 2px;
overflow: auto;
word-wrap: break-word;
font-size: 9pt;
line-height: 125%;
font-family: monospace, fixed;
font-size: 105%;
}
div.fragment {
padding: 4px 6px;
margin: 4px 8px 4px 2px;
border: 1px solid #C4CFE5;
}
div.line {
font-family: monospace, fixed;
font-size: 13px;
min-height: 13px;
line-height: 1.0;
text-wrap: unrestricted;
white-space: -moz-pre-wrap; /* Moz */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
white-space: pre-wrap; /* CSS3 */
word-wrap: break-word; /* IE 5.5+ */
text-indent: -53px;
padding-left: 53px;
padding-bottom: 0px;
margin: 0px;
-webkit-transition-property: background-color, box-shadow;
-webkit-transition-duration: 0.5s;
-moz-transition-property: background-color, box-shadow;
-moz-transition-duration: 0.5s;
-ms-transition-property: background-color, box-shadow;
-ms-transition-duration: 0.5s;
-o-transition-property: background-color, box-shadow;
-o-transition-duration: 0.5s;
transition-property: background-color, box-shadow;
transition-duration: 0.5s;
}
div.line.glow {
background-color: cyan;
box-shadow: 0 0 10px cyan;
}
span.lineno {
padding-right: 4px;
text-align: right;
border-right: 2px solid #0F0;
background-color: #E8E8E8;
white-space: pre;
}
span.lineno a {
background-color: #D8D8D8;
}
span.lineno a:hover {
background-color: #C8C8C8;
}
div.groupHeader {
margin-left: 16px;
margin-top: 12px;
font-weight: bold;
}
div.groupText {
margin-left: 16px;
font-style: italic;
}
/* @group Code Colorization */
span.keyword {
color: #008000
}
span.keywordtype {
color: #604020
}
span.keywordflow {
color: #e08000
}
span.comment {
color: #800000
}
span.preprocessor {
color: #806020
}
span.stringliteral {
color: #002080
}
span.charliteral {
color: #008080
}
span.vhdldigit {
color: #ff00ff
}
span.vhdlchar {
color: #000000
}
span.vhdlkeyword {
color: #700070
}
span.vhdllogic {
color: #ff0000
}
blockquote {
background-color: #F7F8FB;
border-left: 2px solid #9CAFD4;
margin: 0 24px 0 4px;
padding: 0 12px 0 16px;
}

26
doc/style/footer.html Normal file
View file

@ -0,0 +1,26 @@
<!-- HTML footer for doxygen 1.8.8-->
<!-- start footer part -->
<!--BEGIN GENERATE_TREEVIEW-->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
<ul>
$navpath
<li class="footer">$generatedby
<a href="http://www.doxygen.org/index.html">
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/></a> $doxygenversion </li>
</ul>
</div>
<!--END GENERATE_TREEVIEW-->
</div>
</div>
</div>
</div>
</div>
<!--BEGIN !GENERATE_TREEVIEW-->
<hr class="footer"/><address class="footer"><small>
$generatedby &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/>
</a> $doxygenversion
</small></address>
<!--END !GENERATE_TREEVIEW-->
</body>
</html>

42
doc/style/header.html Normal file
View file

@ -0,0 +1,42 @@
<!-- HTML header for doxygen 1.8.8-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- For Mobile Devices -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<!--<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>-->
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
<link href="bootstrap.css" rel="stylesheet" type="text/css" />
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
<script type="text/javascript" src="doxy-boot.js"></script>
</head>
<body>
<nav class="navbar navbar-default" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand">$projectname $projectnumber</a>
</div>
</div>
</nav>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div class="content" id="content">
<div class="container">
<div class="row">
<div class="col-sm-12 panel panel-default" style="padding-bottom: 15px;">
<div style="margin-bottom: 15px;">
<!-- end header part -->

116
doc/svg/thumb-detection.svg Normal file
View file

@ -0,0 +1,116 @@
<?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="thumb-detection.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.979899"
inkscape:cx="270.39655"
inkscape:cy="139.75035"
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 />
</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:0.97031647;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4788"
width="307.88782"
height="45.628574"
x="262.8418"
y="421.0347" />
<rect
y="445.40848"
x="262.68912"
height="21.407471"
width="308.19318"
id="rect4149"
style="opacity:0.92000002;fill:#7b0000;fill-opacity:0.2983426;stroke:#000000;stroke-width:0.66495597;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<g
id="g4151">
<path
sodipodi:nodetypes="sszzzcss"
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"
id="path2824-1-1-3"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.00100005;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
sodipodi:nodetypes="ccccc"
d="m 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"
id="path2824-7-1-4-3"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
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>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

102
doc/tools.dox Normal file
View file

@ -0,0 +1,102 @@
/**
@page tools Helper tools
libinput provides a couple of tools to query state and events. Two of these
tools are usually installed, others are @ref developer_tools only.
@section user_tools User tools
libinput ships with two tools to gather information about devices:
@ref libinput-list-devices and @ref libinput-debug-events. Both tools must
be run as root to have acess to the kernel's @c /dev/input/event* device
files.
@subsection libinput-list-devices
The libinput-list-devices tool shows information about devices recognized by
libinput and can help identifying why a device behaves different than
expected. For example, if a device does not show up in the output, it is not
a supported input device.
@verbatim
$ sudo libinput-list-devices
[...]
Device: SynPS/2 Synaptics TouchPad
Kernel: /dev/input/event4
Group: 9
Seat: seat0, default
Size: 97.33x66.86mm
Capabilities: pointer
Tap-to-click: disabled
Tap drag lock: disabled
Left-handed: disabled
Nat.scrolling: disabled
Middle emulation: n/a
Calibration: n/a
Scroll methods: *two-finger
Click methods: *button-areas clickfinger
[...]
@endverbatim
The above listing shows example output for a touchpad. The
libinput-list-devices tool lists general information about the device (the
kernel event node) but also the configuration options. If an option is
"n/a" it does not exist on this device. Otherwise, the tool will show the
default configuration for this device, for options that have more than a
binary state all available options are listed, with the default one prefixed
with an asterisk (*). In the example above, the default click method is
button-areas but clickinger is available.
Note that the default configuration may differ from the configuration
applied by the desktop environment.
@note This tool is intended to be human-readable and may change its output
at any time.
@subsection libinput-debug-events
This is an installed version of the @ref event-debug developer tool. It
prints events from devices and can help to identify why a device behaves
different than expected.
@verbatim
$ sudo libinput-debug-events --enable-tapping --set-click-method=clickfinger
@endverbatim
See the man page or the @c --help output for information about the available
options.
@section developer_tools Developer tools
The two most common tools used by developers are @ref event-debug and @ref
event-gui.
@subsection event-debug
This is the in-tree version of the @ref libinput-debug-events tool and is
linked to allow for easy debugging (i.e. it avoids libtool shenanigans). The
code is the same. For debugging, run it against a single device only and
enable the --verbose flag. This will print the various state machine
transitions in addition to the events.
@verbatim
$ sudo ./tools/event-debug --verbose --device /dev/input/event3
@endverbatim
See the @c --help output for information about the available options.
@subsection event-gui
A simple GTK-based graphical tool that shows the behavior and location of
touch events, pointer motion, scroll axes and gestures. Since this tool
gathers data directly from libinput, it is thus suitable for
pointer-acceleration testing.
@verbatim
$ sudo ./tools/event-gui
@endverbatim
See the @c --help output for information about the available options.
@note The @c --grab flag puts an exclusive @c EVIOCGRAB on the device to
avoid interference with the desktiop while testing.
*/

View file

@ -43,8 +43,6 @@
* as-is.
*/
#define CASE_RETURN_STRING(a) case a: return #a;
static inline const char*
middlebutton_state_to_str(enum evdev_middlebutton_state state)
{
@ -612,7 +610,7 @@ evdev_middlebutton_filter_button(struct evdev_device *device,
if (button < BTN_LEFT ||
bit >= sizeof(device->middlebutton.button_mask) * 8) {
log_bug_libinput(device->base.seat->libinput,
"Button mask too small for %d\n",
"Button mask too small for %s\n",
libevdev_event_code_get_name(EV_KEY,
button));
return true;

View file

@ -46,8 +46,6 @@
* The state machine only affects the soft button area code.
*/
#define CASE_RETURN_STRING(a) case a: return #a;
static inline const char*
button_state_to_str(enum button_state state) {
switch(state) {
@ -530,7 +528,7 @@ tp_init_softbuttons(struct tp_dispatch *tp,
width = device->abs.dimensions.x;
height = device->abs.dimensions.y;
/* button height: 10mm or 15% orf the touchpad height,
/* button height: 10mm or 15% or the touchpad height,
whichever is smaller */
if ((height * 0.15)/yres > 10) {
tp->buttons.bottom_area.top_edge =
@ -641,22 +639,19 @@ static enum libinput_config_click_method
tp_click_get_default_method(struct tp_dispatch *tp)
{
struct evdev_device *device = tp->device;
uint32_t clickfinger_models = EVDEV_MODEL_CHROMEBOOK |
EVDEV_MODEL_SYSTEM76_BONOBO |
EVDEV_MODEL_SYSTEM76_GALAGO |
EVDEV_MODEL_SYSTEM76_KUDU |
EVDEV_MODEL_CLEVO_W740SU;
if (!tp->buttons.is_clickpad)
return LIBINPUT_CONFIG_CLICK_METHOD_NONE;
else if (libevdev_get_id_vendor(tp->device->evdev) == VENDOR_ID_APPLE)
return LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
switch (device->model) {
case EVDEV_MODEL_CHROMEBOOK:
case EVDEV_MODEL_SYSTEM76_BONOBO:
case EVDEV_MODEL_SYSTEM76_GALAGO:
case EVDEV_MODEL_SYSTEM76_KUDU:
case EVDEV_MODEL_CLEVO_W740SU:
if (device->model_flags & clickfinger_models)
return LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
default:
break;
}
return LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
}
@ -688,7 +683,7 @@ tp_init_middlebutton_emulation(struct tp_dispatch *tp,
if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE)) {
enable_by_default = true;
want_config_option = false;
} else if (device->model == EVDEV_MODEL_ALPS_TOUCHPAD) {
} else if (device->model_flags & EVDEV_MODEL_ALPS_TOUCHPAD) {
enable_by_default = true;
want_config_option = true;
} else
@ -812,7 +807,8 @@ tp_check_clickfinger_distance(struct tp_dispatch *tp,
if (!t1 || !t2)
return 0;
if (t1->is_thumb || t2->is_thumb)
if (t1->thumb.state == THUMB_STATE_YES ||
t2->thumb.state == THUMB_STATE_YES)
return 0;
x = abs(t1->point.x - t2->point.x);
@ -874,6 +870,9 @@ tp_clickfinger_set_button(struct tp_dispatch *tp)
if (t->state != TOUCH_BEGIN && t->state != TOUCH_UPDATE)
continue;
if (t->thumb.state == THUMB_STATE_YES)
continue;
if (!first)
first = t;
else if (!second)
@ -904,9 +903,8 @@ out:
case 0:
case 1: button = BTN_LEFT; break;
case 2: button = BTN_RIGHT; break;
case 3: button = BTN_MIDDLE; break;
default:
button = 0;
button = BTN_MIDDLE; break;
break;
}
@ -971,7 +969,6 @@ tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time)
current = tp->buttons.state;
old = tp->buttons.old_state;
button = 0;
is_top = 0;
if (!tp->buttons.click_pending && current == old)

View file

@ -32,8 +32,6 @@
#include "evdev-mt-touchpad.h"
#define CASE_RETURN_STRING(a) case a: return #a
/* Use a reasonably large threshold until locked into scrolling mode, to
avoid accidentally locking in scrolling mode when trying to use the entire
touchpad to move the pointer. The user can wait for the timeout to trigger
@ -287,30 +285,11 @@ int
tp_edge_scroll_init(struct tp_dispatch *tp, struct evdev_device *device)
{
struct tp_touch *t;
int width, height;
int edge_width, edge_height;
width = device->abs.dimensions.x;
height = device->abs.dimensions.y;
switch (tp->model) {
case MODEL_ALPS:
edge_width = width * .15;
edge_height = height * .15;
break;
case MODEL_APPLETOUCH: /* unibody are all clickpads, so N/A */
edge_width = width * .085;
edge_height = height * .085;
break;
default:
/* For elantech and synaptics, note for lenovo #40 series,
* e.g. the T440s min/max are the absolute edges, not the
* recommended ones as usual with synaptics.
*/
edge_width = width * .04;
edge_height = height * .054;
break;
}
/* 7mm edge size */
edge_width = device->abs.absinfo_x->resolution * 7;
edge_height = device->abs.absinfo_y->resolution * 7;
tp->scroll.right_edge = device->abs.absinfo_x->maximum - edge_width;
tp->scroll.bottom_edge = device->abs.absinfo_y->maximum - edge_height;
@ -381,6 +360,11 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
if (t->palm.state != PALM_NONE)
continue;
/* only scroll with the finger in the previous edge */
if (t->scroll.edge &&
(tp_touch_get_edge(tp, t) & t->scroll.edge) == 0)
continue;
switch (t->scroll.edge) {
case EDGE_NONE:
if (t->scroll.direction != -1) {

View file

@ -32,8 +32,6 @@
#define DEFAULT_GESTURE_SWITCH_TIMEOUT 100 /* ms */
#define DEFAULT_GESTURE_2FG_SCROLL_TIMEOUT 1000 /* ms */
#define CASE_RETURN_STRING(a) case a: return #a
static inline const char*
gesture_state_to_str(enum tp_gesture_2fg_state state)
{
@ -195,9 +193,10 @@ tp_gesture_get_direction(struct tp_dispatch *tp, struct tp_touch *touch)
if (tp->semi_mt)
move_threshold = TP_MM_TO_DPI_NORMALIZED(4);
else
move_threshold = TP_MM_TO_DPI_NORMALIZED(3);
move_threshold = TP_MM_TO_DPI_NORMALIZED(2);
delta = device_delta(touch->point, touch->gesture.initial);
normalized = tp_normalize_delta(tp, delta);
if (normalized_length(normalized) < move_threshold)
@ -546,34 +545,15 @@ tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time)
{
unsigned int active_touches = 0;
struct tp_touch *t;
uint32_t old_thumb_mask, thumb_mask = 0;
int i = 0;
tp_for_each_touch(tp, t) {
if (tp_touch_active(tp, t))
active_touches++;
if (t->is_thumb)
thumb_mask |= 1 << i;
i++;
}
old_thumb_mask = tp->gesture.thumb_mask;
tp->gesture.thumb_mask = thumb_mask;
/* active touches does not include thumb touches, need to count those
* separately, in a bitmask.
* then, if the finger count changes and/or the thumb count changes
* -> cancel gesture.
*/
if (thumb_mask != old_thumb_mask) {
/* if a thumb is detected during a gesture, that gesture is
* cancelled and the user effectively needs to restart. we
* could be smarter, but the complexity isn't worth it */
tp_gesture_cancel(tp, time);
return;
}
if (active_touches != tp->gesture.finger_count) {
/* If all fingers are lifted immediately end the gesture */
if (active_touches == 0) {

View file

@ -35,8 +35,6 @@
#include "evdev-mt-touchpad.h"
#define CASE_RETURN_STRING(a) case a: return #a
#define DEFAULT_TAP_TIMEOUT_PERIOD 180
#define DEFAULT_DRAG_TIMEOUT_PERIOD 300
#define DEFAULT_TAP_MOVE_THRESHOLD TP_MM_TO_DPI_NORMALIZED(3)
@ -98,7 +96,6 @@ tap_event_to_str(enum tap_event event)
}
return NULL;
}
#undef CASE_RETURN_STRING
static void
tp_tap_notify(struct tp_dispatch *tp,
@ -743,7 +740,7 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
/* The simple version: if a touch is a thumb on
* begin we ignore it. All other thumb touches
* follow the normal tap state for now */
if (t->is_thumb) {
if (t->thumb.state == THUMB_STATE_YES) {
t->tap.is_thumb = true;
continue;
}
@ -775,7 +772,7 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time);
} else if (tp->tap.state != TAP_STATE_IDLE &&
t->is_thumb &&
t->thumb.state == THUMB_STATE_YES &&
!t->tap.is_thumb) {
tp_tap_handle_event(tp, t, TAP_EVENT_THUMB, time);
}

View file

@ -30,10 +30,6 @@
#include "evdev-mt-touchpad.h"
/* Number found by trial-and error, seems to be 1200, divided by the
* TP_MAGIC_SLOWDOWN in filter.c */
#define DEFAULT_ACCEL_NUMERATOR 3000.0
#define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0
#define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT 300 /* ms */
#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1 200 /* ms */
#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2 500 /* ms */
@ -212,7 +208,8 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
t->millis = time;
tp->nfingers_down++;
t->palm.time = time;
t->is_thumb = false;
t->thumb.state = THUMB_STATE_MAYBE;
t->thumb.first_touch_time = time;
t->tap.is_thumb = false;
assert(tp->nfingers_down >= 1);
}
@ -318,6 +315,8 @@ tp_process_absolute(struct tp_dispatch *tp,
break;
case ABS_MT_PRESSURE:
t->pressure = e->value;
t->dirty = true;
tp->queued |= TOUCHPAD_EVENT_MOTION;
break;
}
}
@ -345,6 +344,40 @@ tp_process_absolute_st(struct tp_dispatch *tp,
}
}
static inline void
tp_restore_synaptics_touches(struct tp_dispatch *tp,
uint64_t time)
{
unsigned int i;
unsigned int nfake_touches;
nfake_touches = tp_fake_finger_count(tp);
if (nfake_touches < 3)
return;
if (tp->nfingers_down >= nfake_touches ||
tp->nfingers_down == tp->num_slots)
return;
/* Synaptics devices may end touch 2 on BTN_TOOL_TRIPLETAP
* and start it again on the next frame with different coordinates
* (#91352). We search the touches we have, if there is one that has
* just ended despite us being on tripletap, we move it back to
* update.
*/
for (i = 0; i < tp->num_slots; i++) {
struct tp_touch *t = tp_get_touch(tp, i);
if (t->state != TOUCH_END)
continue;
/* new touch, move it through begin to update immediately */
tp_new_touch(tp, t, time);
tp_begin_touch(tp, t, time);
t->state = TOUCH_UPDATE;
}
}
static void
tp_process_fake_touches(struct tp_dispatch *tp,
uint64_t time)
@ -357,6 +390,10 @@ tp_process_fake_touches(struct tp_dispatch *tp,
if (nfake_touches == FAKE_FINGER_OVERFLOW)
return;
if (tp->device->model_flags &
EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD)
tp_restore_synaptics_touches(tp, time);
start = tp->has_mt ? tp->num_slots : 0;
for (i = start; i < tp->ntouches; i++) {
t = tp_get_touch(tp, i);
@ -375,8 +412,7 @@ tp_process_trackpoint_button(struct tp_dispatch *tp,
struct evdev_dispatch *dispatch;
struct input_event event;
if (!tp->buttons.trackpoint ||
(tp->device->tags & EVDEV_TAG_TOUCHPAD_TRACKPOINT) == 0)
if (!tp->buttons.trackpoint)
return;
dispatch = tp->buttons.trackpoint->dispatch;
@ -442,8 +478,8 @@ tp_unpin_finger(struct tp_dispatch *tp, struct tp_touch *t)
ydist = abs(t->point.y - t->pinned.center.y);
ydist *= tp->buttons.motion_dist.y_scale_coeff;
/* 3mm movement -> unpin */
if (vector_length(xdist, ydist) >= 3.0) {
/* 1.5mm movement -> unpin */
if (hypot(xdist, ydist) >= 1.5) {
t->pinned.is_pinned = false;
return;
}
@ -466,7 +502,7 @@ tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
t->palm.state == PALM_NONE &&
!t->pinned.is_pinned &&
!t->is_thumb &&
t->thumb.state != THUMB_STATE_YES &&
tp_button_touch_active(tp, t) &&
tp_edge_scroll_touch_active(tp, t);
}
@ -495,7 +531,8 @@ tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t)
static int
tp_palm_detect_dwt(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
{
if (tp->dwt.keyboard_active &&
if (tp->dwt.dwt_enabled &&
tp->dwt.keyboard_active &&
t->state == TOUCH_BEGIN) {
t->palm.state = PALM_TYPING;
t->palm.first = t->point;
@ -608,20 +645,63 @@ out:
t->palm.state == PALM_TYPING ? "typing" : "trackpoint");
}
static void
tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t)
static inline const char*
thumb_state_to_str(enum tp_thumb_state state)
{
/* once a thumb, always a thumb */
if (!tp->thumb.detect_thumbs || t->is_thumb)
switch(state){
CASE_RETURN_STRING(THUMB_STATE_NO);
CASE_RETURN_STRING(THUMB_STATE_YES);
CASE_RETURN_STRING(THUMB_STATE_MAYBE);
}
return NULL;
}
static void
tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
{
enum tp_thumb_state state = t->thumb.state;
/* once a thumb, always a thumb, once ruled out always ruled out */
if (!tp->thumb.detect_thumbs ||
t->thumb.state != THUMB_STATE_MAYBE)
return;
if (t->point.y < tp->thumb.upper_thumb_line) {
/* if a potential thumb is above the line, it won't ever
* label as thumb */
t->thumb.state = THUMB_STATE_NO;
goto out;
}
/* If the thumb moves by more than 7mm, it's not a resting thumb */
if (t->state == TOUCH_BEGIN)
t->thumb.initial = t->point;
else if (t->state == TOUCH_UPDATE) {
struct device_float_coords delta;
struct normalized_coords normalized;
delta = device_delta(t->point, t->thumb.initial);
normalized = tp_normalize_delta(tp, delta);
if (normalized_length(normalized) >
TP_MM_TO_DPI_NORMALIZED(7)) {
t->thumb.state = THUMB_STATE_NO;
goto out;
}
}
/* Note: a thumb at the edge of the touchpad won't trigger the
* threshold, the surface areas is usually too small.
* threshold, the surface area is usually too small. So we have a
* two-stage detection: pressure and time within the area.
* A finger that remains at the very bottom of the touchpad becomes
* a thumb.
*/
if (t->pressure < tp->thumb.threshold)
return;
t->is_thumb = true;
if (t->pressure > tp->thumb.threshold)
t->thumb.state = THUMB_STATE_YES;
else if (t->point.y > tp->thumb.lower_thumb_line &&
tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE &&
t->thumb.first_touch_time + 300 < time)
t->thumb.state = THUMB_STATE_YES;
/* now what? we marked it as thumb, so:
*
@ -629,10 +709,16 @@ tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t)
* - clickfinger must ignore this touch for finger count
* - software buttons are unaffected
* - edge scrolling unaffected
* - gestures: cancel
* - gestures: unaffected
* - tapping: honour thumb on begin, ignore it otherwise for now,
* this gets a tad complicated otherwise
*/
out:
if (t->thumb.state != state)
log_debug(tp_libinput_context(tp),
"thumb state: %s → %s\n",
thumb_state_to_str(state),
thumb_state_to_str(t->thumb.state));
}
static void
@ -726,34 +812,95 @@ tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
}
static inline void
tp_position_fake_touches(struct tp_dispatch *tp)
{
struct tp_touch *t;
struct tp_touch *topmost = NULL;
unsigned int start, i;
if (tp_fake_finger_count(tp) <= tp->num_slots)
return;
/* We have at least one fake touch down. Find the top-most real
* touch and copy its coordinates over to to all fake touches.
* This is more reliable than just taking the first touch.
*/
for (i = 0; i < tp->num_slots; i++) {
t = tp_get_touch(tp, i);
if (t->state == TOUCH_END ||
t->state == TOUCH_NONE)
continue;
if (topmost == NULL || t->point.y < topmost->point.y)
topmost = t;
}
if (!topmost) {
log_bug_libinput(tp_libinput_context(tp),
"Unable to find topmost touch\n");
return;
}
start = tp->has_mt ? tp->num_slots : 1;
for (i = start; i < tp->ntouches; i++) {
t = tp_get_touch(tp, i);
if (t->state == TOUCH_NONE)
continue;
t->point = topmost->point;
if (!t->dirty)
t->dirty = topmost->dirty;
}
}
static inline bool
tp_need_motion_history_reset(struct tp_dispatch *tp)
{
/* semi-mt finger postions may "jump" when nfingers changes */
if (tp->semi_mt && tp->nfingers_down != tp->old_nfingers_down)
return true;
/* if we're transitioning between slots and fake touches in either
* direction, we may get a coordinate jump
*/
if (tp->nfingers_down != tp->old_nfingers_down &&
(tp->nfingers_down > tp->num_slots ||
tp->old_nfingers_down > tp->num_slots))
return true;
return false;
}
static void
tp_process_state(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *t;
struct tp_touch *first = tp_get_touch(tp, 0);
unsigned int i;
bool restart_filter = false;
bool want_motion_reset;
tp_process_fake_touches(tp, time);
tp_unhover_touches(tp, time);
tp_position_fake_touches(tp);
want_motion_reset = tp_need_motion_history_reset(tp);
for (i = 0; i < tp->ntouches; i++) {
t = tp_get_touch(tp, i);
/* semi-mt finger postions may "jump" when nfingers changes */
if (tp->semi_mt && tp->nfingers_down != tp->old_nfingers_down)
if (want_motion_reset) {
tp_motion_history_reset(t);
if (i >= tp->num_slots && t->state != TOUCH_NONE) {
t->point = first->point;
if (!t->dirty)
t->dirty = first->dirty;
t->quirks.reset_motion_history = true;
} else if (t->quirks.reset_motion_history) {
tp_motion_history_reset(t);
t->quirks.reset_motion_history = false;
}
if (!t->dirty)
continue;
tp_thumb_detect(tp, t);
tp_thumb_detect(tp, t, time);
tp_palm_detect(tp, t, time);
tp_motion_hysteresis(tp, t);
@ -1069,6 +1216,9 @@ tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data)
struct libinput_event_keyboard *kbdev;
unsigned int timeout;
if (!tp->dwt.dwt_enabled)
return;
if (event->type != LIBINPUT_EVENT_KEYBOARD_KEY)
return;
@ -1099,6 +1249,22 @@ tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data)
time + timeout);
}
static bool
tp_dwt_device_is_blacklisted(struct evdev_device *device)
{
unsigned int bus = libevdev_get_id_bustype(device->evdev);
/* evemu will set the right bus type */
if (bus == BUS_BLUETOOTH || bus == BUS_VIRTUAL)
return true;
/* Wacom makes touchpads, but not internal ones */
if (libevdev_get_id_vendor(device->evdev) == VENDOR_ID_WACOM)
return true;
return false;
}
static bool
tp_want_dwt(struct evdev_device *touchpad,
struct evdev_device *keyboard)
@ -1106,11 +1272,8 @@ tp_want_dwt(struct evdev_device *touchpad,
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)
if (tp_dwt_device_is_blacklisted(touchpad) ||
tp_dwt_device_is_blacklisted(keyboard))
return false;
/* If the touchpad is on serio, the keyboard is too, so ignore any
@ -1118,10 +1281,6 @@ tp_want_dwt(struct evdev_device *touchpad,
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 */
@ -1228,14 +1387,10 @@ evdev_tag_touchpad(struct evdev_device *device,
*/
bustype = libevdev_get_id_bustype(device->evdev);
if (bustype == BUS_USB) {
if (device->model == EVDEV_MODEL_APPLE_TOUCHPAD)
if (device->model_flags & EVDEV_MODEL_APPLE_TOUCHPAD)
device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD;
} else if (bustype != BUS_BLUETOOTH)
device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD;
if (udev_device_get_property_value(udev_device,
"TOUCHPAD_HAS_TRACKPOINT_BUTTONS"))
device->tags |= EVDEV_TAG_TOUCHPAD_TRACKPOINT;
}
static struct evdev_dispatch_interface tp_interface = {
@ -1355,14 +1510,10 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal)
tp->accel.x_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_x;
tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y;
switch (tp->device->model) {
case EVDEV_MODEL_LENOVO_X230:
if (tp->device->model_flags & EVDEV_MODEL_LENOVO_X230)
profile = touchpad_lenovo_x230_accel_profile;
break;
default:
else
profile = touchpad_accel_profile_linear;
break;
}
if (evdev_device_init_pointer_acceleration(tp->device, profile) == -1)
return -1;
@ -1450,6 +1601,77 @@ tp_init_scroll(struct tp_dispatch *tp, struct evdev_device *device)
return 0;
}
static int
tp_dwt_config_is_available(struct libinput_device *device)
{
return 1;
}
static enum libinput_config_status
tp_dwt_config_set(struct libinput_device *device,
enum libinput_config_dwt_state enable)
{
struct evdev_device *evdev = (struct evdev_device*)device;
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
switch(enable) {
case LIBINPUT_CONFIG_DWT_ENABLED:
case LIBINPUT_CONFIG_DWT_DISABLED:
break;
default:
return LIBINPUT_CONFIG_STATUS_INVALID;
}
tp->dwt.dwt_enabled = (enable == LIBINPUT_CONFIG_DWT_ENABLED);
return LIBINPUT_CONFIG_STATUS_SUCCESS;
}
static enum libinput_config_dwt_state
tp_dwt_config_get(struct libinput_device *device)
{
struct evdev_device *evdev = (struct evdev_device*)device;
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
return tp->dwt.dwt_enabled ?
LIBINPUT_CONFIG_DWT_ENABLED :
LIBINPUT_CONFIG_DWT_DISABLED;
}
static bool
tp_dwt_default_enabled(struct tp_dispatch *tp)
{
return true;
}
static enum libinput_config_dwt_state
tp_dwt_config_get_default(struct libinput_device *device)
{
struct evdev_device *evdev = (struct evdev_device*)device;
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
return tp_dwt_default_enabled(tp) ?
LIBINPUT_CONFIG_DWT_ENABLED :
LIBINPUT_CONFIG_DWT_DISABLED;
}
static int
tp_init_dwt(struct tp_dispatch *tp,
struct evdev_device *device)
{
if (tp_dwt_device_is_blacklisted(device))
return 0;
tp->dwt.config.is_available = tp_dwt_config_is_available;
tp->dwt.config.set_enabled = tp_dwt_config_set;
tp->dwt.config.get_enabled = tp_dwt_config_get;
tp->dwt.config.get_default_enabled = tp_dwt_config_get_default;
tp->dwt.dwt_enabled = tp_dwt_default_enabled(tp);
device->base.config.dwt = &tp->dwt.config;
return 0;
}
static int
tp_init_palmdetect(struct tp_dispatch *tp,
struct evdev_device *device)
@ -1465,18 +1687,13 @@ tp_init_palmdetect(struct tp_dispatch *tp,
/* Wacom doesn't have internal touchpads,
* Apple touchpads are always big enough to warrant palm detection */
if (device->model == EVDEV_MODEL_WACOM_TOUCHPAD) {
if (device->model_flags & EVDEV_MODEL_WACOM_TOUCHPAD)
return 0;
} else if (device->model != EVDEV_MODEL_APPLE_TOUCHPAD) {
/* We don't know how big the touchpad is */
if (device->abs.absinfo_x->resolution == 1)
return 0;
/* Enable palm detection on touchpads >= 70 mm. Anything smaller
probably won't need it, until we find out it does */
if (width/device->abs.absinfo_x->resolution < 70)
return 0;
}
/* Enable palm detection on touchpads >= 70 mm. Anything smaller
probably won't need it, until we find out it does */
if (width/device->abs.absinfo_x->resolution < 70)
return 0;
/* palm edges are 5% of the width on each side */
tp->palm.right_edge = device->abs.absinfo_x->maximum - width * 0.05;
@ -1507,6 +1724,13 @@ tp_init_thumb(struct tp_dispatch *tp)
{
struct evdev_device *device = tp->device;
const struct input_absinfo *abs;
double w = 0.0, h = 0.0;
int xres, yres;
int ymax;
double threshold;
if (!tp->buttons.is_clickpad)
return 0;
abs = libevdev_get_abs_info(device->evdev, ABS_MT_PRESSURE);
if (!abs)
@ -1515,14 +1739,33 @@ tp_init_thumb(struct tp_dispatch *tp)
if (abs->maximum - abs->minimum < 255)
return 0;
/* The touchpads we looked at so far have a clear thumb threshold of
* ~100, you don't reach that with a normal finger interaction.
/* if the touchpad is less than 50mm high, skip thumb detection.
* it's too small to meaningfully interact with a thumb on the
* touchpad */
evdev_device_get_size(device, &w, &h);
if (h < 50)
return 0;
/* Our reference touchpad is the T440s with 42x42 resolution.
* Higher-res touchpads exhibit higher pressure for the same
* interaction. On the T440s, the threshold value is 100, you don't
* reach that with a normal finger interaction.
* Note: "thumb" means massive touch that should not interact, not
* "using the tip of my thumb for a pinch gestures".
*/
tp->thumb.threshold = 100;
xres = tp->device->abs.absinfo_x->resolution;
yres = tp->device->abs.absinfo_y->resolution;
threshold = 100.0 * hypot(xres, yres)/hypot(42, 42);
tp->thumb.threshold = max(100, threshold);
tp->thumb.detect_thumbs = true;
/* detect thumbs by pressure in the bottom 15mm, detect thumbs by
* lingering in the bottom 8mm */
ymax = tp->device->abs.absinfo_y->maximum;
yres = tp->device->abs.absinfo_y->resolution;
tp->thumb.upper_thumb_line = ymax - yres * 15;
tp->thumb.lower_thumb_line = ymax - yres * 8;
return 0;
}
@ -1546,7 +1789,8 @@ tp_sanity_check(struct tp_dispatch *tp,
error:
log_bug_kernel(libinput,
"device %s failed touchpad sanity checks\n");
"device %s failed touchpad sanity checks\n",
device->devname);
return -1;
}
@ -1629,6 +1873,9 @@ tp_init(struct tp_dispatch *tp,
if (tp_init_buttons(tp, device) != 0)
return -1;
if (tp_init_dwt(tp, device) != 0)
return -1;
if (tp_init_palmdetect(tp, device) != 0)
return -1;

View file

@ -136,17 +136,31 @@ enum tp_gesture_2fg_state {
GESTURE_2FG_STATE_PINCH,
};
enum tp_thumb_state {
THUMB_STATE_NO,
THUMB_STATE_YES,
THUMB_STATE_MAYBE,
};
struct tp_touch {
struct tp_dispatch *tp;
enum touch_state state;
bool has_ended; /* TRACKING_ID == -1 */
bool dirty;
bool is_thumb;
struct device_coords point;
uint64_t millis;
int distance; /* distance == 0 means touch */
int pressure;
struct {
/* A quirk mostly used on Synaptics touchpads. In a
transition to/from fake touches > num_slots, the current
event data is likely garbage and the subsequent event
is likely too. This marker tells us to reset the motion
history again -> this effectively swallows any motion */
bool reset_motion_history;
} quirks;
struct {
struct device_coords samples[TOUCHPAD_HISTORY_LENGTH];
unsigned int index;
@ -195,6 +209,12 @@ struct tp_touch {
struct {
struct device_coords initial;
} gesture;
struct {
enum tp_thumb_state state;
uint64_t first_touch_time;
struct device_coords initial;
} thumb;
};
struct tp_dispatch {
@ -237,7 +257,6 @@ struct tp_dispatch {
double prev_scale;
double angle;
struct device_float_coords center;
uint32_t thumb_mask;
} gesture;
struct {
@ -314,6 +333,9 @@ struct tp_dispatch {
} sendevents;
struct {
struct libinput_device_config_dwt config;
bool dwt_enabled;
bool keyboard_active;
struct libinput_event_listener keyboard_listener;
struct libinput_timer keyboard_timer;
@ -325,6 +347,8 @@ struct tp_dispatch {
struct {
bool detect_thumbs;
int threshold;
int upper_thumb_line;
int lower_thumb_line;
} thumb;
};

View file

@ -563,7 +563,7 @@ evdev_process_touch(struct evdev_device *device,
case ABS_MT_SLOT:
if ((size_t)e->value >= device->mt.slots_len) {
log_bug_libinput(device->base.seat->libinput,
"%s exceeds slots (%d of %d)\n",
"%s exceeds slots (%d of %zd)\n",
device->devname,
e->value,
device->mt.slots_len);
@ -1481,6 +1481,10 @@ evdev_get_trackpoint_dpi(struct evdev_device *device)
DEFAULT_TRACKPOINT_ACCEL);
accel = DEFAULT_TRACKPOINT_ACCEL;
}
log_info(libinput,
"Device '%s' set to const accel %.2f\n",
device->devname,
accel);
}
return DEFAULT_MOUSE_DPI / accel;
@ -1522,8 +1526,8 @@ evdev_read_dpi_prop(struct evdev_device *device)
return dpi;
}
static inline enum evdev_device_model
evdev_read_model(struct evdev_device *device)
static inline uint32_t
evdev_read_model_flags(struct evdev_device *device)
{
const struct model_map {
const char *property;
@ -1538,18 +1542,37 @@ evdev_read_model(struct evdev_device *device)
{ "LIBINPUT_MODEL_APPLE_TOUCHPAD", EVDEV_MODEL_APPLE_TOUCHPAD },
{ "LIBINPUT_MODEL_WACOM_TOUCHPAD", EVDEV_MODEL_WACOM_TOUCHPAD },
{ "LIBINPUT_MODEL_ALPS_TOUCHPAD", EVDEV_MODEL_ALPS_TOUCHPAD },
{ "LIBINPUT_MODEL_SYNAPTICS_SERIAL_TOUCHPAD", EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD },
{ NULL, EVDEV_MODEL_DEFAULT },
};
const struct model_map *m = model_map;
uint32_t model_flags = 0;
while (m->property) {
if (!!udev_device_get_property_value(device->udev_device,
m->property))
break;
model_flags |= m->model;
m++;
}
return m->model;
return model_flags;
}
static inline int
evdev_read_attr_res_prop(struct evdev_device *device,
size_t *xres,
size_t *yres)
{
struct udev_device *udev;
const char *res_prop;
udev = device->udev_device;
res_prop = udev_device_get_property_value(udev,
"LIBINPUT_ATTR_RESOLUTION_HINT");
if (!res_prop)
return false;
return parse_dimension_property(res_prop, xres, yres);
}
static inline int
@ -1579,8 +1602,8 @@ evdev_fix_abs_resolution(struct evdev_device *device,
struct libevdev *evdev = device->evdev;
const struct input_absinfo *absx, *absy;
size_t widthmm = 0, heightmm = 0;
int xres = EVDEV_FAKE_RESOLUTION,
yres = EVDEV_FAKE_RESOLUTION;
size_t xres = EVDEV_FAKE_RESOLUTION,
yres = EVDEV_FAKE_RESOLUTION;
if (!(xcode == ABS_X && ycode == ABS_Y) &&
!(xcode == ABS_MT_POSITION_X && ycode == ABS_MT_POSITION_Y)) {
@ -1601,7 +1624,8 @@ evdev_fix_abs_resolution(struct evdev_device *device,
* property is only for general size hints where we can make
* educated guesses but don't know better.
*/
if (evdev_read_attr_size_prop(device, &widthmm, &heightmm)) {
if (!evdev_read_attr_res_prop(device, &xres, &yres) &&
evdev_read_attr_size_prop(device, &widthmm, &heightmm)) {
xres = (absx->maximum - absx->minimum)/widthmm;
yres = (absy->maximum - absy->minimum)/heightmm;
}
@ -1953,6 +1977,10 @@ evdev_configure_device(struct evdev_device *device)
if (udev_tags & EVDEV_UDEV_TAG_MOUSE ||
udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK) {
evdev_tag_external_mouse(device, device->udev_device);
evdev_tag_trackpoint(device, device->udev_device);
device->dpi = evdev_read_dpi_prop(device);
if (libevdev_has_event_code(evdev, EV_REL, REL_X) &&
libevdev_has_event_code(evdev, EV_REL, REL_Y) &&
evdev_init_accel(device) == -1)
@ -1970,9 +1998,6 @@ 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) {
@ -2144,8 +2169,8 @@ evdev_device_create(struct libinput_seat *seat,
device->scroll.direction = 0;
device->scroll.wheel_click_angle =
evdev_read_wheel_click_prop(device);
device->model = evdev_read_model(device);
device->dpi = evdev_read_dpi_prop(device);
device->model_flags = evdev_read_model_flags(device);
device->dpi = DEFAULT_MOUSE_DPI;
/* at most 5 SYN_DROPPED log-messages per 30s */
ratelimit_init(&device->syn_drop_limit, 30ULL * 1000, 5);

View file

@ -68,8 +68,7 @@ enum evdev_device_tags {
EVDEV_TAG_EXTERNAL_MOUSE = (1 << 0),
EVDEV_TAG_INTERNAL_TOUCHPAD = (1 << 1),
EVDEV_TAG_TRACKPOINT = (1 << 2),
EVDEV_TAG_TOUCHPAD_TRACKPOINT = (1 << 3),
EVDEV_TAG_KEYBOARD = (1 << 4),
EVDEV_TAG_KEYBOARD = (1 << 3),
};
enum evdev_middlebutton_state {
@ -96,16 +95,17 @@ enum evdev_middlebutton_event {
};
enum evdev_device_model {
EVDEV_MODEL_DEFAULT,
EVDEV_MODEL_LENOVO_X230,
EVDEV_MODEL_CHROMEBOOK,
EVDEV_MODEL_SYSTEM76_BONOBO,
EVDEV_MODEL_SYSTEM76_GALAGO,
EVDEV_MODEL_SYSTEM76_KUDU,
EVDEV_MODEL_CLEVO_W740SU,
EVDEV_MODEL_APPLE_TOUCHPAD,
EVDEV_MODEL_WACOM_TOUCHPAD,
EVDEV_MODEL_ALPS_TOUCHPAD,
EVDEV_MODEL_DEFAULT = 0,
EVDEV_MODEL_LENOVO_X230 = (1 << 0),
EVDEV_MODEL_CHROMEBOOK = (1 << 1),
EVDEV_MODEL_SYSTEM76_BONOBO = (1 << 2),
EVDEV_MODEL_SYSTEM76_GALAGO = (1 << 3),
EVDEV_MODEL_SYSTEM76_KUDU = (1 << 4),
EVDEV_MODEL_CLEVO_W740SU = (1 << 5),
EVDEV_MODEL_APPLE_TOUCHPAD = (1 << 6),
EVDEV_MODEL_WACOM_TOUCHPAD = (1 << 7),
EVDEV_MODEL_ALPS_TOUCHPAD = (1 << 8),
EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD = (1 << 9),
};
struct mt_slot {
@ -222,7 +222,7 @@ struct evdev_device {
int dpi; /* HW resolution */
struct ratelimit syn_drop_limit; /* ratelimit for SYN_DROPPED logging */
enum evdev_device_model model;
uint32_t model_flags;
};
#define EVDEV_UNHANDLED_DEVICE ((struct evdev_device *) 1)

View file

@ -200,6 +200,17 @@ struct libinput_device_config_middle_emulation {
struct libinput_device *device);
};
struct libinput_device_config_dwt {
int (*is_available)(struct libinput_device *device);
enum libinput_config_status (*set_enabled)(
struct libinput_device *device,
enum libinput_config_dwt_state enable);
enum libinput_config_dwt_state (*get_enabled)(
struct libinput_device *device);
enum libinput_config_dwt_state (*get_default_enabled)(
struct libinput_device *device);
};
struct libinput_device_config {
struct libinput_device_config_tap *tap;
struct libinput_device_config_calibration *calibration;
@ -210,6 +221,7 @@ struct libinput_device_config {
struct libinput_device_config_scroll_method *scroll_method;
struct libinput_device_config_click_method *click_method;
struct libinput_device_config_middle_emulation *middle_emulation;
struct libinput_device_config_dwt *dwt;
};
struct libinput_device_group {
@ -262,13 +274,15 @@ typedef void (*libinput_source_dispatch_t)(void *data);
void
log_msg(struct libinput *libinput,
enum libinput_log_priority priority,
const char *format, ...);
const char *format, ...)
LIBINPUT_ATTRIBUTE_PRINTF(3, 4);
void
log_msg_va(struct libinput *libinput,
enum libinput_log_priority priority,
const char *format,
va_list args);
va_list args)
LIBINPUT_ATTRIBUTE_PRINTF(3, 0);
int
libinput_init(struct libinput *libinput,

View file

@ -37,10 +37,14 @@
#define VENDOR_ID_APPLE 0x5ac
#define VENDOR_ID_WACOM 0x56a
#define VENDOR_ID_SYNAPTICS_SERIAL 0x002
#define PRODUCT_ID_SYNAPTICS_SERIAL 0x007
/* The HW DPI rate we normalize to before calculating pointer acceleration */
#define DEFAULT_MOUSE_DPI 1000
#define CASE_RETURN_STRING(a) case a: return #a;
void
set_logging_enabled(int enabled);
@ -324,10 +328,4 @@ int parse_mouse_wheel_click_angle_property(const char *prop);
double parse_trackpoint_accel_property(const char *prop);
bool parse_dimension_property(const char *prop, size_t *width, size_t *height);
static inline double
vector_length(double x, double y)
{
return sqrt(x * x + y * y);
}
#endif /* LIBINPUT_UTIL_H */

View file

@ -2782,3 +2782,45 @@ libinput_device_config_scroll_get_default_button(struct libinput_device *device)
return device->config.scroll_method->get_default_button(device);
}
LIBINPUT_EXPORT int
libinput_device_config_dwt_is_available(struct libinput_device *device)
{
if (!device->config.dwt)
return 0;
return device->config.dwt->is_available(device);
}
LIBINPUT_EXPORT enum libinput_config_status
libinput_device_config_dwt_set_enabled(struct libinput_device *device,
enum libinput_config_dwt_state enable)
{
if (enable != LIBINPUT_CONFIG_DWT_ENABLED &&
enable != LIBINPUT_CONFIG_DWT_DISABLED)
return LIBINPUT_CONFIG_STATUS_INVALID;
if (!libinput_device_config_dwt_is_available(device))
return enable ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED :
LIBINPUT_CONFIG_STATUS_SUCCESS;
return device->config.dwt->set_enabled(device, enable);
}
LIBINPUT_EXPORT enum libinput_config_dwt_state
libinput_device_config_dwt_get_enabled(struct libinput_device *device)
{
if (!libinput_device_config_dwt_is_available(device))
return LIBINPUT_CONFIG_DWT_DISABLED;
return device->config.dwt->get_enabled(device);
}
LIBINPUT_EXPORT enum libinput_config_dwt_state
libinput_device_config_dwt_get_default_enabled(struct libinput_device *device)
{
if (!libinput_device_config_dwt_is_available(device))
return LIBINPUT_CONFIG_DWT_DISABLED;
return device->config.dwt->get_default_enabled(device);
}

View file

@ -3574,6 +3574,96 @@ libinput_device_config_scroll_get_button(struct libinput_device *device);
uint32_t
libinput_device_config_scroll_get_default_button(struct libinput_device *device);
/**
* @ingroup config
*
* Possible states for the disable-while-typing feature. See @ref
* disable-while-typing for details.
*/
enum libinput_config_dwt_state {
LIBINPUT_CONFIG_DWT_DISABLED,
LIBINPUT_CONFIG_DWT_ENABLED,
};
/**
* @ingroup config
*
* Check if this device supports configurable disable-while-typing feature.
* This feature is usally available on built-in touchpads and disables the
* touchpad while typing. See @ref disable-while-typing for details.
*
* @param device The device to configure
* @return 0 if this device does not support disable-while-typing, or 1
* otherwise.
*
* @see libinput_device_config_dwt_set_enabled
* @see libinput_device_config_dwt_get_enabled
* @see libinput_device_config_dwt_get_default_enabled
*/
int
libinput_device_config_dwt_is_available(struct libinput_device *device);
/**
* @ingroup config
*
* Enable or disable the disable-while-typing feature. When enabled, the
* device will be disabled while typing and for a short period after. See
* @ref disable-while-typing for details.
*
* @note Enabling or disabling disable-while-typing may not take effect
* immediately.
*
* @param device The device to configure
* @param enable @ref LIBINPUT_CONFIG_DWT_DISABLED to disable
* disable-while-typing, @ref LIBINPUT_CONFIG_DWT_ENABLED to enable
*
* @return A config status code. Disabling disable-while-typing on a
* device that does not support the feature always succeeds.
*
* @see libinput_device_config_dwt_is_available
* @see libinput_device_config_dwt_get_enabled
* @see libinput_device_config_dwt_get_default_enabled
*/
enum libinput_config_status
libinput_device_config_dwt_set_enabled(struct libinput_device *device,
enum libinput_config_dwt_state enable);
/**
* @ingroup config
*
* Check if the disable-while typing feature is currently enabled on this
* device. If the device does not support disable-while-typing, this
* function returns @ref LIBINPUT_CONFIG_DWT_DISABLED.
*
* @param device The device to configure
* @return @ref LIBINPUT_CONFIG_DWT_DISABLED if disabled, @ref
* LIBINPUT_CONFIG_DWT_ENABLED if enabled.
*
* @see libinput_device_config_dwt_is_available
* @see libinput_device_config_dwt_set_enabled
* @see libinput_device_config_dwt_get_default_enabled
*/
enum libinput_config_dwt_state
libinput_device_config_dwt_get_enabled(struct libinput_device *device);
/**
* @ingroup config
*
* Check if the disable-while typing feature is enabled on this device by
* default. If the device does not support disable-while-typing, this
* function returns @ref LIBINPUT_CONFIG_DWT_DISABLED.
*
* @param device The device to configure
* @return @ref LIBINPUT_CONFIG_DWT_DISABLED if disabled, @ref
* LIBINPUT_CONFIG_DWT_ENABLED if enabled.
*
* @see libinput_device_config_dwt_is_available
* @see libinput_device_config_dwt_set_enabled
* @see libinput_device_config_dwt_get_enabled
*/
enum libinput_config_dwt_state
libinput_device_config_dwt_get_default_enabled(struct libinput_device *device);
#ifdef __cplusplus
}
#endif

View file

@ -163,6 +163,13 @@ LIBINPUT_0.20.0 {
libinput_event_get_gesture_event;
} LIBINPUT_0.19.0;
LIBINPUT_0.21.0 {
libinput_device_config_dwt_is_available;
libinput_device_config_dwt_set_enabled;
libinput_device_config_dwt_get_enabled;
libinput_device_config_dwt_get_default_enabled;
} LIBINPUT_0.20.0;
/* tablet APIs, they are not part of any stable API promise yet.
* keep them separate */
LIBINPUT_TABLET_SUPPORT {
@ -189,4 +196,4 @@ LIBINPUT_TABLET_SUPPORT {
libinput_tool_ref;
libinput_tool_set_user_data;
libinput_tool_unref;
} LIBINPUT_0.20.0;
} LIBINPUT_0.21.0;

View file

@ -312,7 +312,7 @@ udev_device_from_devnode(struct libinput *libinput,
dev = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
count++;
if (count > 50) {
if (count > 200) {
log_bug_libinput(libinput,
"udev device never initialized (%s)\n",
devnode);

View file

@ -50,7 +50,7 @@ liblitest_la_SOURCES = \
litest.c
liblitest_la_LIBADD = $(top_builddir)/src/libinput-util.la
liblitest_la_CFLAGS = $(AM_CFLAGS) \
-DLIBINPUT_UDEV_RULES_FILE="\"$(abs_top_srcdir)/udev/90-libinput-model-quirks-litest.rules\"" \
-DLIBINPUT_UDEV_RULES_FILE="\"$(abs_top_builddir)/udev/90-libinput-model-quirks-litest.rules\"" \
-DLIBINPUT_UDEV_HWDB_FILE="\"$(abs_top_srcdir)/udev/90-libinput-model-quirks.hwdb\""
if HAVE_LIBUNWIND
liblitest_la_LIBADD += $(LIBUNWIND_LIBS) -ldl
@ -60,8 +60,10 @@ endif
run_tests = \
test-touchpad \
test-touchpad-tap \
test-touchpad-buttons \
test-tablet \
test-device \
test-gestures \
test-pointer \
test-touch \
test-trackpoint \
@ -116,6 +118,10 @@ test_touchpad_tap_SOURCES = touchpad-tap.c
test_touchpad_tap_LDADD = $(TEST_LIBS)
test_touchpad_tap_LDFLAGS = -no-install
test_touchpad_buttons_SOURCES = touchpad-buttons.c
test_touchpad_buttons_LDADD = $(TEST_LIBS)
test_touchpad_buttons_LDFLAGS = -no-install
test_trackpoint_SOURCES = trackpoint.c
test_trackpoint_LDADD = $(TEST_LIBS)
test_trackpoint_LDFLAGS = -no-install
@ -132,6 +138,10 @@ test_device_SOURCES = device.c
test_device_LDADD = $(TEST_LIBS)
test_device_LDFLAGS = -no-install
test_gestures_SOURCES = gestures.c
test_gestures_LDADD = $(TEST_LIBS)
test_gestures_LDFLAGS = -no-install
test_litest_selftest_SOURCES = litest-selftest.c litest.c litest-int.h litest.h
test_litest_selftest_CFLAGS = -DLITEST_DISABLE_BACKTRACE_LOGGING -DLITEST_NO_MAIN $(liblitest_la_CFLAGS)
test_litest_selftest_LDADD = $(TEST_LIBS)

View file

@ -1009,6 +1009,27 @@ START_TEST(device_udev_tag_apple)
}
END_TEST
START_TEST(device_udev_tag_synaptics_serial)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
struct udev_device *d;
const char *prop;
d = libinput_device_get_udev_device(device);
prop = udev_device_get_property_value(d,
"LIBINPUT_MODEL_SYNAPTICS_SERIAL_TOUCHPAD");
if (libevdev_get_id_vendor(dev->evdev) == VENDOR_ID_SYNAPTICS_SERIAL &&
libevdev_get_id_product(dev->evdev) == PRODUCT_ID_SYNAPTICS_SERIAL)
ck_assert_notnull(prop);
else
ck_assert(prop == NULL);
udev_device_unref(d);
}
END_TEST
START_TEST(device_udev_tag_wacom_tablet)
{
struct litest_device *dev = litest_current_device();
@ -1071,5 +1092,6 @@ litest_setup_tests(void)
litest_add("device:udev tags", device_udev_tag_alps, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("device:udev tags", device_udev_tag_wacom, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("device:udev tags", device_udev_tag_apple, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("device:udev tags", device_udev_tag_synaptics_serial, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("device:udev tags", device_udev_tag_wacom_tablet, LITEST_TABLET, LITEST_ANY);
}

364
test/gestures.c Normal file
View file

@ -0,0 +1,364 @@
/*
* Copyright © 2015 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <config.h>
#include <check.h>
#include <libinput.h>
#include "libinput-util.h"
#include "litest.h"
START_TEST(gestures_cap)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
ck_assert(libinput_device_has_capability(device,
LIBINPUT_DEVICE_CAP_GESTURE));
}
END_TEST
START_TEST(gestures_nocap)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
ck_assert(!libinput_device_has_capability(device,
LIBINPUT_DEVICE_CAP_GESTURE));
}
END_TEST
START_TEST(gestures_swipe_3fg)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
struct libinput_event_gesture *gevent;
double dx, dy;
int cardinal = _i; /* ranged test */
double dir_x, dir_y;
int cardinals[8][2] = {
{ 0, 30 },
{ 30, 30 },
{ 30, 0 },
{ 30, -30 },
{ 0, -30 },
{ -30, -30 },
{ -30, 0 },
{ -30, 30 },
};
if (libevdev_get_num_slots(dev->evdev) < 3)
return;
dir_x = cardinals[cardinal][0];
dir_y = cardinals[cardinal][1];
litest_drain_events(li);
litest_touch_down(dev, 0, 40, 40);
litest_touch_down(dev, 1, 40, 50);
litest_touch_down(dev, 2, 40, 60);
libinput_dispatch(li);
litest_touch_move_three_touches(dev,
40, 40,
40, 50,
40, 60,
dir_x, dir_y,
10, 2);
libinput_dispatch(li);
event = libinput_get_event(li);
gevent = litest_is_gesture_event(event,
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
3);
dx = libinput_event_gesture_get_dx(gevent);
dy = libinput_event_gesture_get_dy(gevent);
ck_assert(dx == 0.0);
ck_assert(dy == 0.0);
libinput_event_destroy(event);
while ((event = libinput_get_event(li)) != NULL) {
gevent = litest_is_gesture_event(event,
LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
3);
dx = libinput_event_gesture_get_dx(gevent);
dy = libinput_event_gesture_get_dy(gevent);
debug_trace("delta: %.2f/%.2f\n", dx, dy);
if (dir_x == 0.0)
ck_assert(dx == 0.0);
else if (dir_x < 0.0)
ck_assert(dx < 0.0);
else if (dir_x > 0.0)
ck_assert(dx > 0.0);
if (dir_y == 0.0)
ck_assert(dy == 0.0);
else if (dir_y < 0.0)
ck_assert(dy < 0.0);
else if (dir_y > 0.0)
ck_assert(dy > 0.0);
dx = libinput_event_gesture_get_dx_unaccelerated(gevent);
dy = libinput_event_gesture_get_dy_unaccelerated(gevent);
if (dir_x == 0.0)
ck_assert(dx == 0.0);
else if (dir_x < 0.0)
ck_assert(dx < 0.0);
else if (dir_x > 0.0)
ck_assert(dx > 0.0);
if (dir_y == 0.0)
ck_assert(dy == 0.0);
else if (dir_y < 0.0)
ck_assert(dy < 0.0);
else if (dir_y > 0.0)
ck_assert(dy > 0.0);
libinput_event_destroy(event);
}
litest_touch_up(dev, 0);
litest_touch_up(dev, 1);
litest_touch_up(dev, 2);
libinput_dispatch(li);
event = libinput_get_event(li);
gevent = litest_is_gesture_event(event,
LIBINPUT_EVENT_GESTURE_SWIPE_END,
3);
ck_assert(!libinput_event_gesture_get_cancelled(gevent));
libinput_event_destroy(event);
}
END_TEST
START_TEST(gestures_pinch)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
struct libinput_event_gesture *gevent;
double dx, dy;
int cardinal = _i; /* ranged test */
double dir_x, dir_y;
int i;
double scale, oldscale;
double angle;
int cardinals[8][2] = {
{ 0, 30 },
{ 30, 30 },
{ 30, 0 },
{ 30, -30 },
{ 0, -30 },
{ -30, -30 },
{ -30, 0 },
{ -30, 30 },
};
if (libevdev_get_num_slots(dev->evdev) < 3)
return;
dir_x = cardinals[cardinal][0];
dir_y = cardinals[cardinal][1];
litest_drain_events(li);
litest_touch_down(dev, 0, 50 + dir_x, 50 + dir_y);
litest_touch_down(dev, 1, 50 - dir_x, 50 - dir_y);
libinput_dispatch(li);
for (i = 0; i < 8; i++) {
litest_push_event_frame(dev);
if (dir_x > 0.0)
dir_x -= 3;
else if (dir_x < 0.0)
dir_x += 3;
if (dir_y > 0.0)
dir_y -= 3;
else if (dir_y < 0.0)
dir_y += 3;
litest_touch_move(dev,
0,
50 + dir_x,
50 + dir_y);
litest_touch_move(dev,
1,
50 - dir_x,
50 - dir_y);
litest_pop_event_frame(dev);
libinput_dispatch(li);
}
event = libinput_get_event(li);
gevent = litest_is_gesture_event(event,
LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
2);
dx = libinput_event_gesture_get_dx(gevent);
dy = libinput_event_gesture_get_dy(gevent);
scale = libinput_event_gesture_get_scale(gevent);
ck_assert(dx == 0.0);
ck_assert(dy == 0.0);
ck_assert(scale == 1.0);
libinput_event_destroy(event);
while ((event = libinput_get_event(li)) != NULL) {
gevent = litest_is_gesture_event(event,
LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
2);
oldscale = scale;
scale = libinput_event_gesture_get_scale(gevent);
ck_assert(scale < oldscale);
angle = libinput_event_gesture_get_angle_delta(gevent);
ck_assert_double_le(fabs(angle), 1.0);
libinput_event_destroy(event);
libinput_dispatch(li);
}
litest_touch_up(dev, 0);
litest_touch_up(dev, 1);
libinput_dispatch(li);
event = libinput_get_event(li);
gevent = litest_is_gesture_event(event,
LIBINPUT_EVENT_GESTURE_PINCH_END,
2);
ck_assert(!libinput_event_gesture_get_cancelled(gevent));
libinput_event_destroy(event);
}
END_TEST
START_TEST(gestures_spread)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
struct libinput_event_gesture *gevent;
double dx, dy;
int cardinal = _i; /* ranged test */
double dir_x, dir_y;
int i;
double scale, oldscale;
double angle;
int cardinals[8][2] = {
{ 0, 1 },
{ 1, 1 },
{ 1, 0 },
{ 1, -1 },
{ 0, -1 },
{ -1, -1 },
{ -1, 0 },
{ -1, 1 },
};
if (libevdev_get_num_slots(dev->evdev) < 3)
return;
dir_x = cardinals[cardinal][0];
dir_y = cardinals[cardinal][1];
litest_drain_events(li);
litest_touch_down(dev, 0, 50 + dir_x, 50 + dir_y);
litest_touch_down(dev, 1, 50 - dir_x, 50 - dir_y);
libinput_dispatch(li);
for (i = 0; i < 15; i++) {
litest_push_event_frame(dev);
if (dir_x > 0.0)
dir_x += 2;
else if (dir_x < 0.0)
dir_x -= 2;
if (dir_y > 0.0)
dir_y += 2;
else if (dir_y < 0.0)
dir_y -= 2;
litest_touch_move(dev,
0,
50 + dir_x,
50 + dir_y);
litest_touch_move(dev,
1,
50 - dir_x,
50 - dir_y);
litest_pop_event_frame(dev);
libinput_dispatch(li);
}
event = libinput_get_event(li);
gevent = litest_is_gesture_event(event,
LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
2);
dx = libinput_event_gesture_get_dx(gevent);
dy = libinput_event_gesture_get_dy(gevent);
scale = libinput_event_gesture_get_scale(gevent);
ck_assert(dx == 0.0);
ck_assert(dy == 0.0);
ck_assert(scale == 1.0);
libinput_event_destroy(event);
while ((event = libinput_get_event(li)) != NULL) {
gevent = litest_is_gesture_event(event,
LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
2);
oldscale = scale;
scale = libinput_event_gesture_get_scale(gevent);
ck_assert(scale > oldscale);
angle = libinput_event_gesture_get_angle_delta(gevent);
ck_assert_double_le(fabs(angle), 1.0);
libinput_event_destroy(event);
libinput_dispatch(li);
}
litest_touch_up(dev, 0);
litest_touch_up(dev, 1);
libinput_dispatch(li);
event = libinput_get_event(li);
gevent = litest_is_gesture_event(event,
LIBINPUT_EVENT_GESTURE_PINCH_END,
2);
ck_assert(!libinput_event_gesture_get_cancelled(gevent));
libinput_event_destroy(event);
}
END_TEST
void
litest_setup_tests(void)
{
/* N, NE, ... */
struct range cardinals = { 0, 8 };
litest_add("gestures:cap", gestures_cap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("gestures:cap", gestures_nocap, LITEST_ANY, LITEST_TOUCHPAD);
litest_add_ranged("gestures:swipe", gestures_swipe_3fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals);
litest_add_ranged("gestures:pinch", gestures_pinch, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals);
litest_add_ranged("gestures:pinch", gestures_spread, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &cardinals);
}

View file

@ -114,16 +114,6 @@ static struct input_absinfo absinfo[] = {
{ .value = -1 }
};
static const char udev_rule[] =
"ACTION==\"remove\", GOTO=\"touchpad_end\"\n"
"KERNEL!=\"event*\", GOTO=\"touchpad_end\"\n"
"ENV{ID_INPUT_TOUCHPAD}==\"\", GOTO=\"touchpad_end\"\n"
"\n"
"ATTRS{name}==\"litest*X1C3rd*\",\\\n"
" ENV{TOUCHPAD_HAS_TRACKPOINT_BUTTONS}=\"1\"\n"
"\n"
"LABEL=\"touchpad_end\"";
struct litest_test_device litest_synaptics_carbon3rd_device = {
.type = LITEST_SYNAPTICS_TRACKPOINT_BUTTONS,
.features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
@ -135,5 +125,4 @@ struct litest_test_device litest_synaptics_carbon3rd_device = {
.id = &input_id,
.events = events,
.absinfo = absinfo,
.udev_rule = udev_rule,
};

View file

@ -63,6 +63,7 @@ const char *filter_device = NULL;
const char *filter_group = NULL;
static inline void litest_remove_model_quirks(void);
static void litest_init_udev_rules(void);
/* defined for the litest selftest */
#ifndef LITEST_DISABLE_BACKTRACE_LOGGING
@ -214,13 +215,13 @@ litest_backtrace(void)
}
if (have_lineno) {
litest_log("%u: %s() (%s:%d)\n",
litest_log("%d: %s() (%s:%d)\n",
i,
procname,
file,
line);
} else {
litest_log("%u: %s (%s%s+%#x) [%p]\n",
litest_log("%d: %s (%s%s+%#x) [%p]\n",
i,
filename,
procname,
@ -452,7 +453,7 @@ litest_drop_udev_rules(void)
&entries,
litest_udev_rule_filter,
alphasort);
if (n < 0)
if (n <= 0)
return;
while (n--) {
@ -472,7 +473,6 @@ litest_drop_udev_rules(void)
}
free(entries);
litest_remove_model_quirks();
litest_reload_udev_rules();
}
@ -832,6 +832,8 @@ litest_run(int argc, char **argv)
if (getenv("LITEST_VERBOSE"))
verbose = 1;
litest_init_udev_rules();
srunner_run_all(sr, CK_ENV);
failed = srunner_ntests_failed(sr);
srunner_free(sr);
@ -850,6 +852,9 @@ litest_run(int argc, char **argv)
free(s);
}
litest_remove_model_quirks();
litest_reload_udev_rules();
return failed;
}
@ -967,12 +972,10 @@ litest_remove_model_quirks(void)
unlink(UDEV_COMMON_HWDB_FILE);
}
static char *
litest_init_udev_rules(struct litest_test_device *dev)
static void
litest_init_udev_rules(void)
{
int rc;
FILE *f;
char *path = NULL;
rc = mkdir(UDEV_RULES_D, 0755);
if (rc == -1 && errno != EEXIST)
@ -985,10 +988,18 @@ litest_init_udev_rules(struct litest_test_device *dev)
strerror(errno));
litest_install_model_quirks();
litest_reload_udev_rules();
}
static char *
litest_init_device_udev_rules(struct litest_test_device *dev)
{
int rc;
FILE *f;
char *path = NULL;
/* device-specific udev rules */
if (!dev->udev_rule)
goto out;
return NULL;
rc = xasprintf(&path,
"%s/%s%s.rules",
@ -1005,7 +1016,6 @@ litest_init_udev_rules(struct litest_test_device *dev)
litest_assert_int_ge(fputs(dev->udev_rule, f), 0);
fclose(f);
out:
litest_reload_udev_rules();
return path;
@ -1039,13 +1049,11 @@ litest_create(enum litest_device_type which,
d = zalloc(sizeof(*d));
litest_assert(d != NULL);
udev_file = litest_init_udev_rules(*dev);
udev_file = litest_init_device_udev_rules(*dev);
/* device has custom create method */
if ((*dev)->create) {
(*dev)->create(d);
if (abs_override || events_override) {
litest_remove_model_quirks();
if (udev_file)
unlink(udev_file);
litest_abort_msg("Custom create cannot be overridden");
@ -1196,10 +1204,10 @@ litest_delete_device(struct litest_device *d)
return;
if (d->udev_rule_file) {
litest_remove_model_quirks();
unlink(d->udev_rule_file);
free(d->udev_rule_file);
d->udev_rule_file = NULL;
litest_reload_udev_rules();
}
libinput_device_unref(d->libinput_device);
@ -1233,6 +1241,9 @@ axis_replacement_value(struct axis_replacement *axes,
{
struct axis_replacement *axis = axes;
if (!axes)
return false;
while (axis->evcode != -1) {
if (axis->evcode == evcode) {
*value = axis->value;
@ -1277,9 +1288,6 @@ litest_auto_assign_value(struct litest_device *d,
break;
default:
value = -1;
if (!axes)
break;
if (!axis_replacement_value(axes, ev->code, &value) &&
d->interface->get_axis_default)
d->interface->get_axis_default(d, ev->code, &value);
@ -1555,6 +1563,32 @@ litest_touch_move_two_touches(struct litest_device *d,
litest_touch_move(d, 1, x1 + dx, y1 + dy);
}
void
litest_touch_move_three_touches(struct litest_device *d,
double x0, double y0,
double x1, double y1,
double x2, double y2,
double dx, double dy,
int steps, int sleep_ms)
{
for (int i = 0; i < steps - 1; i++) {
litest_touch_move(d, 0, x0 + dx / steps * i,
y0 + dy / steps * i);
litest_touch_move(d, 1, x1 + dx / steps * i,
y1 + dy / steps * i);
litest_touch_move(d, 2, x2 + dx / steps * i,
y2 + dy / steps * i);
if (sleep_ms) {
libinput_dispatch(d->libinput);
msleep(sleep_ms);
libinput_dispatch(d->libinput);
}
}
litest_touch_move(d, 0, x0 + dx, y0 + dy);
litest_touch_move(d, 1, x1 + dx, y1 + dy);
litest_touch_move(d, 2, x2 + dx, y2 + dy);
}
void
litest_hover_start(struct litest_device *d,
unsigned int slot,
@ -2019,6 +2053,7 @@ litest_create_uinput_device_from_description(const char *name,
struct udev_device *udev_device;
const char *udev_action;
const char *udev_syspath = NULL;
int rc;
udev = udev_new();
litest_assert_notnull(udev);
@ -2027,7 +2062,8 @@ litest_create_uinput_device_from_description(const char *name,
udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "input",
NULL);
/* remove O_NONBLOCK */
fcntl(udev_monitor_get_fd(udev_monitor), F_SETFL, 0);
rc = fcntl(udev_monitor_get_fd(udev_monitor), F_SETFL, 0);
litest_assert_int_ne(rc, -1);
litest_assert_int_eq(udev_monitor_enable_receiving(udev_monitor),
0);
@ -2233,6 +2269,25 @@ litest_is_keyboard_event(struct libinput_event *event,
return kevent;
}
struct libinput_event_gesture *
litest_is_gesture_event(struct libinput_event *event,
enum libinput_event_type type,
int nfingers)
{
struct libinput_event_gesture *gevent;
litest_assert(event != NULL);
litest_assert_int_eq(libinput_event_get_type(event), type);
gevent = libinput_event_get_gesture_event(event);
litest_assert(gevent != NULL);
if (nfingers != -1)
litest_assert_int_eq(libinput_event_gesture_get_finger_count(gevent),
nfingers);
return gevent;
}
void
litest_assert_tablet_button_event(struct libinput *li, unsigned int button,
enum libinput_button_state state)
@ -2366,6 +2421,12 @@ litest_timeout_dwt_long(void)
msleep(520);
}
void
litest_timeout_gesture(void)
{
msleep(120);
}
void
litest_push_event_frame(struct litest_device *dev)
{

View file

@ -334,6 +334,12 @@ void litest_touch_move_two_touches(struct litest_device *d,
double x1, double y1,
double dx, double dy,
int steps, int sleep_ms);
void litest_touch_move_three_touches(struct litest_device *d,
double x0, double y0,
double x1, double y1,
double x2, double y2,
double dx, double dy,
int steps, int sleep_ms);
void litest_tablet_proximity_in(struct litest_device *d,
int x, int y,
@ -392,6 +398,11 @@ struct libinput_event_keyboard * litest_is_keyboard_event(
struct libinput_event *event,
unsigned int key,
enum libinput_key_state state);
struct libinput_event_gesture * litest_is_gesture_event(
struct libinput_event *event,
enum libinput_event_type type,
int nfingers);
void litest_assert_button_event(struct libinput *li,
unsigned int button,
enum libinput_button_state state);
@ -438,6 +449,7 @@ 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_timeout_gesture(void);
void litest_push_event_frame(struct litest_device *dev);
void litest_pop_event_frame(struct litest_device *dev);

View file

@ -132,6 +132,7 @@ START_TEST(event_conversion_device_notify)
ck_assert(libinput_event_get_pointer_event(event) == NULL);
ck_assert(libinput_event_get_keyboard_event(event) == NULL);
ck_assert(libinput_event_get_touch_event(event) == NULL);
ck_assert(libinput_event_get_gesture_event(event) == NULL);
ck_assert(libinput_event_get_tablet_event(event) == NULL);
litest_restore_log_handler(li);
}
@ -187,6 +188,7 @@ START_TEST(event_conversion_pointer)
ck_assert(libinput_event_get_device_notify_event(event) == NULL);
ck_assert(libinput_event_get_keyboard_event(event) == NULL);
ck_assert(libinput_event_get_touch_event(event) == NULL);
ck_assert(libinput_event_get_gesture_event(event) == NULL);
ck_assert(libinput_event_get_tablet_event(event) == NULL);
litest_restore_log_handler(li);
}
@ -236,6 +238,7 @@ START_TEST(event_conversion_pointer_abs)
ck_assert(libinput_event_get_device_notify_event(event) == NULL);
ck_assert(libinput_event_get_keyboard_event(event) == NULL);
ck_assert(libinput_event_get_touch_event(event) == NULL);
ck_assert(libinput_event_get_gesture_event(event) == NULL);
ck_assert(libinput_event_get_tablet_event(event) == NULL);
litest_restore_log_handler(li);
}
@ -278,6 +281,7 @@ START_TEST(event_conversion_key)
ck_assert(libinput_event_get_device_notify_event(event) == NULL);
ck_assert(libinput_event_get_pointer_event(event) == NULL);
ck_assert(libinput_event_get_touch_event(event) == NULL);
ck_assert(libinput_event_get_gesture_event(event) == NULL);
ck_assert(libinput_event_get_tablet_event(event) == NULL);
litest_restore_log_handler(li);
}
@ -327,6 +331,7 @@ START_TEST(event_conversion_touch)
ck_assert(libinput_event_get_device_notify_event(event) == NULL);
ck_assert(libinput_event_get_pointer_event(event) == NULL);
ck_assert(libinput_event_get_keyboard_event(event) == NULL);
ck_assert(libinput_event_get_gesture_event(event) == NULL);
ck_assert(libinput_event_get_tablet_event(event) == NULL);
litest_restore_log_handler(li);
}
@ -337,6 +342,54 @@ START_TEST(event_conversion_touch)
}
END_TEST
START_TEST(event_conversion_gesture)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
int gestures = 0;
int i;
libinput_dispatch(li);
litest_touch_down(dev, 0, 70, 30);
litest_touch_down(dev, 1, 30, 70);
for (i = 0; i < 8; i++) {
litest_push_event_frame(dev);
litest_touch_move(dev, 0, 70 - i * 5, 30 + i * 5);
litest_touch_move(dev, 1, 30 + i * 5, 70 - i * 5);
litest_pop_event_frame(dev);
libinput_dispatch(li);
}
while ((event = libinput_get_event(li))) {
enum libinput_event_type type;
type = libinput_event_get_type(event);
if (type >= LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN &&
type <= LIBINPUT_EVENT_GESTURE_PINCH_END) {
struct libinput_event_gesture *g;
struct libinput_event *base;
g = libinput_event_get_gesture_event(event);
base = libinput_event_gesture_get_base_event(g);
ck_assert(event == base);
gestures++;
litest_disable_log_handler(li);
ck_assert(libinput_event_get_device_notify_event(event) == NULL);
ck_assert(libinput_event_get_pointer_event(event) == NULL);
ck_assert(libinput_event_get_keyboard_event(event) == NULL);
ck_assert(libinput_event_get_touch_event(event) == NULL);
litest_restore_log_handler(li);
}
libinput_event_destroy(event);
}
ck_assert_int_gt(gestures, 0);
}
END_TEST
START_TEST(event_conversion_tablet)
{
struct litest_device *dev = litest_current_device();
@ -728,6 +781,7 @@ litest_setup_tests(void)
litest_add_for_device("events:conversion", event_conversion_pointer_abs, LITEST_XEN_VIRTUAL_POINTER);
litest_add_for_device("events:conversion", event_conversion_key, LITEST_KEYBOARD);
litest_add_for_device("events:conversion", event_conversion_touch, LITEST_WACOM_TOUCH);
litest_add_for_device("events:conversion", event_conversion_gesture, LITEST_BCM5974);
litest_add_for_device("events:conversion", event_conversion_tablet, LITEST_WACOM_CINTIQ);
litest_add_no_device("bitfield_helpers", bitfield_helpers);

View file

@ -197,7 +197,7 @@ START_TEST(pointer_motion_relative_min_decel)
ck_assert((evx == 0.0) == (dx == 0));
ck_assert((evy == 0.0) == (dy == 0));
len = vector_length(evx, evy);
len = hypot(evx, evy);
ck_assert(fabs(len) >= 0.3);
libinput_event_destroy(event);
@ -251,7 +251,6 @@ START_TEST(pointer_absolute_initial_state)
struct libinput_event_pointer *p1, *p2;
int axis = _i; /* looped test */
dev = litest_current_device();
libinput1 = dev->libinput;
litest_touch_down(dev, 0, 40, 60);
litest_touch_up(dev, 0);

1552
test/touchpad-buttons.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -206,6 +206,14 @@ print_device_notify(struct libinput_event *ev)
printf("-clickfinger");
}
if (libinput_device_config_dwt_is_available(dev)) {
if (libinput_device_config_dwt_get_enabled(dev) ==
LIBINPUT_CONFIG_DWT_ENABLED)
printf(" dwt-on");
else
printf(" dwt-off)");
}
printf("\n");
}
@ -564,8 +572,6 @@ print_gesture_event_with_coords(struct libinput_event *ev)
double dy = libinput_event_gesture_get_dy(t);
double dx_unaccel = libinput_event_gesture_get_dx_unaccelerated(t);
double dy_unaccel = libinput_event_gesture_get_dy_unaccelerated(t);
double scale = libinput_event_gesture_get_scale(t);
double angle = libinput_event_gesture_get_angle_delta(t);
print_event_time(libinput_event_gesture_get_time(t));
@ -573,10 +579,15 @@ print_gesture_event_with_coords(struct libinput_event *ev)
libinput_event_gesture_get_finger_count(t),
dx, dy, dx_unaccel, dy_unaccel);
if (libinput_event_get_type(ev) == LIBINPUT_EVENT_GESTURE_PINCH_UPDATE)
if (libinput_event_get_type(ev) ==
LIBINPUT_EVENT_GESTURE_PINCH_UPDATE) {
double scale = libinput_event_gesture_get_scale(t);
double angle = libinput_event_gesture_get_angle_delta(t);
printf(" %5.2f @ %5.2f\n", scale, angle);
else
} else {
printf("\n");
}
}
static int

View file

@ -654,6 +654,7 @@ main(int argc, char *argv[])
window_init(&w);
sockets_init(li);
handle_event_libinput(NULL, 0, li);
gtk_main();

View file

@ -35,15 +35,6 @@
#include "shared.h"
static inline const char*
bool_to_str(bool b)
{
if (b)
return "yes";
else
return "no";
}
static const char *
tap_default(struct libinput_device *device)
{
@ -181,6 +172,18 @@ click_defaults(struct libinput_device *device)
return str;
}
static const char *
dwt_default(struct libinput_device *device)
{
if (!libinput_device_config_dwt_is_available(device))
return "n/a";
if (libinput_device_config_dwt_get_default_enabled(device))
return "enabled";
else
return "disabled";
}
static void
print_device_notify(struct libinput_event *ev)
{
@ -247,6 +250,8 @@ print_device_notify(struct libinput_event *ev)
printf("Click methods: %s\n", str);
free(str);
printf("Disable-w-typing: %s\n", dwt_default(dev));
printf("\n");
}

View file

@ -53,6 +53,8 @@ enum options {
OPT_LEFT_HANDED_DISABLE,
OPT_MIDDLEBUTTON_ENABLE,
OPT_MIDDLEBUTTON_DISABLE,
OPT_DWT_ENABLE,
OPT_DWT_DISABLE,
OPT_CLICK_METHOD,
OPT_SCROLL_METHOD,
OPT_SCROLL_BUTTON,
@ -87,6 +89,8 @@ tools_usage()
"--disable-left-handed.... enable/disable left-handed button configuration\n"
"--enable-middlebutton\n"
"--disable-middlebutton.... enable/disable middle button emulation\n"
"--enable-dwt\n"
"--disable-dwt..... enable/disable disable-while-typing\n"
"--set-click-method=[none|clickfinger|buttonareas] .... set the desired click method\n"
"--set-scroll-method=[none|twofinger|edge|button] ... set the desired scroll method\n"
"--set-scroll-button=BTN_MIDDLE ... set the button to the given button code\n"
@ -115,6 +119,7 @@ tools_init_context(struct tools_context *context)
options->natural_scroll = -1;
options->left_handed = -1;
options->middlebutton = -1;
options->dwt = -1;
options->click_method = -1;
options->scroll_method = -1;
options->scroll_button = -1;
@ -147,6 +152,8 @@ tools_parse_args(int argc, char **argv, struct tools_context *context)
{ "disable-left-handed", 0, 0, OPT_LEFT_HANDED_DISABLE },
{ "enable-middlebutton", 0, 0, OPT_MIDDLEBUTTON_ENABLE },
{ "disable-middlebutton", 0, 0, OPT_MIDDLEBUTTON_DISABLE },
{ "enable-dwt", 0, 0, OPT_DWT_ENABLE },
{ "disable-dwt", 0, 0, OPT_DWT_DISABLE },
{ "set-click-method", 1, 0, OPT_CLICK_METHOD },
{ "set-scroll-method", 1, 0, OPT_SCROLL_METHOD },
{ "set-scroll-button", 1, 0, OPT_SCROLL_BUTTON },
@ -212,6 +219,12 @@ tools_parse_args(int argc, char **argv, struct tools_context *context)
case OPT_MIDDLEBUTTON_DISABLE:
options->middlebutton = 0;
break;
case OPT_DWT_ENABLE:
options->dwt = LIBINPUT_CONFIG_DWT_ENABLED;
break;
case OPT_DWT_DISABLE:
options->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
break;
case OPT_CLICK_METHOD:
if (!optarg) {
tools_usage();
@ -419,6 +432,9 @@ tools_device_apply_config(struct libinput_device *device,
libinput_device_config_middle_emulation_set_enabled(device,
options->middlebutton);
if (options->dwt != -1)
libinput_device_config_dwt_set_enabled(device, options->dwt);
if (options->click_method != -1)
libinput_device_config_click_set_method(device, options->click_method);

View file

@ -47,6 +47,7 @@ struct tools_options {
enum libinput_config_scroll_method scroll_method;
int scroll_button;
double speed;
int dwt;
};
struct tools_context {

View file

@ -34,6 +34,12 @@ libinput:touchpad:input:b0005v05ACp*
LIBINPUT_MODEL_APPLE_TOUCHPAD=1
LIBINPUT_ATTR_SIZE_HINT=104x75
##########################################
# Elantech
##########################################
libinput:name:*ETPS/2 Elantech Touchpad*:dmi:*
LIBINPUT_ATTR_RESOLUTION_HINT=31x31
##########################################
# Google
##########################################
@ -64,6 +70,12 @@ libinput:name:Atmel maXTouch Touchpad:dmi:*svn*GOOGLE*:pn*Samus*
libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO:*:pvrThinkPadX230*
LIBINPUT_MODEL_LENOVO_X230=1
##########################################
# Synaptics
##########################################
libinput:touchpad:input:b0011v0002p0007*
LIBINPUT_MODEL_SYNAPTICS_SERIAL_TOUCHPAD=1
##########################################
# System76
##########################################

View file

@ -25,18 +25,12 @@ KERNELS=="*input*", \
IMPORT{builtin}="hwdb 'libinput:name:$attr{name}:fwversion:$env{LIBINPUT_MODEL_FIRMWARE_VERSION}'"
# End of touchpad firmware detection
# Matches below are exclusive, if one matches we skip the rest
# hwdb matches:
#
# libinput:touchpad:<modalias>
ENV{ID_INPUT_TOUCHPAD}=="1", \
IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=libinput:touchpad:", \
GOTO="libinput_model_quirks_end"
IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=libinput:touchpad:"
# libinput:name:<name>:dmi:<dmi string>
KERNELS=="input*", \
IMPORT{builtin}="hwdb 'libinput:name:$attr{name}:$attr{[dmi/id]modalias}'", \
GOTO="libinput_model_quirks_end"
IMPORT{builtin}="hwdb 'libinput:name:$attr{name}:$attr{[dmi/id]modalias}'"
LABEL="libinput_model_quirks_end"

View file

@ -4,7 +4,7 @@ udev_PROGRAMS = libinput-device-group \
litest_rules = 80-libinput-device-groups-litest.rules \
90-libinput-model-quirks-litest.rules
udev_SCRIPTS = $(litest_rules)
noinst_SCRIPTS = $(litest_rules)
libinput_device_group_SOURCES = libinput-device-group.c
libinput_device_group_CFLAGS = $(LIBUDEV_CFLAGS) $(GCC_CFLAGS)