Merge branch 'master' into tablet-support

This commit is contained in:
Peter Hutterer 2015-05-22 14:21:21 +10:00
commit 087d25a54e
69 changed files with 10085 additions and 1379 deletions

View file

@ -1,3 +1,4 @@
Copyright © 2006-2009 Simon Thum
Copyright © 2008-2012 Kristian Høgsberg
Copyright © 2010-2012 Intel Corporation
Copyright © 2010-2011 Benjamin Franzke

View file

@ -1,7 +1,7 @@
AC_PREREQ([2.64])
m4_define([libinput_major_version], [0])
m4_define([libinput_minor_version], [13])
m4_define([libinput_minor_version], [15])
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=10:1:0
LIBINPUT_LT_VERSION=12:0:2
AC_SUBST(LIBINPUT_LT_VERSION)
AM_SILENT_RULES([yes])
@ -60,6 +60,20 @@ PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(MTDEV, [mtdev >= 1.1.0])
PKG_CHECK_MODULES(LIBUDEV, [libudev])
PKG_CHECK_MODULES(LIBEVDEV, [libevdev >= 0.4])
PKG_CHECK_MODULES(LIBUNWIND,
[libunwind],
[HAVE_LIBUNWIND=yes],
[HAVE_LIBUNWIND=no])
if test "x$HAVE_LIBUNWIND" = "xyes"; then
AC_DEFINE(HAVE_LIBUNWIND, 1, [Have libunwind support])
fi
AM_CONDITIONAL(HAVE_LIBUNWIND, [test "x$HAVE_LIBUNWIND" = xyes])
AC_PATH_PROG(ADDR2LINE, [addr2line])
if test "x$ADDR2LINE" != "x"; then
AC_DEFINE_UNQUOTED(HAVE_ADDR2LINE, 1, [addr2line found])
AC_DEFINE_UNQUOTED(ADDR2LINE, ["$ADDR2LINE"], [Path to addr2line])
fi
AC_CHECK_LIB([m], [atan2])
AC_CHECK_LIB([rt], [clock_gettime])
@ -201,5 +215,6 @@ AC_MSG_RESULT([
Build documentation ${build_documentation}
Build tests ${build_tests}
Tests use valgrind ${VALGRIND}
Tests use libunwind ${HAVE_LIBUNWIND}
Build GUI event tool ${build_eventgui}
])

View file

@ -1,4 +1,7 @@
EXTRA_DIST = touchpad-tap-state-machine.svg touchpad-softbutton-state-machine.svg
EXTRA_DIST = \
middle-button-emulation.svg \
touchpad-tap-state-machine.svg \
touchpad-softbutton-state-machine.svg
if BUILD_DOCS
@ -16,7 +19,8 @@ header_files = \
$(srcdir)/seats.dox \
$(srcdir)/t440-support.dox \
$(srcdir)/tablet-support.dox \
$(srcdir)/tapping.dox
$(srcdir)/tapping.dox \
$(srcdir)/test-suite.dox
diagram_files = \
$(srcdir)/dot/seats-sketch.gv \

View file

@ -44,7 +44,10 @@ udev_device_type.</dt>
ID_INPUT_TOUCHSCREEN, ID_INPUT_TABLET, ID_INPUT_JOYSTICK,
ID_INPUT_ACCELEROMETER</dt>
<dd>If any of the above is set, libinput initializes the device as the given
type, see @ref udev_device_type.</dd>
type, see @ref udev_device_type. Note that for historical reasons more than
one of these may be set at any time, libinput will select only one of these
to determine the device type. To ensure libinput selects the correct device
type, only set one of them.</dd>
<dt>WL_SEAT</dt>
<dd>Assigns the logical seat for this device. See
libinput_seat_get_logical_name()
@ -57,6 +60,13 @@ See @ref motion_normalization for details.
<dd>The angle in degrees for each click on a mouse wheel. See
libinput_pointer_get_axis_source() for details.
</dd>
<dt>POINTINGSTICK_CONST_ACCEL</dt>
<dd>A constant (linear) acceleration factor to apply to pointingstick deltas
to normalize them.
<dt>LIBINPUT_MODEL_*</dt>
<dd><b>This prefix is reserved as private API, do not use.</b>. See @ref
model_specific_configuration for details.
</dd>
</dl>
Below is an example udev rule to assign "seat1" to a device from vendor
@ -91,4 +101,16 @@ ACTION=="add|change", KERNEL=="event[0-9]*", ENV{ID_VENDOR_ID}=="012a", \
ENV{ID_MODEL_ID}=="034b", ENV{ID_INPUT_TOUCHPAD}="", ENV{ID_INPUT_TABLET}="1"
@endcode
@section model_specific_configuration Model-specific configuration
libinput reserves the property prefix <b>LIBINPUT_MODEL_</b> for
model-specific configuration. <b>This prefix is reserved as private API, do
not use.</b>
The effect of this property may be to enable or disable certain
features on a specific device or set of devices, to change configuration
defaults or any other reason. The effects of setting this property, the
format of the property and the value of the property are subject to change
at any time.
*/

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 112 KiB

View file

@ -23,6 +23,10 @@ screen, it is common for a finger to start inside an exclusion zone and move
rapidly across the touchpad. libinput detects such movements and avoids palm
detection on such touch sequences.
Each exclusion zone is divided into a top part and a bottom part. A touch
starting in the top part of the exclusion zone does not trigger a
tap (see @ref tapping).
In the diagram below, the exclusion zones are painted red.
Touch 'A' starts inside the exclusion zone and moves
almost vertically. It is considered a palm and ignored for cursor movement,
@ -31,6 +35,11 @@ despite moving out of the exclusion zone.
Touch 'B' starts inside the exclusion zone but moves horizontally out of the
zone. It is considered a valid touch and controls the cursor.
Touch 'C' occurs in the top part of the exclusion zone. Despite being a
tapping motion, it does not generate an emulated button event. Touch 'D'
likewise occurs within the exclusion zone but in the bottom half. libinput
will generate a button event for this touch.
@image html palm-detection.svg
@section trackpoint-disabling Palm detection during trackpoint use
@ -48,5 +57,26 @@ the palm on the touchpad while using the trackstick).
If the touchpad is disabled, the @ref t440_support "top software buttons"
remain enabled.
@section disable-while-typing Disable-while-typing
libinput automatically disables the touchpad for a timeout after a key
press, a feature traditionally referred to as "disable while typing" and
previously available through the
[syndaemon(1)](http://linux.die.net/man/1/syndaemon) command. libinput does
not require an external command and the feature is currently enabled for all
touchpads but will be reduced in the future to only apply to touchpads where
finger width or pressure data is unreliable.
Notable behaviors of libinput's disable-while-typing feature:
- Two different timeouts are used, after a single key press the timeout is
short to ensure responsiveness. After multiple key events, the timeout is
longer to avoid accidental pointer manipulation while typing.
- Some keys do not trigger the timeout, specifically some modifier keys
(Ctrl, Alt, Shift, and Fn). Actions such as Ctrl + click thus stay
responsive.
- Touches started while the touchpad is disabled do not control the cursor,
it is thus possible to rest the palm on the touchpad while typing.
- Physical buttons work even while the touchpad is disabled. This includes
software-emulated buttons.
*/

View file

@ -1,7 +1,20 @@
/**
@page scrolling Scrolling
libinput supports three different types of scrolling behavior.
libinput supports three different types of scrolling methods: @ref
twofinger_scrolling, @ref edge_scrolling and @ref button_scrolling. Some devices
support multiple methods, though only one can be enabled at a time. See
libinput_device_config_scroll_set_method() for documentation on how to
switch methods and libinput_device_config_scroll_get_methods() for
documentation on how to query a device for available scroll methods.
Scroll movements provide vertical and horizontal directions, each
scroll event contains both directions where applicable, see
libinput_event_pointer_get_axis_value(). libinput does not provide separate
toggles to enable or disable horizontal scrolling. Instead, horizontal
scrolling is always enabled. This is intentional, libinput does not have
enough context to know when horizontal scrolling is appropriate for a given
widget. The task of filtering horizontal movements is up to the caller.
@section twofinger_scrolling Two-finger scrolling
@ -16,7 +29,6 @@ For scrolling to trigger, a built-in distance threshold has to be met but once
engaged any movement will scroll. In other words, to start scrolling a
sufficiently large movement is required, once scrolling tiny amounts of
movements will translate into tiny scroll movements.
Scrolling in both directions at once is possible by meeting the required
distance thresholds to enable each direction separately.
@ -28,23 +40,27 @@ scroll).
@image html edge-scrolling.svg "Vertical and horizontal edge scrolling"
Due to the layout of the edges, diagonal scrolling is not possible.
Due to the layout of the edges, diagonal scrolling is not possible. The
behavior of edge scrolling using both edges at the same time is undefined.
Edge scrolling conflicts with @ref clickpad_softbuttons and is
not usually available on clickpads.
not usually available on clickpads. See
http://who-t.blogspot.com.au/2015/03/why-libinput-doesnt-support-edge.html
for details.
@section button_scrolling On-Button scrolling
Scrolling when a button is held down is available on selected devices. The
motion of a device is converted into scrolling motion.
For example, Lenovo devices provide a
On-button scrolling converts the motion of a device into scroll events while
a designated button is held down. For example, Lenovo devices provide a
<a href="http://en.wikipedia.org/wiki/Pointing_stick">pointing stick</a> that emulates
scroll events when the trackstick's middle mouse button is held down.
@image html button-scrolling.svg "Button scrolling"
Note that libinput's @ref t440_support enables the use of the middle
The button may be changed with
libinput_device_config_scroll_set_button() but must be on the same device as
the motion events. Cross-device scrolling is not supported but
for one exception: libinput's @ref t440_support enables the use of the middle
button for button scrolling (even when the touchpad is disabled).
*/

View file

@ -2,14 +2,66 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="393.62857"
height="268.62857"
id="svg2">
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="palm-detection.svg">
<metadata
id="metadata3479">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1136"
id="namedview3477"
showgrid="false"
inkscape:zoom="3.5662625"
inkscape:cx="199.35048"
inkscape:cy="156.74673"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<defs
id="defs4">
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0.0"
refX="0.0"
id="marker4663"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4407"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
transform="scale(0.8) translate(12.5,0)" />
</marker>
<marker
refX="0"
refY="0"
@ -59,64 +111,111 @@
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
</defs>
<g
transform="translate(343.95712,-527.33359)"
id="layer3"
style="display:inline">
<rect
width="386.42856"
height="261.42856"
x="-340.35712"
y="530.93359"
id="rect2858-0"
style="color:#000000;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:7.20000076;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="65.281105"
height="254.3844"
x="-336.88608"
y="534.46918"
id="rect12924"
style="color:#000000;fill:#ff0000;fill-opacity:0.32017547;fill-rule:nonzero;stroke:none;stroke-width:3.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="65.281105"
height="254.3844"
x="-22.72864"
y="534.21661"
id="rect13482"
style="color:#000000;fill:#ff0000;fill-opacity:0.32017547;fill-rule:nonzero;stroke:none;stroke-width:3.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
d="m 38.928571,67.914286 c 0,0 3.508205,24.810617 9.642857,57.857144 6.134651,33.04652 23.277202,79.68584 89.642852,90.35714"
transform="translate(-343.95712,527.33359)"
id="path13492"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 1;stroke-dashoffset:0;marker-mid:none;marker-end:url(#Arrow1Lend-2)" />
<text
x="-310.74283"
y="590.96222"
id="text13874"
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Utopia;-inkscape-font-specification:Utopia"><tspan
x="-310.74283"
y="590.96222"
id="tspan13876"
style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial">A</tspan></text>
<text
x="7.8971062"
y="626.08258"
id="text13874-8"
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Utopia;-inkscape-font-specification:Utopia"><tspan
x="7.8971062"
y="626.08258"
id="tspan13876-7"
style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial">B</tspan></text>
<path
d="m 347.5,90.414286 c 0,0 -28.20972,-6.408104 -85,-6.071429 -22.06971,0.130838 -66.07143,4.285715 -66.07143,4.285715"
transform="translate(-343.95712,527.33359)"
id="path13903"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow1Lend-2)" />
</g>
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:7.20000076;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect2858-0"
y="3.6000037"
x="3.6000032"
height="261.42856"
width="386.42856" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ff0000;fill-opacity:0.32017547;fill-rule:nonzero;stroke:none;stroke-width:3.5;marker:none;enable-background:accumulate"
id="rect12924"
y="7.1355872"
x="7.0710421"
height="254.3844"
width="65.281105" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ff0000;fill-opacity:0.32017547;fill-rule:nonzero;stroke:none;stroke-width:3.5;marker:none;enable-background:accumulate"
id="rect13482"
y="6.8830237"
x="321.22849"
height="254.3844"
width="65.281105" />
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:6, 1;stroke-dashoffset:0;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow1Lend-2)"
id="path13492"
d="m 38.928571,67.914286 c 0,0 3.508205,24.810617 9.642857,57.857144 6.134651,33.04652 23.277202,79.68584 89.642852,90.35714" />
<rect
style="fill:#000000;fill-opacity:0.3559322;fill-rule:evenodd;stroke:none;stroke-width:3.30527353px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="rect3490"
width="65.272476"
height="136.21509"
x="7.0411549"
y="7.0411549" />
<text
sodipodi:linespacing="100%"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:100%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
xml:space="preserve"
id="text13874"
y="63.628628"
x="33.214291"><tspan
style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial"
id="tspan13876"
y="63.628628"
x="33.214291">A</tspan></text>
<rect
style="fill:#000000;fill-opacity:0.3559322;fill-rule:evenodd;stroke:none;stroke-width:3.30527353px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="rect3490-2"
width="65.272476"
height="136.21509"
x="321.23563"
y="6.7607527" />
<text
sodipodi:linespacing="100%"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:100%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none"
xml:space="preserve"
id="text13874-8"
y="98.748993"
x="351.85422"><tspan
style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial"
id="tspan13876-7"
y="98.748993"
x="351.85422">B</tspan></text>
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow1Lend-2)"
id="path13903"
d="m 347.5,90.414286 c 0,0 -28.20972,-6.408104 -85,-6.071429 -22.06971,0.130838 -66.07143,4.285715 -66.07143,4.285715" />
<g
transform="translate(343.95712,-527.33359)"
id="layer1"
style="display:inline" />
<text
sodipodi:linespacing="100%"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:100%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none"
xml:space="preserve"
id="text13874-8-1"
y="46.009491"
x="342.27759"><tspan
style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial"
id="tspan13876-7-9"
y="46.009491"
x="342.27759">C</tspan></text>
<text
sodipodi:linespacing="100%"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:100%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none"
xml:space="preserve"
id="text13874-8-1-4"
y="215.65927"
x="37.970726"><tspan
style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial"
id="tspan13876-7-9-5"
y="215.65927"
x="37.970726">D</tspan></text>
<circle
style="fill:#000000;fill-opacity:1;stroke:none;stroke-opacity:1"
id="path4401"
cx="-360.181"
cy="24.53549"
r="4.0658817"
transform="scale(-1,1)" />
<circle
style="fill:#000000;fill-opacity:1;stroke:none;stroke-opacity:1"
id="path4401-9"
cx="-36.452721"
cy="194.8819"
r="4.0658817"
transform="scale(-1,1)" />
</svg>

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View file

@ -33,6 +33,10 @@ continue the dragging process, so that multiple touchpad-widths of distance
can be covered easily. If two-fingers are supported by the hardware, a
second finger can be used to drag while the first is held in-place.
An alternative method to end a drag process is to tap immediately after
lifting the finger. The full sequence is thus: tap, finger down, drag,
finger up, tap.
@section tap_constraints Constraints while tapping
A couple of constraints apply to the contact to be converted into a press, the most common ones are:

92
doc/test-suite.dox Normal file
View file

@ -0,0 +1,92 @@
/**
@page test-suite libinput test suite
The libinput test suite is based on
[Check](http://check.sourceforge.net/doc/check_html/) and runs automatically
during `make check`. Check itself is wrapped into a libinput-specific test
suite called *litest*. Tests are found in `$srcdir/test/`, the test binaries are
prefixed with `test-` and can be run individually.
@section test-config X.Org config to avoid interference
uinput devices created by the test suite are usually recognised by X as
input devices. All events sent through these devices will generate X events
and interfere with your desktop.
Copy the file `$srcdir/test/50-litest.conf` into your `/etc/X11/xorg.conf.d`
and restart X. This will ignore any litest devices and thus not interfere
with your desktop.
@section test-root Permissions required to run tests
Most tests require the creation of uinput devices and access to the
resulting `/dev/input/eventX` nodes. Some tests require temporary udev rules.
<b>This usually requires the tests to be run as root</b>.
@section test-filtering Selective running of tests
litest's tests are grouped by test groups and devices. A test group is e.g.
"touchpad:tap" and incorporates all tapping-related tests for touchpads.
Each test function is (usually) run with one or more specific devices.
The `--list` commandline argument shows the list of suites and tests.
@code
$ ./test/test-device --list
device:wheel:
wheel only
blackwidow
device:invalid devices:
no device
device:group:
no device
logitech trackball
MS surface cover
mouse_roccat
wheel only
blackwidow
...
@endcode
In the above example, the "device:wheel" suite is run for the "wheel only" and
the "blackwidow" device. Both devices are automatically instantiated through
uinput by litest. The "no device" entry signals that litest does not
instantiate a uinput device for a specific test (though the test itself may
instantiate one).
The `--filter-test` argument enables selective running of tests through
basic shell-style function name matching. For example:
@code
$ ./test/test-touchpad --filter-test="*1fg_tap*"
@endcode
The `--filter-device` argument enables selective running of tests through
basic shell-style device name matching. The device names matched are the
litest-specific shortnames, see the output of `--list`. For example:
@code
$ ./test/test-touchpad --filter-device="synaptics*"
@endcode
The `--filter-group` argument enables selective running of test groups
through basic shell-style test group matching. The test groups matched are
litest-specific test groups, see the output of `--list`. For example:
@code
$ ./test/test-touchpad --filter-group="touchpad:*hover*"
@endcode
The `--filter-device` and `--filter-group` arguments can be combined with
`--list` to show which groups and devices will be affected.
@section test-verbosity Controlling test output
Each test supports the `--verbose` commandline option to enable debugging
output, see libinput_log_set_priority() for details. The `LITEST_VERBOSE`
environment variable, if set, also enables verbose mode.
@code
$ ./test/test-device --verbose
$ LITEST_VERBOSE=1 make check
@endcode
*/

File diff suppressed because it is too large Load diff

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 116 KiB

View file

@ -162,6 +162,7 @@ struct input_keymap_entry {
#define INPUT_PROP_SEMI_MT 0x03 /* touch rectangle only */
#define INPUT_PROP_TOPBUTTONPAD 0x04 /* softbuttons at top of pad */
#define INPUT_PROP_POINTING_STICK 0x05 /* is a pointing stick */
#define INPUT_PROP_ACCELEROMETER 0x06 /* has accelerometer */
#define INPUT_PROP_MAX 0x1f
#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1)
@ -459,7 +460,10 @@ struct input_keymap_entry {
#define KEY_VIDEO_NEXT 241 /* drive next video source */
#define KEY_VIDEO_PREV 242 /* drive previous video source */
#define KEY_BRIGHTNESS_CYCLE 243 /* brightness up, after max is min */
#define KEY_BRIGHTNESS_ZERO 244 /* brightness off, use ambient */
#define KEY_BRIGHTNESS_AUTO 244 /* Set Auto Brightness: manual
brightness control is off,
rely on ambient */
#define KEY_BRIGHTNESS_ZERO KEY_BRIGHTNESS_AUTO
#define KEY_DISPLAY_OFF 245 /* display device to off state */
#define KEY_WWAN 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */
@ -629,6 +633,7 @@ struct input_keymap_entry {
#define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */
#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */
#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */
#define KEY_BRIGHTNESS_TOGGLE KEY_DISPLAYTOGGLE
#define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */
#define KEY_LOGOFF 0x1b1 /* AL Logoff */
@ -720,6 +725,24 @@ struct input_keymap_entry {
#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */
#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */
#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */
#define KEY_JOURNAL 0x242 /* AL Log/Journal/Timecard */
#define KEY_CONTROLPANEL 0x243 /* AL Control Panel */
#define KEY_APPSELECT 0x244 /* AL Select Task/Application */
#define KEY_SCREENSAVER 0x245 /* AL Screen Saver */
#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */
#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */
#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */
#define KEY_KBDINPUTASSIST_PREV 0x260
#define KEY_KBDINPUTASSIST_NEXT 0x261
#define KEY_KBDINPUTASSIST_PREVGROUP 0x262
#define KEY_KBDINPUTASSIST_NEXTGROUP 0x263
#define KEY_KBDINPUTASSIST_ACCEPT 0x264
#define KEY_KBDINPUTASSIST_CANCEL 0x265
#define BTN_TRIGGER_HAPPY 0x2c0
#define BTN_TRIGGER_HAPPY1 0x2c0
#define BTN_TRIGGER_HAPPY2 0x2c1
@ -945,7 +968,8 @@ struct input_keymap_entry {
*/
#define MT_TOOL_FINGER 0
#define MT_TOOL_PEN 1
#define MT_TOOL_MAX 1
#define MT_TOOL_PALM 2
#define MT_TOOL_MAX 2
/*
* Values describing the status of a force-feedback effect

View file

@ -1,5 +1,6 @@
lib_LTLIBRARIES = libinput.la
noinst_LTLIBRARIES = libinput-util.la
noinst_LTLIBRARIES = libinput-util.la \
libfilter.la
include_HEADERS = \
libinput.h
@ -10,14 +11,15 @@ libinput_la_SOURCES = \
libinput-private.h \
evdev.c \
evdev.h \
evdev-tablet.c \
evdev-tablet.h \
evdev-middle-button.c \
evdev-mt-touchpad.c \
evdev-mt-touchpad.h \
evdev-mt-touchpad-tap.c \
evdev-mt-touchpad-buttons.c \
evdev-mt-touchpad-edge-scroll.c \
evdev-mt-touchpad-gestures.c \
evdev-tablet.c \
evdev-tablet.h \
filter.c \
filter.h \
filter-private.h \
@ -52,6 +54,13 @@ libinput_util_la_CFLAGS = -I$(top_srcdir)/include \
$(LIBUDEV_CFLAGS) \
$(GCC_CFLAGS)
libfilter_la_SOURCES = \
filter.c \
filter.h \
filter-private.h
libfilter_la_LIBADD =
libfilter_la_CFLAGS =
libinput_la_LDFLAGS = -version-info $(LIBINPUT_LT_VERSION) -shared \
-Wl,--version-script=$(srcdir)/libinput.sym

716
src/evdev-middle-button.c Normal file
View file

@ -0,0 +1,716 @@
/*
* Copyright © 2014 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The copyright holders make
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdint.h>
#include "evdev.h"
#define MIDDLEBUTTON_TIMEOUT 50
/*****************************************
* BEFORE YOU EDIT THIS FILE, look at the state diagram in
* doc/middle-button-emulation-state-machine.svg, or online at
* https://drive.google.com/file/d/0B1NwWmji69nodUJncXRMc1FvY1k/view?usp=sharing
* (it's a http://draw.io diagram)
*
* Any changes in this file must be represented in the diagram.
*
* Note in regards to the state machine: it only handles left, right and
* emulated middle button clicks, all other button events are passed
* through. When in the PASSTHROUGH state, all events are passed through
* as-is.
*/
#define CASE_RETURN_STRING(a) case a: return #a;
static inline const char*
middlebutton_state_to_str(enum evdev_middlebutton_state state)
{
switch (state) {
CASE_RETURN_STRING(MIDDLEBUTTON_IDLE);
CASE_RETURN_STRING(MIDDLEBUTTON_LEFT_DOWN);
CASE_RETURN_STRING(MIDDLEBUTTON_RIGHT_DOWN);
CASE_RETURN_STRING(MIDDLEBUTTON_MIDDLE);
CASE_RETURN_STRING(MIDDLEBUTTON_LEFT_UP_PENDING);
CASE_RETURN_STRING(MIDDLEBUTTON_RIGHT_UP_PENDING);
CASE_RETURN_STRING(MIDDLEBUTTON_PASSTHROUGH);
CASE_RETURN_STRING(MIDDLEBUTTON_IGNORE_LR);
CASE_RETURN_STRING(MIDDLEBUTTON_IGNORE_L);
CASE_RETURN_STRING(MIDDLEBUTTON_IGNORE_R);
}
return NULL;
}
static inline const char*
middlebutton_event_to_str(enum evdev_middlebutton_event event)
{
switch (event) {
CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_L_DOWN);
CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_R_DOWN);
CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_OTHER);
CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_L_UP);
CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_R_UP);
CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_TIMEOUT);
CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_ALL_UP);
}
return NULL;
}
static void
middlebutton_state_error(struct evdev_device *device,
enum evdev_middlebutton_event event)
{
log_bug_libinput(device->base.seat->libinput,
"Invalid event %s in middle btn state %s\n",
middlebutton_event_to_str(event),
middlebutton_state_to_str(device->middlebutton.state));
}
static void
middlebutton_timer_set(struct evdev_device *device, uint64_t now)
{
libinput_timer_set(&device->middlebutton.timer,
now + MIDDLEBUTTON_TIMEOUT);
}
static void
middlebutton_timer_cancel(struct evdev_device *device)
{
libinput_timer_cancel(&device->middlebutton.timer);
}
static inline void
middlebutton_set_state(struct evdev_device *device,
enum evdev_middlebutton_state state,
uint64_t now)
{
switch (state) {
case MIDDLEBUTTON_LEFT_DOWN:
case MIDDLEBUTTON_RIGHT_DOWN:
middlebutton_timer_set(device, now);
device->middlebutton.first_event_time = now;
break;
case MIDDLEBUTTON_IDLE:
case MIDDLEBUTTON_MIDDLE:
case MIDDLEBUTTON_LEFT_UP_PENDING:
case MIDDLEBUTTON_RIGHT_UP_PENDING:
case MIDDLEBUTTON_PASSTHROUGH:
case MIDDLEBUTTON_IGNORE_LR:
case MIDDLEBUTTON_IGNORE_L:
case MIDDLEBUTTON_IGNORE_R:
middlebutton_timer_cancel(device);
break;
}
device->middlebutton.state = state;
}
static void
middlebutton_post_event(struct evdev_device *device,
uint64_t now,
int button,
enum libinput_button_state state)
{
evdev_pointer_notify_button(device,
now,
button,
state);
}
static int
evdev_middlebutton_idle_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
middlebutton_set_state(device, MIDDLEBUTTON_LEFT_DOWN, time);
break;
case MIDDLEBUTTON_EVENT_R_DOWN:
middlebutton_set_state(device, MIDDLEBUTTON_RIGHT_DOWN, time);
break;
case MIDDLEBUTTON_EVENT_OTHER:
return 0;
case MIDDLEBUTTON_EVENT_R_UP:
case MIDDLEBUTTON_EVENT_L_UP:
case MIDDLEBUTTON_EVENT_TIMEOUT:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_ALL_UP:
break;
}
return 1;
}
static int
evdev_middlebutton_ldown_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_R_DOWN:
middlebutton_post_event(device, time,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
break;
case MIDDLEBUTTON_EVENT_OTHER:
middlebutton_post_event(device, time,
BTN_LEFT,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_set_state(device,
MIDDLEBUTTON_PASSTHROUGH,
time);
return 0;
case MIDDLEBUTTON_EVENT_R_UP:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_L_UP:
middlebutton_post_event(device,
device->middlebutton.first_event_time,
BTN_LEFT,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_post_event(device, time,
BTN_LEFT,
LIBINPUT_BUTTON_STATE_RELEASED);
middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
break;
case MIDDLEBUTTON_EVENT_TIMEOUT:
middlebutton_post_event(device,
device->middlebutton.first_event_time,
BTN_LEFT,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_set_state(device,
MIDDLEBUTTON_PASSTHROUGH,
time);
break;
case MIDDLEBUTTON_EVENT_ALL_UP:
middlebutton_state_error(device, event);
break;
}
return 1;
}
static int
evdev_middlebutton_rdown_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
middlebutton_post_event(device, time,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
break;
case MIDDLEBUTTON_EVENT_R_DOWN:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_OTHER:
middlebutton_post_event(device,
device->middlebutton.first_event_time,
BTN_RIGHT,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_set_state(device,
MIDDLEBUTTON_PASSTHROUGH,
time);
return 0;
case MIDDLEBUTTON_EVENT_R_UP:
middlebutton_post_event(device,
device->middlebutton.first_event_time,
BTN_RIGHT,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_post_event(device, time,
BTN_RIGHT,
LIBINPUT_BUTTON_STATE_RELEASED);
middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
break;
case MIDDLEBUTTON_EVENT_L_UP:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_TIMEOUT:
middlebutton_post_event(device,
device->middlebutton.first_event_time,
BTN_RIGHT,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_set_state(device,
MIDDLEBUTTON_PASSTHROUGH,
time);
break;
case MIDDLEBUTTON_EVENT_ALL_UP:
middlebutton_state_error(device, event);
break;
}
return 1;
}
static int
evdev_middlebutton_middle_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
case MIDDLEBUTTON_EVENT_R_DOWN:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_OTHER:
middlebutton_post_event(device, time,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_LR, time);
return 0;
case MIDDLEBUTTON_EVENT_R_UP:
middlebutton_post_event(device, time,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
middlebutton_set_state(device,
MIDDLEBUTTON_LEFT_UP_PENDING,
time);
break;
case MIDDLEBUTTON_EVENT_L_UP:
middlebutton_post_event(device, time,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
middlebutton_set_state(device,
MIDDLEBUTTON_RIGHT_UP_PENDING,
time);
break;
case MIDDLEBUTTON_EVENT_TIMEOUT:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_ALL_UP:
middlebutton_state_error(device, event);
break;
}
return 1;
}
static int
evdev_middlebutton_lup_pending_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_R_DOWN:
middlebutton_post_event(device, time,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
break;
case MIDDLEBUTTON_EVENT_OTHER:
middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_L, time);
return 0;
case MIDDLEBUTTON_EVENT_R_UP:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_L_UP:
middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
break;
case MIDDLEBUTTON_EVENT_TIMEOUT:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_ALL_UP:
middlebutton_state_error(device, event);
break;
}
return 1;
}
static int
evdev_middlebutton_rup_pending_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
middlebutton_post_event(device, time,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
break;
case MIDDLEBUTTON_EVENT_R_DOWN:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_OTHER:
middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_R, time);
return 0;
case MIDDLEBUTTON_EVENT_R_UP:
middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
break;
case MIDDLEBUTTON_EVENT_L_UP:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_TIMEOUT:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_ALL_UP:
middlebutton_state_error(device, event);
break;
}
return 1;
}
static int
evdev_middlebutton_passthrough_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
case MIDDLEBUTTON_EVENT_R_DOWN:
case MIDDLEBUTTON_EVENT_OTHER:
case MIDDLEBUTTON_EVENT_R_UP:
case MIDDLEBUTTON_EVENT_L_UP:
return 0;
case MIDDLEBUTTON_EVENT_TIMEOUT:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_ALL_UP:
middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
break;
}
return 1;
}
static int
evdev_middlebutton_ignore_lr_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
case MIDDLEBUTTON_EVENT_R_DOWN:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_OTHER:
return 0;
case MIDDLEBUTTON_EVENT_R_UP:
middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_L, time);
break;
case MIDDLEBUTTON_EVENT_L_UP:
middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_R, time);
break;
case MIDDLEBUTTON_EVENT_TIMEOUT:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_ALL_UP:
middlebutton_state_error(device, event);
break;
}
return 1;
}
static int
evdev_middlebutton_ignore_l_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_R_DOWN:
return 0;
case MIDDLEBUTTON_EVENT_OTHER:
case MIDDLEBUTTON_EVENT_R_UP:
return 0;
case MIDDLEBUTTON_EVENT_L_UP:
middlebutton_set_state(device,
MIDDLEBUTTON_PASSTHROUGH,
time);
break;
case MIDDLEBUTTON_EVENT_TIMEOUT:
case MIDDLEBUTTON_EVENT_ALL_UP:
middlebutton_state_error(device, event);
break;
}
return 1;
}
static int
evdev_middlebutton_ignore_r_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
return 0;
case MIDDLEBUTTON_EVENT_R_DOWN:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_OTHER:
return 0;
case MIDDLEBUTTON_EVENT_R_UP:
middlebutton_set_state(device,
MIDDLEBUTTON_PASSTHROUGH,
time);
break;
case MIDDLEBUTTON_EVENT_L_UP:
return 0;
case MIDDLEBUTTON_EVENT_TIMEOUT:
case MIDDLEBUTTON_EVENT_ALL_UP:
break;
}
return 1;
}
static int
evdev_middlebutton_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
int rc;
enum evdev_middlebutton_state current;
current = device->middlebutton.state;
switch (current) {
case MIDDLEBUTTON_IDLE:
rc = evdev_middlebutton_idle_handle_event(device, time, event);
break;
case MIDDLEBUTTON_LEFT_DOWN:
rc = evdev_middlebutton_ldown_handle_event(device, time, event);
break;
case MIDDLEBUTTON_RIGHT_DOWN:
rc = evdev_middlebutton_rdown_handle_event(device, time, event);
break;
case MIDDLEBUTTON_MIDDLE:
rc = evdev_middlebutton_middle_handle_event(device, time, event);
break;
case MIDDLEBUTTON_LEFT_UP_PENDING:
rc = evdev_middlebutton_lup_pending_handle_event(device,
time,
event);
break;
case MIDDLEBUTTON_RIGHT_UP_PENDING:
rc = evdev_middlebutton_rup_pending_handle_event(device,
time,
event);
break;
case MIDDLEBUTTON_PASSTHROUGH:
rc = evdev_middlebutton_passthrough_handle_event(device,
time,
event);
break;
case MIDDLEBUTTON_IGNORE_LR:
rc = evdev_middlebutton_ignore_lr_handle_event(device,
time,
event);
break;
case MIDDLEBUTTON_IGNORE_L:
rc = evdev_middlebutton_ignore_l_handle_event(device,
time,
event);
break;
case MIDDLEBUTTON_IGNORE_R:
rc = evdev_middlebutton_ignore_r_handle_event(device,
time,
event);
break;
}
log_debug(device->base.seat->libinput,
"middlebuttonstate: %s → %s → %s, rc %d\n",
middlebutton_state_to_str(current),
middlebutton_event_to_str(event),
middlebutton_state_to_str(device->middlebutton.state),
rc);
return rc;
}
static inline void
evdev_middlebutton_apply_config(struct evdev_device *device)
{
if (device->middlebutton.want_enabled ==
device->middlebutton.enabled)
return;
if (device->middlebutton.button_mask != 0)
return;
device->middlebutton.enabled = device->middlebutton.want_enabled;
}
bool
evdev_middlebutton_filter_button(struct evdev_device *device,
uint64_t time,
int button,
enum libinput_button_state state)
{
enum evdev_middlebutton_event event;
bool is_press = state == LIBINPUT_BUTTON_STATE_PRESSED;
int rc;
unsigned int bit = (button - BTN_LEFT);
uint32_t old_mask = 0;
if (!device->middlebutton.enabled)
return false;
switch (button) {
case BTN_LEFT:
if (is_press)
event = MIDDLEBUTTON_EVENT_L_DOWN;
else
event = MIDDLEBUTTON_EVENT_L_UP;
break;
case BTN_RIGHT:
if (is_press)
event = MIDDLEBUTTON_EVENT_R_DOWN;
else
event = MIDDLEBUTTON_EVENT_R_UP;
break;
/* BTN_MIDDLE counts as "other" and resets middle button
* emulation */
case BTN_MIDDLE:
default:
event = MIDDLEBUTTON_EVENT_OTHER;
break;
}
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",
libevdev_event_code_get_name(EV_KEY,
button));
return true;
}
rc = evdev_middlebutton_handle_event(device, time, event);
old_mask = device->middlebutton.button_mask;
if (is_press)
device->middlebutton.button_mask |= 1 << bit;
else
device->middlebutton.button_mask &= ~(1 << bit);
if (old_mask != device->middlebutton.button_mask &&
device->middlebutton.button_mask == 0) {
evdev_middlebutton_handle_event(device,
time,
MIDDLEBUTTON_EVENT_ALL_UP);
evdev_middlebutton_apply_config(device);
}
return rc;
}
static void
evdev_middlebutton_handle_timeout(uint64_t now, void *data)
{
struct evdev_device *device = (struct evdev_device*)data;
evdev_middlebutton_handle_event(device,
libinput_now(device->base.seat->libinput),
MIDDLEBUTTON_EVENT_TIMEOUT);
}
static int
evdev_middlebutton_is_available(struct libinput_device *device)
{
return 1;
}
static enum libinput_config_status
evdev_middlebutton_set(struct libinput_device *device,
enum libinput_config_middle_emulation_state enable)
{
struct evdev_device *evdev = (struct evdev_device*)device;
switch (enable) {
case LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED:
evdev->middlebutton.want_enabled = true;
break;
case LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED:
evdev->middlebutton.want_enabled = false;
break;
default:
return LIBINPUT_CONFIG_STATUS_INVALID;
}
evdev_middlebutton_apply_config(evdev);
return LIBINPUT_CONFIG_STATUS_SUCCESS;
}
static enum libinput_config_middle_emulation_state
evdev_middlebutton_get(struct libinput_device *device)
{
struct evdev_device *evdev = (struct evdev_device*)device;
return evdev->middlebutton.enabled ?
LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED :
LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
}
static enum libinput_config_middle_emulation_state
evdev_middlebutton_get_default(struct libinput_device *device)
{
struct evdev_device *evdev = (struct evdev_device*)device;
return evdev->middlebutton.enabled_default ?
LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED :
LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
}
void
evdev_init_middlebutton(struct evdev_device *device,
bool enable,
bool want_config)
{
libinput_timer_init(&device->middlebutton.timer,
device->base.seat->libinput,
evdev_middlebutton_handle_timeout,
device);
device->middlebutton.enabled_default = enable;
device->middlebutton.want_enabled = enable;
device->middlebutton.enabled = enable;
if (!want_config)
return;
device->middlebutton.config.available = evdev_middlebutton_is_available;
device->middlebutton.config.set = evdev_middlebutton_set;
device->middlebutton.config.get = evdev_middlebutton_get;
device->middlebutton.config.get_default = evdev_middlebutton_get_default;
device->base.config.middle_emulation = &device->middlebutton.config;
}

View file

@ -144,12 +144,15 @@ tp_button_set_leave_timer(struct tp_dispatch *tp, struct tp_touch *t)
* as described in the state machine diagram.
*/
static void
tp_button_set_state(struct tp_dispatch *tp, struct tp_touch *t,
enum button_state new_state, enum button_event event)
tp_button_set_state(struct tp_dispatch *tp,
struct tp_touch *t,
enum button_state new_state,
enum button_event event)
{
libinput_timer_cancel(&t->button.timer);
t->button.state = new_state;
switch (t->button.state) {
case BUTTON_STATE_NONE:
t->button.curr = 0;
@ -235,7 +238,9 @@ tp_button_bottom_handle_event(struct tp_dispatch *tp,
case BUTTON_EVENT_IN_BOTTOM_R:
case BUTTON_EVENT_IN_BOTTOM_L:
if (event != t->button.curr)
tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM,
tp_button_set_state(tp,
t,
BUTTON_STATE_BOTTOM,
event);
break;
case BUTTON_EVENT_IN_TOP_R:
@ -256,8 +261,8 @@ tp_button_bottom_handle_event(struct tp_dispatch *tp,
static void
tp_button_top_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum button_event event)
struct tp_touch *t,
enum button_event event)
{
switch (event) {
case BUTTON_EVENT_IN_BOTTOM_R:
@ -268,7 +273,9 @@ tp_button_top_handle_event(struct tp_dispatch *tp,
case BUTTON_EVENT_IN_TOP_M:
case BUTTON_EVENT_IN_TOP_L:
if (event != t->button.curr)
tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW,
tp_button_set_state(tp,
t,
BUTTON_STATE_TOP_NEW,
event);
break;
case BUTTON_EVENT_IN_AREA:
@ -286,8 +293,8 @@ tp_button_top_handle_event(struct tp_dispatch *tp,
static void
tp_button_top_new_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum button_event event)
struct tp_touch *t,
enum button_event event)
{
switch(event) {
case BUTTON_EVENT_IN_BOTTOM_R:
@ -298,7 +305,9 @@ tp_button_top_new_handle_event(struct tp_dispatch *tp,
case BUTTON_EVENT_IN_TOP_M:
case BUTTON_EVENT_IN_TOP_L:
if (event != t->button.curr)
tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW,
tp_button_set_state(tp,
t,
BUTTON_STATE_TOP_NEW,
event);
break;
case BUTTON_EVENT_IN_AREA:
@ -320,18 +329,22 @@ tp_button_top_new_handle_event(struct tp_dispatch *tp,
static void
tp_button_top_to_ignore_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum button_event event)
struct tp_touch *t,
enum button_event event)
{
switch(event) {
case BUTTON_EVENT_IN_TOP_R:
case BUTTON_EVENT_IN_TOP_M:
case BUTTON_EVENT_IN_TOP_L:
if (event == t->button.curr)
tp_button_set_state(tp, t, BUTTON_STATE_TOP,
tp_button_set_state(tp,
t,
BUTTON_STATE_TOP,
event);
else
tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW,
tp_button_set_state(tp,
t,
BUTTON_STATE_TOP_NEW,
event);
break;
case BUTTON_EVENT_IN_BOTTOM_R:
@ -352,8 +365,8 @@ tp_button_top_to_ignore_handle_event(struct tp_dispatch *tp,
static void
tp_button_ignore_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum button_event event)
struct tp_touch *t,
enum button_event event)
{
switch (event) {
case BUTTON_EVENT_IN_BOTTOM_R:
@ -426,18 +439,22 @@ tp_button_handle_state(struct tp_dispatch *tp, uint64_t time)
if (t->state == TOUCH_END) {
tp_button_handle_event(tp, t, BUTTON_EVENT_UP, time);
} else if (t->dirty) {
enum button_event event;
if (is_inside_bottom_right_area(tp, t))
tp_button_handle_event(tp, t, BUTTON_EVENT_IN_BOTTOM_R, time);
event = BUTTON_EVENT_IN_BOTTOM_R;
else if (is_inside_bottom_left_area(tp, t))
tp_button_handle_event(tp, t, BUTTON_EVENT_IN_BOTTOM_L, time);
event = BUTTON_EVENT_IN_BOTTOM_L;
else if (is_inside_top_right_area(tp, t))
tp_button_handle_event(tp, t, BUTTON_EVENT_IN_TOP_R, time);
event = BUTTON_EVENT_IN_TOP_R;
else if (is_inside_top_middle_area(tp, t))
tp_button_handle_event(tp, t, BUTTON_EVENT_IN_TOP_M, time);
event = BUTTON_EVENT_IN_TOP_M;
else if (is_inside_top_left_area(tp, t))
tp_button_handle_event(tp, t, BUTTON_EVENT_IN_TOP_L, time);
event = BUTTON_EVENT_IN_TOP_L;
else
tp_button_handle_event(tp, t, BUTTON_EVENT_IN_AREA, time);
event = BUTTON_EVENT_IN_AREA;
tp_button_handle_event(tp, t, event, time);
}
if (tp->queued & TOUCHPAD_EVENT_BUTTON_RELEASE)
tp_button_handle_event(tp, t, BUTTON_EVENT_RELEASE, time);
@ -629,12 +646,25 @@ tp_button_config_click_get_method(struct libinput_device *device)
static enum libinput_config_click_method
tp_click_get_default_method(struct tp_dispatch *tp)
{
struct evdev_device *device = tp->device;
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;
else
return LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
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:
return LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
default:
break;
}
return LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
}
static enum libinput_config_click_method
@ -694,6 +724,10 @@ tp_init_buttons(struct tp_dispatch *tp,
tp_init_top_softbuttons(tp, device, 1.0);
if (!tp->buttons.is_clickpad &&
!libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE))
evdev_init_middlebutton(tp->device, true, false);
tp_for_each_touch(tp, t) {
t->button.state = BUTTON_STATE_NONE;
libinput_timer_init(&t->button.timer,
@ -734,10 +768,10 @@ tp_post_physical_buttons(struct tp_dispatch *tp, uint64_t time)
state = LIBINPUT_BUTTON_STATE_RELEASED;
b = evdev_to_left_handed(tp->device, button);
evdev_pointer_notify_button(tp->device,
time,
b,
state);
evdev_pointer_notify_physical_button(tp->device,
time,
b,
state);
}
button++;
@ -765,8 +799,10 @@ tp_notify_clickpadbutton(struct tp_dispatch *tp,
event.type = EV_KEY;
event.code = button;
event.value = (state == LIBINPUT_BUTTON_STATE_PRESSED) ? 1 : 0;
dispatch->interface->process(dispatch, tp->buttons.trackpoint,
&event, time);
dispatch->interface->process(dispatch,
tp->buttons.trackpoint,
&event,
time);
return 1;
}
@ -779,8 +815,9 @@ tp_notify_clickpadbutton(struct tp_dispatch *tp,
* by the softbutton code with one based on the number of fingers.
*/
if (tp->buttons.click_method == LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER &&
state == LIBINPUT_BUTTON_STATE_PRESSED) {
state == LIBINPUT_BUTTON_STATE_PRESSED) {
switch (tp->nfingers_down) {
case 0:
case 1: button = BTN_LEFT; break;
case 2: button = BTN_RIGHT; break;
case 3: button = BTN_MIDDLE; break;
@ -814,46 +851,48 @@ tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time)
if (current) {
struct tp_touch *t;
uint32_t area = 0;
tp_for_each_touch(tp, t) {
switch (t->button.curr) {
case BUTTON_EVENT_IN_AREA:
button |= AREA;
area |= AREA;
break;
case BUTTON_EVENT_IN_TOP_L:
is_top = 1;
/* fallthrough */
case BUTTON_EVENT_IN_BOTTOM_L:
button |= LEFT;
area |= LEFT;
break;
case BUTTON_EVENT_IN_TOP_M:
is_top = 1;
button |= MIDDLE;
area |= MIDDLE;
break;
case BUTTON_EVENT_IN_TOP_R:
is_top = 1;
/* fallthrough */
case BUTTON_EVENT_IN_BOTTOM_R:
button |= RIGHT;
area |= RIGHT;
break;
default:
break;
}
}
if (button == 0) {
if (area == 0 &&
tp->buttons.click_method != LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) {
/* No touches, wait for a touch before processing */
tp->buttons.click_pending = true;
return 0;
}
if ((button & MIDDLE) || ((button & LEFT) && (button & RIGHT)))
if ((area & MIDDLE) || ((area & LEFT) && (area & RIGHT)))
button = evdev_to_left_handed(tp->device, BTN_MIDDLE);
else if (button & RIGHT)
else if (area & RIGHT)
button = evdev_to_left_handed(tp->device, BTN_RIGHT);
else if (button & LEFT)
else if (area & LEFT)
button = evdev_to_left_handed(tp->device, BTN_LEFT);
else /* main area is always BTN_LEFT */
else /* main or no area (for clickfinger) is always BTN_LEFT */
button = BTN_LEFT;
tp->buttons.active = button;
@ -870,8 +909,11 @@ tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time)
tp->buttons.click_pending = false;
if (button)
return tp_notify_clickpadbutton(tp, time, button, is_top, state);
return tp_notify_clickpadbutton(tp,
time,
button,
is_top,
state);
return 0;
}
@ -893,5 +935,6 @@ tp_button_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
bool
tp_button_is_inside_softbutton_area(struct tp_dispatch *tp, struct tp_touch *t)
{
return is_inside_top_button_area(tp, t) || is_inside_bottom_button_area(tp, t);
return is_inside_top_button_area(tp, t) ||
is_inside_bottom_button_area(tp, t);
}

View file

@ -29,6 +29,8 @@
#include "evdev-mt-touchpad.h"
#define CASE_RETURN_STRING(a) case a: return #a
#define DEFAULT_SCROLL_LOCK_TIMEOUT 300 /* ms */
/* Use a reasonably large threshold until locked into scrolling mode, to
avoid accidentally locking in scrolling mode when trying to use the entire
@ -44,6 +46,32 @@ enum scroll_event {
SCROLL_EVENT_POSTED,
};
static inline const char*
edge_state_to_str(enum tp_edge_scroll_touch_state state)
{
switch (state) {
CASE_RETURN_STRING(EDGE_SCROLL_TOUCH_STATE_NONE);
CASE_RETURN_STRING(EDGE_SCROLL_TOUCH_STATE_EDGE_NEW);
CASE_RETURN_STRING(EDGE_SCROLL_TOUCH_STATE_EDGE);
CASE_RETURN_STRING(EDGE_SCROLL_TOUCH_STATE_AREA);
}
return NULL;
}
static inline const char*
edge_event_to_str(enum scroll_event event)
{
switch (event) {
CASE_RETURN_STRING(SCROLL_EVENT_TOUCH);
CASE_RETURN_STRING(SCROLL_EVENT_MOTION);
CASE_RETURN_STRING(SCROLL_EVENT_RELEASE);
CASE_RETURN_STRING(SCROLL_EVENT_TIMEOUT);
CASE_RETURN_STRING(SCROLL_EVENT_POSTED);
}
return NULL;
}
static uint32_t
tp_touch_get_edge(struct tp_dispatch *tp, struct tp_touch *t)
{
@ -204,7 +232,10 @@ tp_edge_scroll_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum scroll_event event)
{
switch (t->scroll.edge_state) {
struct libinput *libinput = tp->device->base.seat->libinput;
enum tp_edge_scroll_touch_state current = t->scroll.edge_state;
switch (current) {
case EDGE_SCROLL_TOUCH_STATE_NONE:
tp_edge_scroll_handle_none(tp, t, event);
break;
@ -218,6 +249,12 @@ tp_edge_scroll_handle_event(struct tp_dispatch *tp,
tp_edge_scroll_handle_area(tp, t, event);
break;
}
log_debug(libinput,
"edge state: %s → %s → %s\n",
edge_state_to_str(current),
edge_event_to_str(event),
edge_state_to_str(t->scroll.edge_state));
}
static void
@ -313,7 +350,6 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
struct tp_touch *t;
enum libinput_pointer_axis axis;
double *delta;
double initial_dx, initial_dy, *initial_delta;
struct normalized_coords normalized;
const struct normalized_coords zero = { 0.0, 0.0 };
const struct discrete_coords zero_discrete = { 0.0, 0.0 };
@ -340,20 +376,17 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
case EDGE_RIGHT:
axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
delta = &normalized.y;
initial_delta = &initial_dy;
break;
case EDGE_BOTTOM:
axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
delta = &normalized.x;
initial_delta = &initial_dx;
break;
default: /* EDGE_RIGHT | EDGE_BOTTOM */
continue; /* Don't know direction yet, skip */
}
normalized = tp_get_delta(t);
tp_filter_motion(tp, &normalized.x, &normalized.y,
NULL, NULL, time);
normalized = tp_filter_motion(tp, &normalized, time);
switch (t->scroll.edge_state) {
case EDGE_SCROLL_TOUCH_STATE_NONE:
@ -363,16 +396,11 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
t->scroll.edge_state);
break;
case EDGE_SCROLL_TOUCH_STATE_EDGE_NEW:
initial_dx = t->point.x - t->scroll.initial.x;
initial_dy = t->point.y - t->scroll.initial.y;
tp_normalize_delta(tp,
initial_dx,
initial_dy,
&normalized);
if (fabs(*initial_delta) < DEFAULT_SCROLL_THRESHOLD) {
normalized.x = 0.0;
normalized.y = 0.0;
}
normalized = tp_normalize_delta(tp,
device_delta(t->point,
t->scroll.initial));
if (fabs(*delta) < DEFAULT_SCROLL_THRESHOLD)
normalized = zero;
break;
case EDGE_SCROLL_TOUCH_STATE_EDGE:
break;

View file

@ -39,7 +39,7 @@ tp_get_touches_delta(struct tp_dispatch *tp, bool average)
struct normalized_coords normalized;
struct normalized_coords delta = {0.0, 0.0};
for (i = 0; i < tp->real_touches; i++) {
for (i = 0; i < tp->num_slots; i++) {
t = &tp->touches[i];
if (tp_touch_active(tp, t) && t->dirty) {
@ -93,14 +93,13 @@ tp_gesture_post_pointer_motion(struct tp_dispatch *tp, uint64_t time)
/* When a clickpad is clicked, combine motion of all active touches */
if (tp->buttons.is_clickpad && tp->buttons.state)
delta = tp_get_combined_touches_delta(tp);
unaccel = tp_get_combined_touches_delta(tp);
else
delta = tp_get_average_touches_delta(tp);
unaccel = tp_get_average_touches_delta(tp);
tp_filter_motion(tp, &delta.x, &delta.y, &unaccel.x, &unaccel.y, time);
delta = tp_filter_motion(tp, &unaccel, time);
if (delta.x != 0.0 || delta.y != 0.0 ||
unaccel.x != 0.0 || unaccel.y != 0.0) {
if (!normalized_is_zero(delta) || !normalized_is_zero(unaccel)) {
pointer_notify_motion(&tp->device->base, time,
&delta, &unaccel);
}
@ -111,10 +110,23 @@ tp_gesture_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
{
struct normalized_coords delta;
delta = tp_get_average_touches_delta(tp);
tp_filter_motion(tp, &delta.x, &delta.y, NULL, NULL, time);
if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG)
return;
if (delta.x == 0.0 && delta.y == 0.0)
/* On some semi-mt models slot 0 is more accurate, so for semi-mt
* we only use slot 0. */
if (tp->semi_mt) {
if (!tp->touches[0].dirty)
return;
delta = tp_get_delta(&tp->touches[0]);
} else {
delta = tp_get_average_touches_delta(tp);
}
delta = tp_filter_motion(tp, &delta, time);
if (normalized_is_zero(delta))
return;
tp_gesture_start(tp, time);
@ -154,6 +166,9 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time)
void
tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
{
if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG)
return;
evdev_stop_scroll(tp->device,
time,
LIBINPUT_POINTER_AXIS_SOURCE_FINGER);

View file

@ -34,9 +34,10 @@
#include "evdev-mt-touchpad.h"
#define CASE_RETURN_STRING(a) case a: return #a;
#define CASE_RETURN_STRING(a) case a: return #a
#define DEFAULT_TAP_TIMEOUT_PERIOD 180
#define DEFAULT_DRAG_TIMEOUT_PERIOD 500
#define DEFAULT_TAP_MOVE_THRESHOLD TP_MM_TO_DPI_NORMALIZED(3)
enum tap_event {
@ -59,8 +60,8 @@ enum tap_event {
*/
static inline const char*
tap_state_to_str(enum tp_tap_state state) {
tap_state_to_str(enum tp_tap_state state)
{
switch(state) {
CASE_RETURN_STRING(TAP_STATE_IDLE);
CASE_RETURN_STRING(TAP_STATE_HOLD);
@ -73,15 +74,18 @@ tap_state_to_str(enum tp_tap_state state) {
CASE_RETURN_STRING(TAP_STATE_DRAGGING);
CASE_RETURN_STRING(TAP_STATE_DRAGGING_WAIT);
CASE_RETURN_STRING(TAP_STATE_DRAGGING_OR_DOUBLETAP);
CASE_RETURN_STRING(TAP_STATE_DRAGGING_OR_TAP);
CASE_RETURN_STRING(TAP_STATE_DRAGGING_2);
CASE_RETURN_STRING(TAP_STATE_MULTITAP);
CASE_RETURN_STRING(TAP_STATE_MULTITAP_DOWN);
CASE_RETURN_STRING(TAP_STATE_DEAD);
}
return NULL;
}
static inline const char*
tap_event_to_str(enum tap_event event) {
tap_event_to_str(enum tap_event event)
{
switch(event) {
CASE_RETURN_STRING(TAP_EVENT_TOUCH);
CASE_RETURN_STRING(TAP_EVENT_MOTION);
@ -126,6 +130,12 @@ tp_tap_set_timer(struct tp_dispatch *tp, uint64_t time)
libinput_timer_set(&tp->tap.timer, time + DEFAULT_TAP_TIMEOUT_PERIOD);
}
static void
tp_tap_set_drag_timer(struct tp_dispatch *tp, uint64_t time)
{
libinput_timer_set(&tp->tap.timer, time + DEFAULT_DRAG_TIMEOUT_PERIOD);
}
static void
tp_tap_clear_timer(struct tp_dispatch *tp)
{
@ -145,9 +155,10 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp,
tp_tap_set_timer(tp, time);
break;
case TAP_EVENT_RELEASE:
break;
case TAP_EVENT_MOTION:
log_bug_libinput(libinput,
"invalid event, no fingers are down\n");
"invalid tap event, no fingers are down\n");
break;
case TAP_EVENT_TIMEOUT:
break;
@ -218,10 +229,11 @@ tp_tap_tapped_handle_event(struct tp_dispatch *tp,
case TAP_EVENT_MOTION:
case TAP_EVENT_RELEASE:
log_bug_libinput(libinput,
"invalid event when fingers are up\n");
"invalid tap event when fingers are up\n");
break;
case TAP_EVENT_TOUCH:
tp->tap.state = TAP_STATE_DRAGGING_OR_DOUBLETAP;
tp_tap_set_timer(tp, time);
break;
case TAP_EVENT_TIMEOUT:
tp->tap.state = TAP_STATE_IDLE;
@ -351,11 +363,8 @@ tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp,
tp->tap.state = TAP_STATE_DRAGGING_2;
break;
case TAP_EVENT_RELEASE:
tp->tap.state = TAP_STATE_IDLE;
tp->tap.state = TAP_STATE_MULTITAP;
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
tp_tap_clear_timer(tp);
break;
case TAP_EVENT_MOTION:
case TAP_EVENT_TIMEOUT:
@ -380,7 +389,7 @@ tp_tap_dragging_handle_event(struct tp_dispatch *tp,
break;
case TAP_EVENT_RELEASE:
tp->tap.state = TAP_STATE_DRAGGING_WAIT;
tp_tap_set_timer(tp, time);
tp_tap_set_drag_timer(tp, time);
break;
case TAP_EVENT_MOTION:
case TAP_EVENT_TIMEOUT:
@ -401,8 +410,8 @@ tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp,
switch (event) {
case TAP_EVENT_TOUCH:
tp->tap.state = TAP_STATE_DRAGGING;
tp_tap_clear_timer(tp);
tp->tap.state = TAP_STATE_DRAGGING_OR_TAP;
tp_tap_set_timer(tp, time);
break;
case TAP_EVENT_RELEASE:
case TAP_EVENT_MOTION:
@ -418,6 +427,32 @@ tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp,
}
}
static void
tp_tap_dragging_tap_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event, uint64_t time)
{
switch (event) {
case TAP_EVENT_TOUCH:
tp->tap.state = TAP_STATE_DRAGGING_2;
tp_tap_clear_timer(tp);
break;
case TAP_EVENT_RELEASE:
tp->tap.state = TAP_STATE_IDLE;
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
break;
case TAP_EVENT_MOTION:
case TAP_EVENT_TIMEOUT:
tp->tap.state = TAP_STATE_DRAGGING;
break;
case TAP_EVENT_BUTTON:
tp->tap.state = TAP_STATE_DEAD;
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
break;
}
}
static void
tp_tap_dragging2_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
@ -443,6 +478,78 @@ tp_tap_dragging2_handle_event(struct tp_dispatch *tp,
}
}
static void
tp_tap_multitap_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event, uint64_t time)
{
struct libinput *libinput = tp->device->base.seat->libinput;
switch (event) {
case TAP_EVENT_RELEASE:
log_bug_libinput(libinput,
"invalid tap event, no fingers are down\n");
break;
case TAP_EVENT_TOUCH:
tp->tap.state = TAP_STATE_MULTITAP_DOWN;
tp->tap.multitap_last_time = time;
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
tp_tap_set_timer(tp, time);
break;
case TAP_EVENT_MOTION:
log_bug_libinput(libinput,
"invalid tap event, no fingers are down\n");
break;
case TAP_EVENT_TIMEOUT:
tp->tap.state = TAP_STATE_IDLE;
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
break;
case TAP_EVENT_BUTTON:
tp->tap.state = TAP_STATE_IDLE;
tp_tap_clear_timer(tp);
break;
}
}
static void
tp_tap_multitap_down_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time)
{
switch (event) {
case TAP_EVENT_RELEASE:
tp->tap.state = TAP_STATE_MULTITAP;
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
break;
case TAP_EVENT_TOUCH:
tp->tap.state = TAP_STATE_DRAGGING_2;
tp_tap_notify(tp,
tp->tap.multitap_last_time,
1,
LIBINPUT_BUTTON_STATE_RELEASED);
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
tp_tap_clear_timer(tp);
break;
case TAP_EVENT_MOTION:
case TAP_EVENT_TIMEOUT:
tp->tap.state = TAP_STATE_DRAGGING;
tp_tap_notify(tp,
tp->tap.multitap_last_time,
1,
LIBINPUT_BUTTON_STATE_RELEASED);
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
tp_tap_clear_timer(tp);
break;
case TAP_EVENT_BUTTON:
tp->tap.state = TAP_STATE_DEAD;
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
tp_tap_clear_timer(tp);
break;
}
}
static void
tp_tap_dead_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
@ -508,9 +615,18 @@ tp_tap_handle_event(struct tp_dispatch *tp,
case TAP_STATE_DRAGGING_WAIT:
tp_tap_dragging_wait_handle_event(tp, t, event, time);
break;
case TAP_STATE_DRAGGING_OR_TAP:
tp_tap_dragging_tap_handle_event(tp, t, event, time);
break;
case TAP_STATE_DRAGGING_2:
tp_tap_dragging2_handle_event(tp, t, event, time);
break;
case TAP_STATE_MULTITAP:
tp_tap_multitap_handle_event(tp, t, event, time);
break;
case TAP_STATE_MULTITAP_DOWN:
tp_tap_multitap_down_handle_event(tp, t, event, time);
break;
case TAP_STATE_DEAD:
tp_tap_dead_handle_event(tp, t, event, time);
break;
@ -530,16 +646,11 @@ static bool
tp_tap_exceeds_motion_threshold(struct tp_dispatch *tp,
struct tp_touch *t)
{
int threshold = DEFAULT_TAP_MOVE_THRESHOLD;
double dx, dy;
struct normalized_coords normalized;
struct normalized_coords norm =
tp_normalize_delta(tp, device_delta(t->point,
t->tap.initial));
dx = abs(t->tap.initial.x - t->point.x);
dy = abs(t->tap.initial.y - t->point.y);
tp_normalize_delta(tp, dx, dy, &normalized);
return normalized.x * normalized.x + normalized.y * normalized.y
> threshold * threshold;
return normalized_length(norm) > DEFAULT_TAP_MOVE_THRESHOLD;
}
static bool
@ -575,6 +686,14 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
t->tap.state = TAP_TOUCH_STATE_TOUCH;
t->tap.initial = t->point;
tp_tap_handle_event(tp, t, TAP_EVENT_TOUCH, time);
/* If we think this is a palm, pretend there's a
* motion event which will prevent tap clicks
* without requiring extra states in the FSM.
*/
if (tp_palm_tap_is_palm(tp, t))
tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time);
} else if (t->state == TOUCH_END) {
tp_tap_handle_event(tp, t, TAP_EVENT_RELEASE, time);
t->tap.state = TAP_TOUCH_STATE_IDLE;
@ -603,8 +722,10 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
case TAP_STATE_TOUCH:
case TAP_STATE_TAPPED:
case TAP_STATE_DRAGGING_OR_DOUBLETAP:
case TAP_STATE_DRAGGING_OR_TAP:
case TAP_STATE_TOUCH_2:
case TAP_STATE_TOUCH_3:
case TAP_STATE_MULTITAP_DOWN:
filter_motion = 1;
break;
@ -780,6 +901,7 @@ tp_tap_dragging(struct tp_dispatch *tp)
case TAP_STATE_DRAGGING:
case TAP_STATE_DRAGGING_2:
case TAP_STATE_DRAGGING_WAIT:
case TAP_STATE_DRAGGING_OR_TAP:
return true;
default:
return false;

View file

@ -34,6 +34,9 @@
#define DEFAULT_ACCEL_NUMERATOR 3000.0
#define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0
#define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT 500 /* ms */
#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1 200 /* ms */
#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2 500 /* ms */
#define FAKE_FINGER_OVERFLOW (1 << 7)
static inline int
tp_hysteresis(int in, int center, int margin)
@ -58,33 +61,16 @@ tp_motion_history_offset(struct tp_touch *t, int offset)
return &t->history.samples[offset_index];
}
void
struct normalized_coords
tp_filter_motion(struct tp_dispatch *tp,
double *dx, double *dy,
double *dx_unaccel, double *dy_unaccel,
const struct normalized_coords *unaccelerated,
uint64_t time)
{
struct normalized_coords unaccelerated;
struct normalized_coords accelerated;
if (normalized_is_zero(*unaccelerated))
return *unaccelerated;
unaccelerated.x = *dx;
unaccelerated.y = *dy;
if (unaccelerated.x != 0.0 || unaccelerated.y != 0.0)
accelerated = filter_dispatch(tp->device->pointer.filter,
&unaccelerated,
tp,
time);
else
accelerated = unaccelerated;
if (dx_unaccel)
*dx_unaccel = unaccelerated.x;
if (dy_unaccel)
*dy_unaccel = unaccelerated.y;
*dx = accelerated.x;
*dy = accelerated.y;
return filter_dispatch(tp->device->pointer.filter,
unaccelerated, tp, time);
}
static inline void
@ -144,8 +130,10 @@ tp_get_touch(struct tp_dispatch *tp, unsigned int slot)
static inline unsigned int
tp_fake_finger_count(struct tp_dispatch *tp)
{
/* don't count BTN_TOUCH */
return ffs(tp->fake_touches >> 1);
if (tp->fake_touches & FAKE_FINGER_OVERFLOW)
return FAKE_FINGER_OVERFLOW;
else /* don't count BTN_TOUCH */
return ffs(tp->fake_touches >> 1);
}
static inline bool
@ -163,6 +151,8 @@ tp_fake_finger_set(struct tp_dispatch *tp,
switch (code) {
case BTN_TOUCH:
if (!is_press)
tp->fake_touches &= ~FAKE_FINGER_OVERFLOW;
shift = 0;
break;
case BTN_TOOL_FINGER:
@ -173,14 +163,24 @@ tp_fake_finger_set(struct tp_dispatch *tp,
case BTN_TOOL_QUADTAP:
shift = code - BTN_TOOL_DOUBLETAP + 2;
break;
/* when QUINTTAP is released we're either switching to 6 fingers
(flag stays in place until BTN_TOUCH is released) or
one of DOUBLE/TRIPLE/QUADTAP (will clear the flag on press) */
case BTN_TOOL_QUINTTAP:
if (is_press)
tp->fake_touches |= FAKE_FINGER_OVERFLOW;
return;
default:
return;
}
if (is_press)
if (is_press) {
tp->fake_touches &= ~FAKE_FINGER_OVERFLOW;
tp->fake_touches |= 1 << shift;
else
} else {
tp->fake_touches &= ~(0x1 << shift);
}
}
static inline void
@ -233,7 +233,7 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
}
t->dirty = true;
t->palm.is_palm = false;
t->palm.state = PALM_NONE;
t->state = TOUCH_END;
t->pinned.is_pinned = false;
t->millis = time;
@ -261,23 +261,22 @@ tp_estimate_delta(int x0, int x1, int x2, int x3)
struct normalized_coords
tp_get_delta(struct tp_touch *t)
{
double dx, dy; /* in device coords */
struct normalized_coords normalized = { 0.0, 0.0 };
struct device_float_coords delta;
const struct normalized_coords zero = { 0.0, 0.0 };
if (t->history.count < TOUCHPAD_MIN_SAMPLES)
return normalized;
return zero;
dx = tp_estimate_delta(tp_motion_history_offset(t, 0)->x,
tp_motion_history_offset(t, 1)->x,
tp_motion_history_offset(t, 2)->x,
tp_motion_history_offset(t, 3)->x);
dy = tp_estimate_delta(tp_motion_history_offset(t, 0)->y,
tp_motion_history_offset(t, 1)->y,
tp_motion_history_offset(t, 2)->y,
tp_motion_history_offset(t, 3)->y);
tp_normalize_delta(t->tp, dx, dy, &normalized);
delta.x = tp_estimate_delta(tp_motion_history_offset(t, 0)->x,
tp_motion_history_offset(t, 1)->x,
tp_motion_history_offset(t, 2)->x,
tp_motion_history_offset(t, 3)->x);
delta.y = tp_estimate_delta(tp_motion_history_offset(t, 0)->y,
tp_motion_history_offset(t, 1)->y,
tp_motion_history_offset(t, 2)->y,
tp_motion_history_offset(t, 3)->y);
return normalized;
return tp_normalize_delta(t->tp, delta);
}
static void
@ -303,6 +302,9 @@ tp_process_absolute(struct tp_dispatch *tp,
case ABS_MT_SLOT:
tp->slot = e->value;
break;
case ABS_MT_DISTANCE:
t->distance = e->value;
break;
case ABS_MT_TRACKING_ID:
if (e->value != -1)
tp_new_touch(tp, t, time);
@ -335,19 +337,18 @@ tp_process_absolute_st(struct tp_dispatch *tp,
}
static void
tp_process_fake_touch(struct tp_dispatch *tp,
const struct input_event *e,
uint64_t time)
tp_process_fake_touches(struct tp_dispatch *tp,
uint64_t time)
{
struct tp_touch *t;
unsigned int nfake_touches;
unsigned int i, start;
tp_fake_finger_set(tp, e->code, e->value != 0);
nfake_touches = tp_fake_finger_count(tp);
if (nfake_touches == FAKE_FINGER_OVERFLOW)
return;
start = tp->has_mt ? tp->real_touches : 0;
start = tp->has_mt ? tp->num_slots : 0;
for (i = start; i < tp->ntouches; i++) {
t = tp_get_touch(tp, i);
if (i < nfake_touches)
@ -408,7 +409,8 @@ tp_process_key(struct tp_dispatch *tp,
case BTN_TOOL_DOUBLETAP:
case BTN_TOOL_TRIPLETAP:
case BTN_TOOL_QUADTAP:
tp_process_fake_touch(tp, e, time);
case BTN_TOOL_QUINTTAP:
tp_fake_finger_set(tp, e->code, !!e->value);
break;
case BTN_0:
case BTN_1:
@ -455,29 +457,58 @@ int
tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
{
return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
!t->palm.is_palm &&
t->palm.state == PALM_NONE &&
!t->pinned.is_pinned &&
tp_button_touch_active(tp, t) &&
tp_edge_scroll_touch_active(tp, t);
}
bool
tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t)
{
if (t->state != TOUCH_BEGIN)
return false;
if (t->point.x > tp->palm.left_edge &&
t->point.x < tp->palm.right_edge)
return false;
/* We're inside the left/right palm edge and in the northern half of
* the touchpad - this tap is a palm */
if (t->point.y < tp->palm.vert_center)
return true;
return false;
}
static void
tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
{
const int PALM_TIMEOUT = 200; /* ms */
const int DIRECTIONS = NE|E|SE|SW|W|NW;
struct device_float_coords delta;
int dirs;
if (tp->sendevents.keyboard_active &&
t->state == TOUCH_BEGIN) {
t->palm.state = PALM_TYPING;
t->palm.time = time;
t->palm.first = t->point;
return;
}
/* If labelled a touch as palm, we unlabel as palm when
we move out of the palm edge zone within the timeout, provided
the direction is within 45 degrees of the horizontal.
*/
if (t->palm.is_palm) {
if (t->palm.state == PALM_EDGE) {
if (time < t->palm.time + PALM_TIMEOUT &&
(t->point.x > tp->palm.left_edge && t->point.x < tp->palm.right_edge)) {
int dirs = vector_get_direction(t->point.x - t->palm.first.x,
t->point.y - t->palm.first.y);
delta = device_delta(t->point, t->palm.first);
dirs = normalized_get_direction(
tp_normalize_delta(tp, delta));
if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS)) {
t->palm.is_palm = false;
t->palm.state = PALM_NONE;
}
}
return;
@ -496,13 +527,35 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_button_is_inside_softbutton_area(tp, t))
return;
t->palm.is_palm = true;
t->palm.state = PALM_EDGE;
t->palm.time = time;
t->palm.first = t->point;
}
static void
tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
tp_unhover_abs_distance(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *t;
unsigned int i;
for (i = 0; i < tp->ntouches; i++) {
t = tp_get_touch(tp, i);
if (t->state == TOUCH_HOVERING) {
if (t->distance == 0) {
/* avoid jumps when landing a finger */
tp_motion_history_reset(t);
tp_begin_touch(tp, t, time);
}
} else {
if (t->distance > 0)
tp_end_touch(tp, t, time);
}
}
}
static void
tp_unhover_fake_touches(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *t;
unsigned int nfake_touches;
@ -512,6 +565,9 @@ tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
return;
nfake_touches = tp_fake_finger_count(tp);
if (nfake_touches == FAKE_FINGER_OVERFLOW)
return;
if (tp->nfingers_down == nfake_touches &&
((tp->nfingers_down == 0 && !tp_fake_finger_is_touching(tp)) ||
(tp->nfingers_down > 0 && tp_fake_finger_is_touching(tp))))
@ -544,7 +600,8 @@ tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
for (i = tp->ntouches - 1; i >= 0; i--) {
t = tp_get_touch(tp, i);
if (t->state == TOUCH_HOVERING)
if (t->state == TOUCH_HOVERING ||
t->state == TOUCH_NONE)
continue;
tp_end_touch(tp, t, time);
@ -556,6 +613,16 @@ tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
}
}
static void
tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
{
if (tp->reports_distance)
tp_unhover_abs_distance(tp, time);
else
tp_unhover_fake_touches(tp, time);
}
static void
tp_process_state(struct tp_dispatch *tp, uint64_t time)
{
@ -563,6 +630,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time)
struct tp_touch *first = tp_get_touch(tp, 0);
unsigned int i;
tp_process_fake_touches(tp, time);
tp_unhover_touches(tp, time);
for (i = 0; i < tp->ntouches; i++) {
@ -572,7 +640,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time)
if (tp->semi_mt && tp->nfingers_down != tp->old_nfingers_down)
tp_motion_history_reset(t);
if (i >= tp->real_touches && t->state != TOUCH_NONE) {
if (i >= tp->num_slots && t->state != TOUCH_NONE) {
t->point = first->point;
if (!t->dirty)
t->dirty = first->dirty;
@ -647,7 +715,9 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time)
filter_motion |= tp_tap_handle_state(tp, time);
filter_motion |= tp_post_button_events(tp, time);
if (filter_motion || tp->sendevents.trackpoint_active) {
if (filter_motion ||
tp->sendevents.trackpoint_active ||
tp->sendevents.keyboard_active) {
tp_edge_scroll_stop_events(tp, time);
tp_gesture_stop(tp, time);
return;
@ -669,10 +739,10 @@ tp_handle_state(struct tp_dispatch *tp,
}
static void
tp_process(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct input_event *e,
uint64_t time)
tp_interface_process(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct input_event *e,
uint64_t time)
{
struct tp_dispatch *tp =
(struct tp_dispatch *)dispatch;
@ -697,14 +767,19 @@ static void
tp_remove_sendevents(struct tp_dispatch *tp)
{
libinput_timer_cancel(&tp->sendevents.trackpoint_timer);
libinput_timer_cancel(&tp->sendevents.keyboard_timer);
if (tp->buttons.trackpoint)
libinput_device_remove_event_listener(
&tp->sendevents.trackpoint_listener);
if (tp->sendevents.keyboard)
libinput_device_remove_event_listener(
&tp->sendevents.keyboard_listener);
}
static void
tp_remove(struct evdev_dispatch *dispatch)
tp_interface_remove(struct evdev_dispatch *dispatch)
{
struct tp_dispatch *tp =
(struct tp_dispatch*)dispatch;
@ -717,7 +792,7 @@ tp_remove(struct evdev_dispatch *dispatch)
}
static void
tp_destroy(struct evdev_dispatch *dispatch)
tp_interface_destroy(struct evdev_dispatch *dispatch)
{
struct tp_dispatch *tp =
(struct tp_dispatch*)dispatch;
@ -726,6 +801,12 @@ tp_destroy(struct evdev_dispatch *dispatch)
free(tp);
}
static void
tp_release_fake_touches(struct tp_dispatch *tp)
{
tp->fake_touches = 0;
}
static void
tp_clear_state(struct tp_dispatch *tp)
{
@ -749,6 +830,7 @@ tp_clear_state(struct tp_dispatch *tp)
tp_for_each_touch(tp, t) {
tp_end_sequence(tp, t, now);
}
tp_release_fake_touches(tp);
tp_handle_state(tp, now);
}
@ -771,6 +853,15 @@ tp_suspend(struct tp_dispatch *tp, struct evdev_device *device)
}
}
static void
tp_interface_suspend(struct evdev_dispatch *dispatch,
struct evdev_device *device)
{
struct tp_dispatch *tp = (struct tp_dispatch *)dispatch;
tp_clear_state(tp);
}
static void
tp_resume(struct tp_dispatch *tp, struct evdev_device *device)
{
@ -816,13 +907,76 @@ tp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data)
}
static void
tp_device_added(struct evdev_device *device,
struct evdev_device *added_device)
tp_keyboard_timeout(uint64_t now, void *data)
{
struct tp_dispatch *tp = data;
tp_tap_resume(tp, now);
tp->sendevents.keyboard_active = false;
}
static void
tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data)
{
struct tp_dispatch *tp = data;
struct libinput_event_keyboard *kbdev;
unsigned int timeout;
if (event->type != LIBINPUT_EVENT_KEYBOARD_KEY)
return;
kbdev = libinput_event_get_keyboard_event(event);
/* Only trigger the timer on key down. */
if (libinput_event_keyboard_get_key_state(kbdev) !=
LIBINPUT_KEY_STATE_PRESSED)
return;
/* modifier keys don't trigger disable-while-typing so things like
* ctrl+zoom or ctrl+click are possible */
switch (libinput_event_keyboard_get_key(kbdev)) {
case KEY_LEFTCTRL:
case KEY_RIGHTCTRL:
case KEY_LEFTALT:
case KEY_RIGHTALT:
case KEY_LEFTSHIFT:
case KEY_RIGHTSHIFT:
case KEY_FN:
return;
default:
break;
}
if (!tp->sendevents.keyboard_active) {
tp_edge_scroll_stop_events(tp, time);
tp_gesture_stop(tp, time);
tp_tap_suspend(tp, time);
tp->sendevents.keyboard_active = true;
timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1;
} else {
timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2;
}
libinput_timer_set(&tp->sendevents.keyboard_timer,
time + timeout);
}
static void
tp_interface_device_added(struct evdev_device *device,
struct evdev_device *added_device)
{
struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
unsigned int bus_tp = libevdev_get_id_bustype(device->evdev),
bus_trp = libevdev_get_id_bustype(added_device->evdev),
bus_kbd = libevdev_get_id_bustype(added_device->evdev);
bool tp_is_internal, trp_is_internal, kbd_is_internal;
tp_is_internal = bus_tp != BUS_USB && bus_tp != BUS_BLUETOOTH;
trp_is_internal = bus_trp != BUS_USB && bus_trp != BUS_BLUETOOTH;
if (tp->buttons.trackpoint == NULL &&
(added_device->tags & EVDEV_TAG_TRACKPOINT)) {
(added_device->tags & EVDEV_TAG_TRACKPOINT) &&
tp_is_internal && trp_is_internal) {
/* Don't send any pending releases to the new trackpoint */
tp->buttons.active_is_topbutton = false;
tp->buttons.trackpoint = added_device;
@ -831,6 +985,18 @@ tp_device_added(struct evdev_device *device,
tp_trackpoint_event, tp);
}
/* FIXME: detect external keyboard better */
kbd_is_internal = bus_tp != BUS_BLUETOOTH &&
bus_kbd == bus_tp;
if (tp_is_internal && kbd_is_internal &&
tp->sendevents.keyboard == NULL) {
libinput_device_add_event_listener(&added_device->base,
&tp->sendevents.keyboard_listener,
tp_keyboard_event, tp);
tp->sendevents.keyboard = added_device;
tp->sendevents.keyboard_active = false;
}
if (tp->sendevents.current_mode !=
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
return;
@ -840,8 +1006,8 @@ tp_device_added(struct evdev_device *device,
}
static void
tp_device_removed(struct evdev_device *device,
struct evdev_device *removed_device)
tp_interface_device_removed(struct evdev_device *device,
struct evdev_device *removed_device)
{
struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
struct libinput_device *dev;
@ -857,6 +1023,12 @@ tp_device_removed(struct evdev_device *device,
tp->buttons.trackpoint = NULL;
}
if (removed_device == tp->sendevents.keyboard) {
libinput_device_remove_event_listener(
&tp->sendevents.keyboard_listener);
tp->sendevents.keyboard = NULL;
}
if (tp->sendevents.current_mode !=
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
return;
@ -873,8 +1045,8 @@ tp_device_removed(struct evdev_device *device,
}
static void
tp_tag_device(struct evdev_device *device,
struct udev_device *udev_device)
tp_interface_tag_device(struct evdev_device *device,
struct udev_device *udev_device)
{
int bustype;
@ -896,15 +1068,16 @@ tp_tag_device(struct evdev_device *device,
}
static struct evdev_dispatch_interface tp_interface = {
tp_process,
tp_remove,
tp_destroy,
tp_device_added,
tp_device_removed,
tp_device_removed, /* device_suspended, treat as remove */
tp_device_added, /* device_resumed, treat as add */
tp_tag_device,
NULL, /* post_added */
tp_interface_process,
tp_interface_suspend,
tp_interface_remove,
tp_interface_destroy,
tp_interface_device_added,
tp_interface_device_removed,
tp_interface_device_removed, /* device_suspended, treat as remove */
tp_interface_device_added, /* device_resumed, treat as add */
tp_interface_tag_device,
NULL, /* post_added */
};
static void
@ -915,6 +1088,28 @@ tp_init_touch(struct tp_dispatch *tp,
t->has_ended = true;
}
static void
tp_sync_touch(struct tp_dispatch *tp,
struct evdev_device *device,
struct tp_touch *t,
int slot)
{
struct libevdev *evdev = device->evdev;
if (!libevdev_fetch_slot_value(evdev,
slot,
ABS_MT_POSITION_X,
&t->point.x))
t->point.x = libevdev_get_event_value(evdev, EV_ABS, ABS_X);
if (!libevdev_fetch_slot_value(evdev,
slot,
ABS_MT_POSITION_Y,
&t->point.y))
t->point.y = libevdev_get_event_value(evdev, EV_ABS, ABS_Y);
libevdev_fetch_slot_value(evdev, slot, ABS_MT_DISTANCE, &t->distance);
}
static int
tp_init_slots(struct tp_dispatch *tp,
struct evdev_device *device)
@ -934,11 +1129,11 @@ tp_init_slots(struct tp_dispatch *tp,
absinfo = libevdev_get_abs_info(device->evdev, ABS_MT_SLOT);
if (absinfo) {
tp->real_touches = absinfo->maximum + 1;
tp->num_slots = absinfo->maximum + 1;
tp->slot = absinfo->value;
tp->has_mt = true;
} else {
tp->real_touches = 1;
tp->num_slots = 1;
tp->slot = 0;
tp->has_mt = false;
}
@ -954,7 +1149,7 @@ tp_init_slots(struct tp_dispatch *tp,
}
}
tp->ntouches = max(tp->real_touches, n_btn_tool_touches);
tp->ntouches = max(tp->num_slots, n_btn_tool_touches);
tp->touches = calloc(tp->ntouches, sizeof(struct tp_touch));
if (!tp->touches)
return -1;
@ -962,6 +1157,12 @@ tp_init_slots(struct tp_dispatch *tp,
for (i = 0; i < tp->ntouches; i++)
tp_init_touch(tp, &tp->touches[i]);
/* Always sync the first touch so we get ABS_X/Y synced on
* single-touch touchpads */
tp_sync_touch(tp, device, &tp->touches[0], 0);
for (i = 1; i < tp->num_slots; i++)
tp_sync_touch(tp, device, &tp->touches[i], i);
return 0;
}
@ -969,6 +1170,7 @@ static int
tp_init_accel(struct tp_dispatch *tp, double diagonal)
{
int res_x, res_y;
accel_profile_func_t profile;
res_x = tp->device->abs.absinfo_x->resolution;
res_y = tp->device->abs.absinfo_y->resolution;
@ -992,9 +1194,16 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal)
tp->accel.y_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal;
}
if (evdev_device_init_pointer_acceleration(
tp->device,
touchpad_accel_profile_linear) == -1)
switch (tp->device->model) {
case EVDEV_MODEL_LENOVO_X230:
profile = touchpad_lenovo_x230_accel_profile;
break;
default:
profile = touchpad_accel_profile_linear;
break;
}
if (evdev_device_init_pointer_acceleration(tp->device, profile) == -1)
return -1;
return 0;
@ -1087,13 +1296,16 @@ static int
tp_init_palmdetect(struct tp_dispatch *tp,
struct evdev_device *device)
{
int width;
int width, height;
tp->palm.right_edge = INT_MAX;
tp->palm.left_edge = INT_MIN;
tp->palm.vert_center = INT_MIN;
width = abs(device->abs.absinfo_x->maximum -
device->abs.absinfo_x->minimum);
height = abs(device->abs.absinfo_y->maximum -
device->abs.absinfo_y->minimum);
/* Apple touchpads are always big enough to warrant palm detection */
if (evdev_device_get_id_vendor(device) != VENDOR_ID_APPLE) {
@ -1101,15 +1313,16 @@ tp_init_palmdetect(struct tp_dispatch *tp,
if (device->abs.absinfo_x->resolution == 1)
return 0;
/* Enable palm detection on touchpads >= 80 mm. Anything smaller
/* 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 < 80)
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;
tp->palm.left_edge = device->abs.absinfo_x->minimum + width * 0.05;
tp->palm.vert_center = device->abs.absinfo_y->minimum + height/2;
return 0;
}
@ -1121,6 +1334,10 @@ tp_init_sendevents(struct tp_dispatch *tp,
libinput_timer_init(&tp->sendevents.trackpoint_timer,
tp->device->base.seat->libinput,
tp_trackpoint_timeout, tp);
libinput_timer_init(&tp->sendevents.keyboard_timer,
tp->device->base.seat->libinput,
tp_keyboard_timeout, tp);
return 0;
}
@ -1143,6 +1360,10 @@ tp_init(struct tp_dispatch *tp,
device->abs.absinfo_y->minimum);
diagonal = sqrt(width*width + height*height);
tp->reports_distance = libevdev_has_event_code(device->evdev,
EV_ABS,
ABS_MT_DISTANCE);
tp->hysteresis_margin.x =
diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
tp->hysteresis_margin.y =
@ -1313,7 +1534,7 @@ evdev_mt_touchpad_create(struct evdev_device *device)
tp->model = tp_get_model(device);
if (tp_init(tp, device) != 0) {
tp_destroy(&tp->base);
tp_interface_destroy(&tp->base);
return NULL;
}

View file

@ -61,6 +61,12 @@ enum touch_state {
TOUCH_END
};
enum touch_palm_state {
PALM_NONE = 0,
PALM_EDGE,
PALM_TYPING,
};
enum button_event {
BUTTON_EVENT_IN_BOTTOM_R = 30,
BUTTON_EVENT_IN_BOTTOM_L,
@ -94,9 +100,12 @@ enum tp_tap_state {
TAP_STATE_TOUCH_3,
TAP_STATE_TOUCH_3_HOLD,
TAP_STATE_DRAGGING_OR_DOUBLETAP,
TAP_STATE_DRAGGING_OR_TAP,
TAP_STATE_DRAGGING,
TAP_STATE_DRAGGING_WAIT,
TAP_STATE_DRAGGING_2,
TAP_STATE_MULTITAP,
TAP_STATE_MULTITAP_DOWN,
TAP_STATE_DEAD, /**< finger count exceeded */
};
@ -127,6 +136,7 @@ struct tp_touch {
bool dirty;
struct device_coords point;
uint64_t millis;
int distance; /* distance == 0 means touch */
struct {
struct device_coords samples[TOUCHPAD_HISTORY_LENGTH];
@ -167,7 +177,7 @@ struct tp_touch {
} scroll;
struct {
bool is_palm;
enum touch_palm_state state;
struct device_coords first; /* first coordinates if is_palm == true */
uint32_t time; /* first timestamp if is_palm == true */
} palm;
@ -181,9 +191,10 @@ struct tp_dispatch {
unsigned int slot; /* current slot */
bool has_mt;
bool semi_mt;
bool reports_distance; /* does the device support true hovering */
enum touchpad_model model;
unsigned int real_touches; /* number of slots */
unsigned int num_slots; /* number of slots */
unsigned int ntouches; /* no slots inc. fakes */
struct tp_touch *touches; /* len == ntouches */
/* bit 0: BTN_TOUCH
@ -255,41 +266,50 @@ struct tp_dispatch {
struct libinput_timer timer;
enum tp_tap_state state;
uint32_t buttons_pressed;
uint64_t multitap_last_time;
} tap;
struct {
int32_t right_edge; /* in device coordinates */
int32_t left_edge; /* in device coordinates */
int32_t vert_center; /* in device coordinates */
} palm;
struct {
struct libinput_device_config_send_events config;
enum libinput_config_send_events_mode current_mode;
bool trackpoint_active;
struct libinput_event_listener trackpoint_listener;
struct libinput_timer trackpoint_timer;
bool keyboard_active;
struct libinput_event_listener keyboard_listener;
struct libinput_timer keyboard_timer;
struct evdev_device *keyboard;
} sendevents;
};
#define tp_for_each_touch(_tp, _t) \
for (unsigned int _i = 0; _i < (_tp)->ntouches && (_t = &(_tp)->touches[_i]); _i++)
static inline void
tp_normalize_delta(struct tp_dispatch *tp,
double dx, double dy,
struct normalized_coords *normalized)
static inline struct normalized_coords
tp_normalize_delta(struct tp_dispatch *tp, struct device_float_coords delta)
{
normalized->x = dx * tp->accel.x_scale_coeff;
normalized->y = dy * tp->accel.y_scale_coeff;
struct normalized_coords normalized;
normalized.x = delta.x * tp->accel.x_scale_coeff;
normalized.y = delta.y * tp->accel.y_scale_coeff;
return normalized;
}
struct normalized_coords
tp_get_delta(struct tp_touch *t);
void
struct normalized_coords
tp_filter_motion(struct tp_dispatch *tp,
double *dx, double *dy,
double *dx_unaccel, double *dy_unaccel,
const struct normalized_coords *unaccelerated,
uint64_t time);
int
@ -385,4 +405,7 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time);
void
tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time);
bool
tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t);
#endif

View file

@ -1022,6 +1022,7 @@ tablet_check_initial_proximity(struct evdev_device *device,
static struct evdev_dispatch_interface tablet_interface = {
tablet_process,
NULL, /* suspend */
NULL, /* remove */
tablet_destroy,
NULL, /* device_added */

View file

@ -137,6 +137,21 @@ evdev_keyboard_notify_key(struct evdev_device *device,
keyboard_notify_key(&device->base, time, key, state);
}
void
evdev_pointer_notify_physical_button(struct evdev_device *device,
uint32_t time,
int button,
enum libinput_button_state state)
{
if (evdev_middlebutton_filter_button(device,
time,
button,
state))
return;
evdev_pointer_notify_button(device, time, button, state);
}
void
evdev_pointer_notify_button(struct evdev_device *device,
uint32_t time,
@ -151,13 +166,13 @@ evdev_pointer_notify_button(struct evdev_device *device,
(state == LIBINPUT_BUTTON_STATE_RELEASED && down_count == 0)) {
pointer_notify_button(&device->base, time, button, state);
if (state == LIBINPUT_BUTTON_STATE_RELEASED &&
device->left_handed.change_to_enabled)
device->left_handed.change_to_enabled(device);
if (state == LIBINPUT_BUTTON_STATE_RELEASED) {
if (device->left_handed.change_to_enabled)
device->left_handed.change_to_enabled(device);
if (state == LIBINPUT_BUTTON_STATE_RELEASED &&
device->scroll.change_scroll_method)
device->scroll.change_scroll_method(device);
if (device->scroll.change_scroll_method)
device->scroll.change_scroll_method(device);
}
}
}
@ -230,8 +245,8 @@ normalize_delta(struct evdev_device *device,
const struct device_coords *delta,
struct normalized_coords *normalized)
{
normalized->x = delta->x * (double)device->dpi / DEFAULT_MOUSE_DPI;
normalized->y = delta->y * (double)device->dpi / DEFAULT_MOUSE_DPI;
normalized->x = delta->x * DEFAULT_MOUSE_DPI / (double)device->dpi;
normalized->y = delta->y * DEFAULT_MOUSE_DPI / (double)device->dpi;
}
static void
@ -266,12 +281,13 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time)
}
/* Apply pointer acceleration. */
accel = filter_dispatch(device->pointer.filter, &unaccel, device, time);
accel = filter_dispatch(device->pointer.filter,
&unaccel,
device,
time);
if (accel.x == 0.0 && accel.y == 0.0 &&
unaccel.x == 0.0 && unaccel.y == 0.0) {
if (normalized_is_zero(accel) && normalized_is_zero(unaccel))
break;
}
pointer_notify_motion(base, time, &accel, &unaccel);
break;
@ -432,10 +448,10 @@ evdev_button_scroll_button(struct evdev_device *device,
} else {
/* If the button is released quickly enough emit the
* button press/release events. */
evdev_pointer_notify_button(device, time,
evdev_pointer_notify_physical_button(device, time,
device->scroll.button,
LIBINPUT_BUTTON_STATE_PRESSED);
evdev_pointer_notify_button(device, time,
evdev_pointer_notify_physical_button(device, time,
device->scroll.button,
LIBINPUT_BUTTON_STATE_RELEASED);
}
@ -507,7 +523,7 @@ evdev_process_key(struct evdev_device *device,
evdev_button_scroll_button(device, time, e->value);
break;
}
evdev_pointer_notify_button(
evdev_pointer_notify_physical_button(
device,
time,
evdev_to_left_handed(device, e->code),
@ -744,6 +760,65 @@ fallback_process(struct evdev_dispatch *dispatch,
}
}
static void
release_pressed_keys(struct evdev_device *device)
{
struct libinput *libinput = device->base.seat->libinput;
uint64_t time;
int code;
if ((time = libinput_now(libinput)) == 0)
return;
for (code = 0; code < KEY_CNT; code++) {
int count = get_key_down_count(device, code);
if (count == 0)
continue;
if (count > 1) {
log_bug_libinput(libinput,
"Key %d is down %d times.\n",
code,
count);
}
switch (get_key_type(code)) {
case EVDEV_KEY_TYPE_NONE:
break;
case EVDEV_KEY_TYPE_KEY:
evdev_keyboard_notify_key(
device,
time,
code,
LIBINPUT_KEY_STATE_RELEASED);
break;
case EVDEV_KEY_TYPE_BUTTON:
evdev_pointer_notify_physical_button(
device,
time,
evdev_to_left_handed(device, code),
LIBINPUT_BUTTON_STATE_RELEASED);
break;
}
count = get_key_down_count(device, code);
if (count != 0) {
log_bug_libinput(libinput,
"Releasing key %d failed.\n",
code);
break;
}
}
}
static void
fallback_suspend(struct evdev_dispatch *dispatch,
struct evdev_device *device)
{
release_pressed_keys(device);
}
static void
fallback_destroy(struct evdev_dispatch *dispatch)
{
@ -801,6 +876,7 @@ evdev_calibration_get_default_matrix(struct libinput_device *libinput_device,
struct evdev_dispatch_interface fallback_interface = {
fallback_process,
fallback_suspend,
NULL, /* remove */
fallback_destroy,
NULL, /* device_added */
@ -971,8 +1047,15 @@ evdev_scroll_get_default_method(struct libinput_device *device)
if (libevdev_has_property(evdev->evdev, INPUT_PROP_POINTING_STICK))
return LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
else
return LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
/* Mice without a scroll wheel but with middle button have on-button
* scrolling by default */
if (!libevdev_has_event_code(evdev->evdev, EV_REL, REL_WHEEL) &&
!libevdev_has_event_code(evdev->evdev, EV_REL, REL_HWHEEL) &&
libevdev_has_event_code(evdev->evdev, EV_KEY, BTN_MIDDLE))
return LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
return LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
}
static enum libinput_config_status
@ -1004,8 +1087,15 @@ evdev_scroll_get_default_button(struct libinput_device *device)
if (libevdev_has_property(evdev->evdev, INPUT_PROP_POINTING_STICK))
return BTN_MIDDLE;
else
return 0;
/* A device that defaults to button scrolling defaults
to BTN_MIDDLE */
if (evdev_scroll_get_default_method(device) ==
LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN &&
libevdev_has_event_code(evdev->evdev, EV_KEY, BTN_MIDDLE))
return BTN_MIDDLE;
return 0;
}
static int
@ -1131,6 +1221,24 @@ fallback_dispatch_create(struct libinput_device *device)
evdev_init_calibration(evdev_device, dispatch);
evdev_init_sendevents(evdev_device, dispatch);
/* BTN_MIDDLE is set on mice even when it's not present. So
* we can only use the absense of BTN_MIDDLE to mean something, i.e.
* we enable it by default on anything that only has L&R.
* If we have L&R and no middle, we don't expose it as config
* option */
if (libevdev_has_event_code(evdev_device->evdev, EV_KEY, BTN_LEFT) &&
libevdev_has_event_code(evdev_device->evdev, EV_KEY, BTN_RIGHT)) {
bool has_middle = libevdev_has_event_code(evdev_device->evdev,
EV_KEY,
BTN_MIDDLE);
bool want_config = has_middle;
bool enable_by_default = !has_middle;
evdev_init_middlebutton(evdev_device,
enable_by_default,
want_config);
}
return dispatch;
}
@ -1140,6 +1248,19 @@ evdev_process_event(struct evdev_device *device, struct input_event *e)
struct evdev_dispatch *dispatch = device->dispatch;
uint64_t time = e->time.tv_sec * 1000ULL + e->time.tv_usec / 1000;
#if 0
if (libevdev_event_is_code(e, EV_SYN, SYN_REPORT))
log_debug(device->base.seat->libinput,
"-------------- EV_SYN ------------\n");
else
log_debug(device->base.seat->libinput,
"%-7s %-16s %-20s %4d\n",
evdev_device_get_sysname(device),
libevdev_event_type_get_name(e->type),
libevdev_event_code_get_name(e->type, e->code),
e->value);
#endif
dispatch->interface->process(dispatch, device, e, time);
}
@ -1322,6 +1443,31 @@ evdev_read_wheel_click_prop(struct evdev_device *device)
return angle;
}
static inline int
evdev_get_trackpoint_dpi(struct evdev_device *device)
{
struct libinput *libinput = device->base.seat->libinput;
const char *trackpoint_accel;
double accel = DEFAULT_TRACKPOINT_ACCEL;
trackpoint_accel = udev_device_get_property_value(
device->udev_device, "POINTINGSTICK_CONST_ACCEL");
if (trackpoint_accel) {
accel = parse_trackpoint_accel_property(trackpoint_accel);
if (accel == 0.0) {
log_error(libinput, "Trackpoint accel property for "
"'%s' is present but invalid, "
"using %.2f instead\n",
device->devname,
DEFAULT_TRACKPOINT_ACCEL);
accel = DEFAULT_TRACKPOINT_ACCEL;
}
}
return DEFAULT_MOUSE_DPI / accel;
}
static inline int
evdev_read_dpi_prop(struct evdev_device *device)
{
@ -1329,6 +1475,14 @@ evdev_read_dpi_prop(struct evdev_device *device)
const char *mouse_dpi;
int dpi = DEFAULT_MOUSE_DPI;
/*
* Trackpoints do not have dpi, instead hwdb may contain a
* POINTINGSTICK_CONST_ACCEL value to compensate for sensitivity
* differences between models, we translate this to a fake dpi.
*/
if (libevdev_has_property(device->evdev, INPUT_PROP_POINTING_STICK))
return evdev_get_trackpoint_dpi(device);
mouse_dpi = udev_device_get_property_value(device->udev_device,
"MOUSE_DPI");
if (mouse_dpi) {
@ -1346,6 +1500,33 @@ evdev_read_dpi_prop(struct evdev_device *device)
return dpi;
}
static inline enum evdev_device_model
evdev_read_model(struct evdev_device *device)
{
const struct model_map {
const char *property;
enum evdev_device_model model;
} model_map[] = {
{ "LIBINPUT_MODEL_LENOVO_X230", EVDEV_MODEL_LENOVO_X230 },
{ "LIBINPUT_MODEL_CHROMEBOOK", EVDEV_MODEL_CHROMEBOOK },
{ "LIBINPUT_MODEL_SYSTEM76_BONOBO", EVDEV_MODEL_SYSTEM76_BONOBO },
{ "LIBINPUT_MODEL_SYSTEM76_GALAGO", EVDEV_MODEL_SYSTEM76_GALAGO },
{ "LIBINPUT_MODEL_SYSTEM76_KUDU", EVDEV_MODEL_SYSTEM76_KUDU },
{ "LIBINPUT_MODEL_CLEVO_W740SU", EVDEV_MODEL_CLEVO_W740SU },
{ NULL, EVDEV_MODEL_DEFAULT },
};
const struct model_map *m = model_map;
while (m->property) {
if (!!udev_device_get_property_value(device->udev_device,
m->property))
break;
m++;
}
return m->model;
}
/* Return 1 if the given resolutions have been set, or 0 otherwise */
inline int
evdev_fix_abs_resolution(struct evdev_device *device,
@ -1380,13 +1561,6 @@ evdev_fix_abs_resolution(struct evdev_device *device,
absx = libevdev_get_abs_info(evdev, xcode);
absy = libevdev_get_abs_info(evdev, ycode);
if ((absx->resolution == 0 && absy->resolution != 0) ||
(absx->resolution != 0 && absy->resolution == 0)) {
log_bug_kernel(libinput,
"Kernel has only x or y resolution, not both.\n");
return 0;
}
if (absx->resolution == 0 || absx->resolution == EVDEV_FAKE_RESOLUTION) {
fixed = *absx;
fixed.resolution = xresolution;
@ -1434,6 +1608,18 @@ evdev_device_get_udev_tags(struct evdev_device *device,
return tags;
}
/* Fake MT devices have the ABS_MT_SLOT bit set because of
the limited ABS_* range - they aren't MT devices, they
just have too many ABS_ axes */
static inline bool
evdev_is_fake_mt_device(struct evdev_device *device)
{
struct libevdev *evdev = device->evdev;
return libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT) &&
libevdev_get_num_slots(evdev) == -1;
}
static inline void
evdev_fix_android_mt(struct evdev_device *device)
{
@ -1444,12 +1630,13 @@ evdev_fix_android_mt(struct evdev_device *device)
return;
if (!libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) ||
!libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y))
!libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y) ||
evdev_is_fake_mt_device(device))
return;
libevdev_set_abs_info(evdev, ABS_X,
libevdev_enable_event_code(evdev, EV_ABS, ABS_X,
libevdev_get_abs_info(evdev, ABS_MT_POSITION_X));
libevdev_set_abs_info(evdev, ABS_Y,
libevdev_enable_event_code(evdev, EV_ABS, ABS_Y,
libevdev_get_abs_info(evdev, ABS_MT_POSITION_Y));
}
@ -1464,11 +1651,27 @@ evdev_check_min_max(struct evdev_device *device, unsigned int code)
absinfo = libevdev_get_abs_info(evdev, code);
if (absinfo->minimum == absinfo->maximum) {
log_bug_kernel(device->base.seat->libinput,
"Device '%s' has min == max on %s\n",
device->devname,
libevdev_event_code_get_name(EV_ABS, code));
return -1;
/* Some devices have a sort-of legitimate min/max of 0 for
* ABS_MISC and above (e.g. Roccat Kone XTD). Don't ignore
* them, simply disable the axes so we won't get events,
* we don't know what to do with them anyway.
*/
if (absinfo->minimum == 0 &&
code >= ABS_MISC && code < ABS_MT_SLOT) {
log_info(device->base.seat->libinput,
"Disabling EV_ABS %#x on device '%s' (min == max == 0)\n",
code,
device->devname);
libevdev_disable_event_code(device->evdev,
EV_ABS,
code);
} else {
log_bug_kernel(device->base.seat->libinput,
"Device '%s' has min == max on %s\n",
device->devname,
libevdev_event_code_get_name(EV_ABS, code));
return -1;
}
}
return 0;
@ -1477,17 +1680,46 @@ evdev_check_min_max(struct evdev_device *device, unsigned int code)
static int
evdev_reject_device(struct evdev_device *device)
{
struct libinput *libinput = device->base.seat->libinput;
struct libevdev *evdev = device->evdev;
unsigned int code;
const struct input_absinfo *absx, *absy;
if (libevdev_has_event_code(evdev, EV_ABS, ABS_X) ^
libevdev_has_event_code(evdev, EV_ABS, ABS_Y))
return -1;
if (libevdev_has_event_code(evdev, EV_REL, REL_X) ^
libevdev_has_event_code(evdev, EV_REL, REL_Y))
return -1;
if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) ^
libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y))
return -1;
if (libevdev_has_event_code(evdev, EV_ABS, ABS_X)) {
absx = libevdev_get_abs_info(evdev, ABS_X);
absy = libevdev_get_abs_info(evdev, ABS_Y);
if ((absx->resolution == 0 && absy->resolution != 0) ||
(absx->resolution != 0 && absy->resolution == 0)) {
log_bug_kernel(libinput,
"Kernel has only x or y resolution, not both.\n");
return -1;
}
}
if (!evdev_is_fake_mt_device(device) &&
libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X)) {
absx = libevdev_get_abs_info(evdev, ABS_MT_POSITION_X);
absy = libevdev_get_abs_info(evdev, ABS_MT_POSITION_Y);
if ((absx->resolution == 0 && absy->resolution != 0) ||
(absx->resolution != 0 && absy->resolution == 0)) {
log_bug_kernel(libinput,
"Kernel has only x or y MT resolution, not both.\n");
return -1;
}
}
for (code = 0; code < ABS_CNT; code++) {
switch (code) {
case ABS_MISC:
@ -1550,8 +1782,16 @@ evdev_configure_mt_device(struct evdev_device *device)
for (slot = 0; slot < num_slots; ++slot) {
slots[slot].seat_slot = -1;
slots[slot].point.x = 0;
slots[slot].point.y = 0;
if (evdev_need_mtdev(device))
continue;
slots[slot].point.x = libevdev_get_slot_value(evdev,
slot,
ABS_MT_POSITION_X);
slots[slot].point.y = libevdev_get_slot_value(evdev,
slot,
ABS_MT_POSITION_Y);
}
device->mt.slots = slots;
device->mt.slots_len = num_slots;
@ -1615,10 +1855,10 @@ evdev_configure_device(struct evdev_device *device)
return -1;
}
if (libevdev_has_event_code(evdev, EV_ABS, ABS_X) ||
libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X)) {
if (!evdev_is_fake_mt_device(device))
evdev_fix_android_mt(device);
if (libevdev_has_event_code(evdev, EV_ABS, ABS_X)) {
if (evdev_fix_abs_resolution(device,
ABS_X,
ABS_Y,
@ -1627,12 +1867,10 @@ evdev_configure_device(struct evdev_device *device)
device->abs.fake_resolution = 1;
device->abs.absinfo_x = libevdev_get_abs_info(evdev, ABS_X);
device->abs.absinfo_y = libevdev_get_abs_info(evdev, ABS_Y);
device->abs.point.x = device->abs.absinfo_x->value;
device->abs.point.y = device->abs.absinfo_y->value;
/* Fake MT devices have the ABS_MT_SLOT bit set because of
the limited ABS_* range - they aren't MT devices, they
just have too many ABS_ axes */
if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT) &&
libevdev_get_num_slots(evdev) == -1) {
if (evdev_is_fake_mt_device(device)) {
udev_tags &= ~EVDEV_UDEV_TAG_TOUCHSCREEN;
} else if (evdev_configure_mt_device(device) == -1) {
return -1;
@ -1663,8 +1901,8 @@ evdev_configure_device(struct evdev_device *device)
}
if (udev_tags & EVDEV_UDEV_TAG_MOUSE) {
if (!libevdev_has_event_code(evdev, EV_ABS, ABS_X) &&
!libevdev_has_event_code(evdev, EV_ABS, ABS_Y) &&
if (libevdev_has_event_code(evdev, EV_REL, REL_X) &&
libevdev_has_event_code(evdev, EV_REL, REL_Y) &&
evdev_device_init_pointer_acceleration(
device,
pointer_accel_profile_linear) == -1)
@ -1689,6 +1927,13 @@ evdev_configure_device(struct evdev_device *device)
log_info(libinput,
"input device '%s', %s is a keyboard\n",
device->devname, devnode);
/* want natural-scroll config option */
if (libevdev_has_event_code(evdev, EV_REL, REL_WHEEL) ||
libevdev_has_event_code(evdev, EV_REL, REL_HWHEEL)) {
device->scroll.natural_scrolling_enabled = true;
device->seat_caps |= EVDEV_DEVICE_POINTER;
}
}
if (udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN) {
@ -1804,7 +2049,8 @@ evdev_device_create(struct libinput_seat *seat,
/* Use non-blocking mode so that we can loop on read on
* evdev_device_data() until all events on the fd are
* read. mtdev_get() also expects this. */
fd = open_restricted(libinput, devnode, O_RDWR | O_NONBLOCK);
fd = open_restricted(libinput, devnode,
O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (fd < 0) {
log_info(libinput,
"opening input device '%s' failed (%s).\n",
@ -1844,6 +2090,7 @@ evdev_device_create(struct libinput_seat *seat,
device->scroll.wheel_click_angle =
evdev_read_wheel_click_prop(device);
device->dpi = evdev_read_dpi_prop(device);
device->model = evdev_read_model(device);
/* at most 5 SYN_DROPPED log-messages per 30s */
ratelimit_init(&device->syn_drop_limit, 30ULL * 1000, 5);
@ -2044,6 +2291,15 @@ evdev_device_has_button(struct evdev_device *device, uint32_t code)
return libevdev_has_event_code(device->evdev, EV_KEY, code);
}
int
evdev_device_has_key(struct evdev_device *device, uint32_t code)
{
if (!(device->seat_caps & EVDEV_DEVICE_KEYBOARD))
return -1;
return libevdev_has_event_code(device->evdev, EV_KEY, code);
}
static inline bool
evdev_is_scrolling(const struct evdev_device *device,
enum libinput_pointer_axis axis)
@ -2121,7 +2377,7 @@ evdev_post_scroll(struct evdev_device *device,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
event.x = 0.0;
if (event.x != 0.0 || event.y != 0.0) {
if (!normalized_is_zero(event)) {
const struct discrete_coords zero_discrete = { 0.0, 0.0 };
evdev_notify_axis(device,
time,
@ -2154,49 +2410,6 @@ evdev_stop_scroll(struct evdev_device *device,
device->scroll.direction = 0;
}
static void
release_pressed_keys(struct evdev_device *device)
{
struct libinput *libinput = device->base.seat->libinput;
uint64_t time;
int code;
if ((time = libinput_now(libinput)) == 0)
return;
for (code = 0; code < KEY_CNT; code++) {
int count = get_key_down_count(device, code);
if (count > 1) {
log_bug_libinput(libinput,
"Key %d is down %d times.\n",
code,
count);
}
while (get_key_down_count(device, code) > 0) {
switch (get_key_type(code)) {
case EVDEV_KEY_TYPE_NONE:
break;
case EVDEV_KEY_TYPE_KEY:
evdev_keyboard_notify_key(
device,
time,
code,
LIBINPUT_KEY_STATE_RELEASED);
break;
case EVDEV_KEY_TYPE_BUTTON:
evdev_pointer_notify_button(
device,
time,
evdev_to_left_handed(device, code),
LIBINPUT_BUTTON_STATE_RELEASED);
break;
}
}
}
}
void
evdev_notify_suspended_device(struct evdev_device *device)
{
@ -2242,14 +2455,16 @@ evdev_device_suspend(struct evdev_device *device)
{
evdev_notify_suspended_device(device);
if (device->dispatch->interface->suspend)
device->dispatch->interface->suspend(device->dispatch,
device);
if (device->source) {
libinput_remove_source(device->base.seat->libinput,
device->source);
device->source = NULL;
}
release_pressed_keys(device);
if (device->mtdev) {
mtdev_close_delete(device->mtdev);
device->mtdev = NULL;
@ -2279,7 +2494,8 @@ evdev_device_resume(struct evdev_device *device)
return -ENODEV;
devnode = udev_device_get_devnode(device->udev_device);
fd = open_restricted(libinput, devnode, O_RDWR | O_NONBLOCK);
fd = open_restricted(libinput, devnode,
O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (fd < 0)
return -errno;

View file

@ -36,6 +36,13 @@
/* The HW DPI rate we normalize to before calculating pointer acceleration */
#define DEFAULT_MOUSE_DPI 1000
/*
* The constant (linear) acceleration factor we use to normalize trackpoint
* deltas before calculating pointer acceleration.
*/
#define DEFAULT_TRACKPOINT_ACCEL 1.0
/* The fake resolution value for abs devices without resolution */
#define EVDEV_FAKE_RESOLUTION 1
@ -64,6 +71,39 @@ enum evdev_device_tags {
EVDEV_TAG_TOUCHPAD_TRACKPOINT = (1 << 3),
};
enum evdev_middlebutton_state {
MIDDLEBUTTON_IDLE,
MIDDLEBUTTON_LEFT_DOWN,
MIDDLEBUTTON_RIGHT_DOWN,
MIDDLEBUTTON_MIDDLE,
MIDDLEBUTTON_LEFT_UP_PENDING,
MIDDLEBUTTON_RIGHT_UP_PENDING,
MIDDLEBUTTON_IGNORE_LR,
MIDDLEBUTTON_IGNORE_L,
MIDDLEBUTTON_IGNORE_R,
MIDDLEBUTTON_PASSTHROUGH,
};
enum evdev_middlebutton_event {
MIDDLEBUTTON_EVENT_L_DOWN,
MIDDLEBUTTON_EVENT_R_DOWN,
MIDDLEBUTTON_EVENT_OTHER,
MIDDLEBUTTON_EVENT_L_UP,
MIDDLEBUTTON_EVENT_R_UP,
MIDDLEBUTTON_EVENT_TIMEOUT,
MIDDLEBUTTON_EVENT_ALL_UP,
};
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,
};
struct mt_slot {
int32_t seat_slot;
struct device_coords point;
@ -159,8 +199,22 @@ struct evdev_device {
void (*change_to_enabled)(struct evdev_device *device);
} left_handed;
struct {
struct libinput_device_config_middle_emulation config;
/* middle-button emulation enabled */
bool enabled;
bool enabled_default;
bool want_enabled;
enum evdev_middlebutton_state state;
struct libinput_timer timer;
uint32_t button_mask;
uint64_t first_event_time;
} middlebutton;
int dpi; /* HW resolution */
struct ratelimit syn_drop_limit; /* ratelimit for SYN_DROPPED logging */
enum evdev_device_model model;
};
#define EVDEV_UNHANDLED_DEVICE ((struct evdev_device *) 1)
@ -174,6 +228,10 @@ struct evdev_dispatch_interface {
struct input_event *event,
uint64_t time);
/* Device is being suspended */
void (*suspend)(struct evdev_dispatch *dispatch,
struct evdev_device *device);
/* Device is being removed (may be NULL) */
void (*remove)(struct evdev_dispatch *dispatch);
@ -283,6 +341,9 @@ evdev_device_get_size(struct evdev_device *device,
int
evdev_device_has_button(struct evdev_device *device, uint32_t code);
int
evdev_device_has_key(struct evdev_device *device, uint32_t code);
double
evdev_device_transform_x(struct evdev_device *device,
double x,
@ -315,6 +376,11 @@ evdev_pointer_notify_button(struct evdev_device *device,
uint32_t time,
int button,
enum libinput_button_state state);
void
evdev_pointer_notify_physical_button(struct evdev_device *device,
uint32_t time,
int button,
enum libinput_button_state state);
void
evdev_init_natural_scroll(struct evdev_device *device);
@ -336,6 +402,17 @@ evdev_device_remove(struct evdev_device *device);
void
evdev_device_destroy(struct evdev_device *device);
bool
evdev_middlebutton_filter_button(struct evdev_device *device,
uint64_t time,
int button,
enum libinput_button_state state);
void
evdev_init_middlebutton(struct evdev_device *device,
bool enabled,
bool want_config);
static inline double
evdev_convert_to_mm(const struct input_absinfo *absinfo, double v)
{

View file

@ -1,4 +1,5 @@
/*
* Copyright © 2006-2009 Simon Thum
* Copyright © 2012 Jonas Ådahl
*
* Permission to use, copy, modify, distribute, and sell this software and
@ -122,7 +123,7 @@ feed_trackers(struct pointer_accelerator *accel,
trackers[current].delta.x = 0.0;
trackers[current].delta.y = 0.0;
trackers[current].time = time;
trackers[current].dir = vector_get_direction(delta->x, delta->y);
trackers[current].dir = normalized_get_direction(*delta);
}
static struct pointer_tracker *
@ -137,11 +138,9 @@ tracker_by_offset(struct pointer_accelerator *accel, unsigned int offset)
static double
calculate_tracker_velocity(struct pointer_tracker *tracker, uint64_t time)
{
double distance;
double tdelta = time - tracker->time + 1;
distance = hypot(tracker->delta.x, tracker->delta.y);
return distance / tdelta; /* units/ms */
return normalized_length(tracker->delta) / tdelta; /* units/ms */
}
static double
@ -260,13 +259,15 @@ accelerator_set_speed(struct motion_filter *filter,
assert(speed >= -1.0 && speed <= 1.0);
/* delay when accel kicks in */
accel_filter->threshold = DEFAULT_THRESHOLD - speed/6.0;
accel_filter->threshold = DEFAULT_THRESHOLD - speed / 4.0;
if (accel_filter->threshold < 0.2)
accel_filter->threshold = 0.2;
/* adjust max accel factor */
accel_filter->accel = DEFAULT_ACCELERATION + speed;
accel_filter->accel = DEFAULT_ACCELERATION + speed * 1.5;
/* higher speed -> faster to reach max */
accel_filter->incline = DEFAULT_INCLINE + speed/2.0;
accel_filter->incline = DEFAULT_INCLINE + speed * 0.75;
filter->speed = speed;
return true;
@ -345,3 +346,40 @@ touchpad_accel_profile_linear(struct motion_filter *filter,
return speed_out * TP_MAGIC_SLOWDOWN;
}
double
touchpad_lenovo_x230_accel_profile(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time)
{
/* Keep the magic factor from touchpad_accel_profile_linear. */
const double TP_MAGIC_SLOWDOWN = 0.4;
/* Those touchpads presents an actual lower resolution that what is
* advertised. We see some jumps from the cursor due to the big steps
* in X and Y when we are receiving data.
* Apply a factor to minimize those jumps at low speed, and try
* keeping the same feeling as regular touchpads at high speed.
* It still feels slower but it is usable at least */
const double TP_MAGIC_LOW_RES_FACTOR = 4.0;
double speed_out;
struct pointer_accelerator *accel_filter =
(struct pointer_accelerator *)filter;
double s1, s2;
const double max_accel = accel_filter->accel *
TP_MAGIC_LOW_RES_FACTOR; /* unitless factor */
const double threshold = accel_filter->threshold /
TP_MAGIC_LOW_RES_FACTOR; /* units/ms */
const double incline = accel_filter->incline * TP_MAGIC_LOW_RES_FACTOR;
speed_in *= TP_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR;
s1 = min(1, speed_in * 5);
s2 = 1 + (speed_in - threshold) * incline;
speed_out = min(max_accel, s2 > 1 ? s2 : s1);
return speed_out * TP_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR;
}

View file

@ -67,4 +67,9 @@ touchpad_accel_profile_linear(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time);
double
touchpad_lenovo_x230_accel_profile(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time);
#endif /* FILTER_H */

View file

@ -24,6 +24,7 @@
#define LIBINPUT_PRIVATE_H
#include <errno.h>
#include <math.h>
#include "linux/input.h"
@ -39,6 +40,14 @@ struct device_coords {
int x, y;
};
/*
* A coordinate pair in device coordinates, capable of holding non discrete
* values, this is necessary e.g. when device coordinates get averaged.
*/
struct device_float_coords {
double x, y;
};
/* A dpi-normalized coordinate pair */
struct normalized_coords {
double x, y;
@ -173,6 +182,17 @@ struct libinput_device_config_click_method {
enum libinput_config_click_method (*get_default_method)(struct libinput_device *device);
};
struct libinput_device_config_middle_emulation {
int (*available)(struct libinput_device *device);
enum libinput_config_status (*set)(
struct libinput_device *device,
enum libinput_config_middle_emulation_state);
enum libinput_config_middle_emulation_state (*get)(
struct libinput_device *device);
enum libinput_config_middle_emulation_state (*get_default)(
struct libinput_device *device);
};
struct libinput_device_config {
struct libinput_device_config_tap *tap;
struct libinput_device_config_calibration *calibration;
@ -182,6 +202,7 @@ struct libinput_device_config {
struct libinput_device_config_left_handed *left_handed;
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_group {
@ -393,4 +414,85 @@ libinput_now(struct libinput *libinput)
return ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000;
}
static inline struct device_float_coords
device_delta(struct device_coords a, struct device_coords b)
{
struct device_float_coords delta;
delta.x = a.x - b.x;
delta.y = a.y - b.y;
return delta;
}
static inline double
normalized_length(struct normalized_coords norm)
{
return hypot(norm.x, norm.y);
}
static inline int
normalized_is_zero(struct normalized_coords norm)
{
return norm.x == 0.0 && norm.y == 0.0;
}
enum directions {
N = 1 << 0,
NE = 1 << 1,
E = 1 << 2,
SE = 1 << 3,
S = 1 << 4,
SW = 1 << 5,
W = 1 << 6,
NW = 1 << 7,
UNDEFINED_DIRECTION = 0xff
};
static inline int
normalized_get_direction(struct normalized_coords norm)
{
int dir = UNDEFINED_DIRECTION;
int d1, d2;
double r;
if (fabs(norm.x) < 2.0 && fabs(norm.y) < 2.0) {
if (norm.x > 0.0 && norm.y > 0.0)
dir = S | SE | E;
else if (norm.x > 0.0 && norm.y < 0.0)
dir = N | NE | E;
else if (norm.x < 0.0 && norm.y > 0.0)
dir = S | SW | W;
else if (norm.x < 0.0 && norm.y < 0.0)
dir = N | NW | W;
else if (norm.x > 0.0)
dir = NE | E | SE;
else if (norm.x < 0.0)
dir = NW | W | SW;
else if (norm.y > 0.0)
dir = SE | S | SW;
else if (norm.y < 0.0)
dir = NE | N | NW;
} else {
/* Calculate r within the interval [0 to 8)
*
* r = [0 .. 2π] where 0 is North
* d_f = r / 2π ([0 .. 1))
* d_8 = 8 * d_f
*/
r = atan2(norm.y, norm.x);
r = fmod(r + 2.5*M_PI, 2*M_PI);
r *= 4*M_1_PI;
/* Mark one or two close enough octants */
d1 = (int)(r + 0.9) % 8;
d2 = (int)(r + 0.1) % 8;
dir = (1 << d1) | (1 << d2);
}
return dir;
}
#endif /* LIBINPUT_PRIVATE_H */

View file

@ -29,6 +29,7 @@
#include "config.h"
#include <ctype.h>
#include <locale.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
@ -201,3 +202,33 @@ parse_mouse_wheel_click_angle_property(const char *prop)
return angle;
}
/**
* Helper function to parse the TRACKPOINT_CONST_ACCEL property from udev.
* Property is of the form:
* TRACKPOINT_CONST_ACCEL=<float>
*
* @param prop The value of the udev property (without the TRACKPOINT_CONST_ACCEL=)
* @return The acceleration, or 0.0 on error.
*/
double
parse_trackpoint_accel_property(const char *prop)
{
locale_t c_locale;
double accel;
char *endp;
/* Create a "C" locale to force strtod to use '.' as separator */
c_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
if (c_locale == (locale_t)0)
return 0.0;
accel = strtod_l(prop, &endp, c_locale);
freelocale(c_locale);
if (*endp != '\0')
return 0.0;
return accel;
}

View file

@ -122,63 +122,6 @@ msleep(unsigned int ms)
usleep(ms * 1000);
}
enum directions {
N = 1 << 0,
NE = 1 << 1,
E = 1 << 2,
SE = 1 << 3,
S = 1 << 4,
SW = 1 << 5,
W = 1 << 6,
NW = 1 << 7,
UNDEFINED_DIRECTION = 0xff
};
static inline int
vector_get_direction(double dx, double dy)
{
int dir = UNDEFINED_DIRECTION;
int d1, d2;
double r;
if (fabs(dx) < 2.0 && fabs(dy) < 2.0) {
if (dx > 0.0 && dy > 0.0)
dir = S | SE | E;
else if (dx > 0.0 && dy < 0.0)
dir = N | NE | E;
else if (dx < 0.0 && dy > 0.0)
dir = S | SW | W;
else if (dx < 0.0 && dy < 0.0)
dir = N | NW | W;
else if (dx > 0.0)
dir = NE | E | SE;
else if (dx < 0.0)
dir = NW | W | SW;
else if (dy > 0.0)
dir = SE | S | SW;
else if (dy < 0.0)
dir = NE | N | NW;
} else {
/* Calculate r within the interval [0 to 8)
*
* r = [0 .. 2π] where 0 is North
* d_f = r / 2π ([0 .. 1))
* d_8 = 8 * d_f
*/
r = atan2(dy, dx);
r = fmod(r + 2.5*M_PI, 2*M_PI);
r *= 4*M_1_PI;
/* Mark one or two close enough octants */
d1 = (int)(r + 0.9) % 8;
d2 = (int)(r + 0.1) % 8;
dir = (1 << d1) | (1 << d2);
}
return dir;
}
static inline int
long_bit_is_set(const unsigned long *array, int bit)
{
@ -323,5 +266,6 @@ enum ratelimit_state ratelimit_test(struct ratelimit *r);
int parse_mouse_dpi_property(const char *prop);
int parse_mouse_wheel_click_angle_property(const char *prop);
double parse_trackpoint_accel_property(const char *prop);
#endif /* LIBINPUT_UTIL_H */

View file

@ -1326,6 +1326,35 @@ notify_removed_device(struct libinput_device *device)
&removed_device_event->base);
}
static inline bool
device_has_cap(struct libinput_device *device,
enum libinput_device_capability cap)
{
const char *capability;
if (libinput_device_has_capability(device, cap))
return true;
switch (cap) {
case LIBINPUT_DEVICE_CAP_POINTER:
capability = "CAP_POINTER";
break;
case LIBINPUT_DEVICE_CAP_KEYBOARD:
capability = "CAP_KEYBOARD";
break;
case LIBINPUT_DEVICE_CAP_TOUCH:
capability = "CAP_TOUCH";
break;
}
log_bug_libinput(device->seat->libinput,
"Event for missing capability %s on device \"%s\"\n",
capability,
libinput_device_get_name(device));
return false;
}
void
keyboard_notify_key(struct libinput_device *device,
uint64_t time,
@ -1335,6 +1364,9 @@ keyboard_notify_key(struct libinput_device *device,
struct libinput_event_keyboard *key_event;
uint32_t seat_key_count;
if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_KEYBOARD))
return;
key_event = zalloc(sizeof *key_event);
if (!key_event)
return;
@ -1361,6 +1393,9 @@ pointer_notify_motion(struct libinput_device *device,
{
struct libinput_event_pointer *motion_event;
if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_POINTER))
return;
motion_event = zalloc(sizeof *motion_event);
if (!motion_event)
return;
@ -1383,6 +1418,9 @@ pointer_notify_motion_absolute(struct libinput_device *device,
{
struct libinput_event_pointer *motion_absolute_event;
if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_POINTER))
return;
motion_absolute_event = zalloc(sizeof *motion_absolute_event);
if (!motion_absolute_event)
return;
@ -1406,6 +1444,9 @@ pointer_notify_button(struct libinput_device *device,
struct libinput_event_pointer *button_event;
int32_t seat_button_count;
if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_POINTER))
return;
button_event = zalloc(sizeof *button_event);
if (!button_event)
return;
@ -1436,6 +1477,9 @@ pointer_notify_axis(struct libinput_device *device,
{
struct libinput_event_pointer *axis_event;
if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_POINTER))
return;
axis_event = zalloc(sizeof *axis_event);
if (!axis_event)
return;
@ -1462,6 +1506,9 @@ touch_notify_touch_down(struct libinput_device *device,
{
struct libinput_event_touch *touch_event;
if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_TOUCH))
return;
touch_event = zalloc(sizeof *touch_event);
if (!touch_event)
return;
@ -1487,6 +1534,9 @@ touch_notify_touch_motion(struct libinput_device *device,
{
struct libinput_event_touch *touch_event;
if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_TOUCH))
return;
touch_event = zalloc(sizeof *touch_event);
if (!touch_event)
return;
@ -1511,6 +1561,9 @@ touch_notify_touch_up(struct libinput_device *device,
{
struct libinput_event_touch *touch_event;
if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_TOUCH))
return;
touch_event = zalloc(sizeof *touch_event);
if (!touch_event)
return;
@ -1532,6 +1585,9 @@ touch_notify_frame(struct libinput_device *device,
{
struct libinput_event_touch *touch_event;
if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_TOUCH))
return;
touch_event = zalloc(sizeof *touch_event);
if (!touch_event)
return;
@ -1855,6 +1911,12 @@ libinput_device_pointer_has_button(struct libinput_device *device, uint32_t code
return evdev_device_has_button((struct evdev_device *)device, code);
}
LIBINPUT_EXPORT int
libinput_device_keyboard_has_key(struct libinput_device *device, uint32_t code)
{
return evdev_device_has_key((struct evdev_device *)device, code);
}
LIBINPUT_EXPORT struct libinput_event *
libinput_event_device_notify_get_base_event(struct libinput_event_device_notify *event)
{
@ -2010,11 +2072,12 @@ libinput_device_config_tap_set_enabled(struct libinput_device *device,
enable != LIBINPUT_CONFIG_TAP_DISABLED)
return LIBINPUT_CONFIG_STATUS_INVALID;
if (enable &&
libinput_device_config_tap_get_finger_count(device) == 0)
return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
if (libinput_device_config_tap_get_finger_count(device) == 0)
return enable ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED :
LIBINPUT_CONFIG_STATUS_SUCCESS;
return device->config.tap->set_enabled(device, enable);
}
LIBINPUT_EXPORT enum libinput_config_tap_state
@ -2237,9 +2300,6 @@ LIBINPUT_EXPORT enum libinput_config_status
libinput_device_config_click_set_method(struct libinput_device *device,
enum libinput_config_click_method method)
{
if ((libinput_device_config_click_get_methods(device) & method) != method)
return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
/* Check method is a single valid method */
switch (method) {
case LIBINPUT_CONFIG_CLICK_METHOD_NONE:
@ -2250,6 +2310,9 @@ libinput_device_config_click_set_method(struct libinput_device *device,
return LIBINPUT_CONFIG_STATUS_INVALID;
}
if ((libinput_device_config_click_get_methods(device) & method) != method)
return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
if (device->config.click_method)
return device->config.click_method->set_method(device, method);
else /* method must be _NONE to get here */
@ -2274,6 +2337,60 @@ libinput_device_config_click_get_default_method(struct libinput_device *device)
return LIBINPUT_CONFIG_CLICK_METHOD_NONE;
}
LIBINPUT_EXPORT int
libinput_device_config_middle_emulation_is_available(
struct libinput_device *device)
{
if (device->config.middle_emulation)
return device->config.middle_emulation->available(device);
else
return LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
}
LIBINPUT_EXPORT enum libinput_config_status
libinput_device_config_middle_emulation_set_enabled(
struct libinput_device *device,
enum libinput_config_middle_emulation_state enable)
{
int available =
libinput_device_config_middle_emulation_is_available(device);
switch (enable) {
case LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED:
if (!available)
return LIBINPUT_CONFIG_STATUS_SUCCESS;
break;
case LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED:
if (!available)
return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
break;
default:
return LIBINPUT_CONFIG_STATUS_INVALID;
}
return device->config.middle_emulation->set(device, enable);
}
LIBINPUT_EXPORT enum libinput_config_middle_emulation_state
libinput_device_config_middle_emulation_get_enabled(
struct libinput_device *device)
{
if (!libinput_device_config_middle_emulation_is_available(device))
return LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
return device->config.middle_emulation->get(device);
}
LIBINPUT_EXPORT enum libinput_config_middle_emulation_state
libinput_device_config_middle_emulation_get_default_enabled(
struct libinput_device *device)
{
if (!libinput_device_config_middle_emulation_is_available(device))
return LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
return device->config.middle_emulation->get_default(device);
}
LIBINPUT_EXPORT uint32_t
libinput_device_config_scroll_get_methods(struct libinput_device *device)
{

View file

@ -2183,6 +2183,22 @@ libinput_device_get_size(struct libinput_device *device,
int
libinput_device_pointer_has_button(struct libinput_device *device, uint32_t code);
/**
* @ingroup device
*
* Check if a @ref LIBINPUT_DEVICE_CAP_KEYBOARD device has a key with the
* given code (see linux/input.h).
*
* @param device A current input device
* @param code Key code to check for, e.g. <i>KEY_ESC</i>
*
* @return 1 if the device supports this key code, 0 if it does not, -1
* on error.
*/
int
libinput_device_keyboard_has_key(struct libinput_device *device,
uint32_t code);
/**
* @ingroup device
*
@ -2914,6 +2930,130 @@ libinput_device_config_click_get_method(struct libinput_device *device);
enum libinput_config_click_method
libinput_device_config_click_get_default_method(struct libinput_device *device);
/**
* @ingroup config
*/
enum libinput_config_middle_emulation_state {
/**
* Middle mouse button emulation is to be disabled, or
* is currently disabled.
*/
LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED,
/**
* Middle mouse button emulation is to be enabled, or
* is currently enabled.
*/
LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED,
};
/**
* @ingroup config
*
* Check if middle mouse button emulation configuration is available on this
* device. See libinput_device_config_middle_emulation_set_enabled() for
* details.
*
* @note Some devices provide middle mouse button emulation but do not allow
* enabling/disabling that emulation. These devices return zero in
* libinput_device_config_middle_emulation_is_available().
*
* @param device The device to query
*
* @return Non-zero if middle mouse button emulation is available and can be
* configured, zero otherwise.
*
* @see libinput_device_config_middle_emulation_set_enabled
* @see libinput_device_config_middle_emulation_get_enabled
* @see libinput_device_config_middle_emulation_get_default_enabled
*/
int
libinput_device_config_middle_emulation_is_available(
struct libinput_device *device);
/**
* @ingroup config
*
* Enable or disable middle button emulation on this device. When enabled, a
* simultaneous press of the left and right button generates a middle mouse
* button event. Releasing the buttons generates a middle mouse button
* release, the left and right button events are discarded otherwise.
*
* The middle button release event may be generated when either button is
* released, or when both buttons have been released. The exact behavior is
* device-dependent.
*
* The middle button emulation behavior when combined with other device
* buttons, including a physical middle button is device-dependent.
*
* @note Some devices provide middle mouse button emulation but do not allow
* enabling/disabling that emulation.
*
* @param device The device to configure
* @param enable @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED to
* disable, @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED To enable
* middle button emulation.
*
* @return A config status code. Disabling middle button emulation on a
* device that does not support middle button emulation always succeeds.
*
* @see libinput_device_config_middle_emulation_is_available
* @see libinput_device_config_middle_emulation_get_enabled
* @see libinput_device_config_middle_emulation_get_default_enabled
*/
enum libinput_config_status
libinput_device_config_middle_emulation_set_enabled(
struct libinput_device *device,
enum libinput_config_middle_emulation_state enable);
/**
* @ingroup config
*
* Check if configurable middle button emulation is enabled on this device.
* If the device does not have configurable middle button emulation, this
* function returns @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED.
*
* @note Some devices provide middle mouse button emulation but do not allow
* enabling/disabling that emulation. These devices always return @ref
* LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED.
*
* @param device The device to configure
* @return @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED if disabled
* or not available/configurable, @ref
* LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED If enabled.
*
* @see libinput_device_config_middle_emulation_is_available
* @see libinput_device_config_middle_emulation_set_enabled
* @see libinput_device_config_middle_emulation_get_default_enabled
*/
enum libinput_config_middle_emulation_state
libinput_device_config_middle_emulation_get_enabled(
struct libinput_device *device);
/**
* @ingroup config
*
* Check if configurable middle button emulation is enabled by default on
* this device. If the device does not have configurable middle button
* emulation, this function returns @ref
* LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED.
*
* @note Some devices provide middle mouse button emulation but do not allow
* enabling/disabling that emulation. These devices always return @ref
* LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED.
*
* @param device The device to configure
* @return @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED If disabled
* or not available, @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED if
* enabled.
*
* @see libinput_device_config_middle_emulation_is_available
* @see libinput_device_config_middle_emulation_set_enabled
* @see libinput_device_config_middle_emulation_get_enabled
*/
enum libinput_config_middle_emulation_state
libinput_device_config_middle_emulation_get_default_enabled(
struct libinput_device *device);
/**
* @ingroup config
*

View file

@ -129,6 +129,19 @@ local:
*;
};
LIBINPUT_0.14.0 {
global:
libinput_device_config_middle_emulation_get_default_enabled;
libinput_device_config_middle_emulation_get_enabled;
libinput_device_config_middle_emulation_is_available;
libinput_device_config_middle_emulation_set_enabled;
} LIBINPUT_0.12.0;
LIBINPUT_0.15.0 {
global:
libinput_device_keyboard_has_key;
} LIBINPUT_0.14.0;
/* tablet APIs, they are not part of any stable API promise yet.
* keep them separate */
LIBINPUT_TABLET_SUPPORT {
@ -154,4 +167,4 @@ LIBINPUT_TABLET_SUPPORT {
libinput_tool_ref;
libinput_tool_set_user_data;
libinput_tool_unref;
} LIBINPUT_0.12.0;
} LIBINPUT_0.15.0;

View file

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

View file

@ -101,6 +101,15 @@ libinput_timer_handler(void *data)
struct libinput *libinput = data;
struct libinput_timer *timer, *tmp;
uint64_t now;
uint64_t discard;
int r;
r = read(libinput->timer.fd, &discard, sizeof(discard));
if (r == -1 && errno != EAGAIN)
log_bug_libinput(libinput,
"Error %d reading from timerfd (%s)",
errno,
strerror(errno));
now = libinput_now(libinput);
if (now == 0)
@ -119,7 +128,8 @@ libinput_timer_handler(void *data)
int
libinput_timer_subsys_init(struct libinput *libinput)
{
libinput->timer.fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
libinput->timer.fd = timerfd_create(CLOCK_MONOTONIC,
TFD_CLOEXEC | TFD_NONBLOCK);
if (libinput->timer.fd < 0)
return -1;

View file

@ -14,10 +14,14 @@ liblitest_la_SOURCES = \
litest.h \
litest-int.h \
litest-alps-semi-mt.c \
litest-atmel-hover.c \
litest-bcm5974.c \
litest-generic-singletouch.c \
litest-keyboard.c \
litest-keyboard-razer-blackwidow.c \
litest-logitech-trackball.c \
litest-mouse.c \
litest-mouse-roccat.c \
litest-ms-surface-cover.c \
litest-protocol-a-touch-screen.c \
litest-qemu-usb-tablet.c \
@ -33,23 +37,31 @@ liblitest_la_SOURCES = \
litest-wacom-isdv4-tablet.c \
litest-wacom-touch.c \
litest-wacom-intuos-finger.c \
litest-wheel-only.c \
litest-xen-virtual-pointer.c \
litest-vmware-virtual-usb-mouse.c \
litest.c
liblitest_la_LIBADD = $(top_builddir)/src/libinput-util.la
liblitest_la_CFLAGS = $(AM_CFLAGS)
if HAVE_LIBUNWIND
liblitest_la_LIBADD += $(LIBUNWIND_LIBS) -ldl
liblitest_la_CFLAGS += $(LIBUNWIND_CFLAGS)
endif
run_tests = \
test-udev \
test-path \
test-touchpad \
test-tablet \
test-device \
test-pointer \
test-touch \
test-log \
test-tablet \
test-touchpad \
test-trackpoint \
test-udev \
test-path \
test-log \
test-misc \
test-keyboard \
test-device
test-litest-selftest
build_tests = \
test-build-cxx \
test-build-linker \
@ -83,7 +95,6 @@ test_log_LDADD = $(TEST_LIBS)
test_log_LDFLAGS = -no-install
test_tablet_SOURCES = tablet.c
test_tablet_CFLAGS = $(AM_CPPFLAGS)
test_tablet_LDADD = $(TEST_LIBS)
test_tablet_LDFLAGS = -static
@ -107,6 +118,11 @@ test_device_SOURCES = device.c
test_device_LDADD = $(TEST_LIBS)
test_device_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
test_litest_selftest_LDADD = $(TEST_LIBS)
test_litest_selftest_LDFLAGS = -no-install
# build-test only
test_build_pedantic_c99_SOURCES = build-pedantic.c
test_build_pedantic_c99_CFLAGS = -std=c99 -pedantic -Werror
@ -123,6 +139,8 @@ test_build_linker_LDADD = $(top_builddir)/src/libinput.la $(top_builddir)/src/li
test_build_cxx_SOURCES = build-cxx.cc
test_build_cxx_CXXFLAGS = -Wall -Wextra -Wno-unused-parameter $(AM_CXXFLAGS)
AM_TESTS_ENVIRONMENT= LITEST_VERBOSE=1; export LITEST_VERBOSE;
if HAVE_VALGRIND
VALGRIND_FLAGS=--leak-check=full \
--quiet \
@ -130,7 +148,7 @@ VALGRIND_FLAGS=--leak-check=full \
--suppressions=$(srcdir)/valgrind.suppressions
valgrind:
$(MAKE) check-TESTS LOG_COMPILER="$(VALGRIND)" LOG_FLAGS="$(VALGRIND_FLAGS)" CK_FORK=no
$(MAKE) check-TESTS LOG_COMPILER="$(VALGRIND)" LOG_FLAGS="$(VALGRIND_FLAGS)" CK_FORK=no USING_VALGRIND=yes
check: valgrind

View file

@ -3,6 +3,7 @@
/* This is a build-test only */
int
main(void) {
main(void)
{
return 0;
}

View file

@ -313,6 +313,9 @@ START_TEST(device_reenable_syspath_changed)
litest_device = litest_add_device(li, LITEST_MOUSE);
device2 = litest_device->libinput_device;
/* Note: if the sysname isn't the same, some other device got added
* or removed while this test was running. This is unlikely and
* would result in a false positive, so let's fail the test here */
ck_assert_str_eq(libinput_device_get_sysname(device1),
libinput_device_get_sysname(device2));
@ -814,37 +817,39 @@ START_TEST(abs_mt_device_no_absx)
}
END_TEST
START_TEST(abs_device_no_range)
static void
assert_device_ignored(struct libinput *li, struct input_absinfo *absinfo)
{
struct libevdev_uinput *uinput;
struct libinput *li;
struct libinput_device *device;
int code;
uinput = litest_create_uinput_abs_device("test device", NULL,
absinfo,
EV_KEY, BTN_LEFT,
EV_KEY, BTN_RIGHT,
-1);
device = libinput_path_add_device(li,
libevdev_uinput_get_devnode(uinput));
litest_assert_ptr_null(device);
libevdev_uinput_destroy(uinput);
}
START_TEST(abs_device_no_range)
{
struct libinput *li;
int code = _i; /* looped test */
/* set x/y so libinput doesn't just reject for missing axes */
struct input_absinfo absinfo[] = {
{ ABS_X, 0, 10, 0, 0, 0 },
{ ABS_Y, 0, 10, 0, 0, 0 },
{ -1, 0, 0, 0, 0, 0 },
{ code, 0, 0, 0, 0, 0 },
{ -1, -1, -1, -1, -1, -1 }
};
li = litest_create_context();
litest_disable_log_handler(li);
for (code = 0; code < ABS_MT_SLOT; code++) {
if (code == ABS_MISC)
continue;
absinfo[2].value = code;
uinput = litest_create_uinput_abs_device("test device", NULL,
absinfo,
EV_KEY, BTN_LEFT,
EV_KEY, BTN_RIGHT,
-1);
device = libinput_path_add_device(li,
libevdev_uinput_get_devnode(uinput));
ck_assert(device == NULL);
libevdev_uinput_destroy(uinput);
}
assert_device_ignored(li, absinfo);
litest_restore_log_handler(li);
libinput_unref(li);
@ -853,10 +858,8 @@ END_TEST
START_TEST(abs_mt_device_no_range)
{
struct libevdev_uinput *uinput;
struct libinput *li;
struct libinput_device *device;
int code;
int code = _i; /* looped test */
/* set x/y so libinput doesn't just reject for missing axes */
struct input_absinfo absinfo[] = {
{ ABS_X, 0, 10, 0, 0, 0 },
@ -865,37 +868,90 @@ START_TEST(abs_mt_device_no_range)
{ ABS_MT_TRACKING_ID, 0, 255, 0, 0, 0 },
{ ABS_MT_POSITION_X, 0, 10, 0, 0, 0 },
{ ABS_MT_POSITION_Y, 0, 10, 0, 0, 0 },
{ -1, 0, 0, 0, 0, 0 },
{ code, 0, 0, 0, 0, 0 },
{ -1, -1, -1, -1, -1, -1 }
};
li = litest_create_context();
litest_disable_log_handler(li);
for (code = ABS_MT_SLOT + 1; code < ABS_CNT; code++) {
if (code == ABS_MT_TOOL_TYPE ||
code == ABS_MT_TRACKING_ID) /* kernel overrides it */
continue;
absinfo[6].value = code;
uinput = litest_create_uinput_abs_device("test device", NULL,
absinfo,
EV_KEY, BTN_LEFT,
EV_KEY, BTN_RIGHT,
-1);
device = libinput_path_add_device(li,
libevdev_uinput_get_devnode(uinput));
ck_assert(device == NULL);
libevdev_uinput_destroy(uinput);
}
if (code != ABS_MT_TOOL_TYPE &&
code != ABS_MT_TRACKING_ID) /* kernel overrides it */
assert_device_ignored(li, absinfo);
litest_restore_log_handler(li);
libinput_unref(li);
}
END_TEST
int main (int argc, char **argv)
START_TEST(abs_device_missing_res)
{
struct libinput *li;
struct input_absinfo absinfo[] = {
{ ABS_X, 0, 10, 0, 0, 10 },
{ ABS_Y, 0, 10, 0, 0, 0 },
{ -1, -1, -1, -1, -1, -1 }
};
li = litest_create_context();
litest_disable_log_handler(li);
assert_device_ignored(li, absinfo);
absinfo[0].resolution = 0;
absinfo[1].resolution = 20;
assert_device_ignored(li, absinfo);
litest_restore_log_handler(li);
libinput_unref(li);
}
END_TEST
START_TEST(abs_mt_device_missing_res)
{
struct libinput *li;
struct input_absinfo absinfo[] = {
{ ABS_X, 0, 10, 0, 0, 10 },
{ ABS_Y, 0, 10, 0, 0, 10 },
{ ABS_MT_SLOT, 0, 2, 0, 0, 0 },
{ ABS_MT_TRACKING_ID, 0, 255, 0, 0, 0 },
{ ABS_MT_POSITION_X, 0, 10, 0, 0, 10 },
{ ABS_MT_POSITION_Y, 0, 10, 0, 0, 0 },
{ -1, -1, -1, -1, -1, -1 }
};
li = litest_create_context();
litest_disable_log_handler(li);
assert_device_ignored(li, absinfo);
absinfo[4].resolution = 0;
absinfo[5].resolution = 20;
assert_device_ignored(li, absinfo);
litest_restore_log_handler(li);
libinput_unref(li);
}
END_TEST
START_TEST(device_wheel_only)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
ck_assert(libinput_device_has_capability(device,
LIBINPUT_DEVICE_CAP_POINTER));
}
END_TEST
void
litest_setup_tests(void)
{
struct range abs_range = { 0, ABS_MISC };
struct range abs_mt_range = { ABS_MT_SLOT + 1, ABS_CNT };
litest_add("device:sendevents", device_sendevents_config, LITEST_ANY, LITEST_TOUCHPAD|LITEST_TABLET);
litest_add("device:sendevents", device_sendevents_config_invalid, LITEST_ANY, LITEST_TABLET);
litest_add("device:sendevents", device_sendevents_config_touchpad, LITEST_TOUCHPAD, LITEST_TABLET);
@ -906,7 +962,6 @@ int main (int argc, char **argv)
litest_add("device:sendevents", device_disable_events_pending, LITEST_RELATIVE, LITEST_TOUCHPAD|LITEST_TABLET);
litest_add("device:sendevents", device_double_disable, LITEST_ANY, LITEST_TABLET);
litest_add("device:sendevents", device_double_enable, LITEST_ANY, LITEST_TABLET);
litest_add_no_device("device:sendevents", device_reenable_syspath_changed);
litest_add_no_device("device:sendevents", device_reenable_device_removed);
litest_add_for_device("device:sendevents", device_disable_release_buttons, LITEST_MOUSE);
@ -927,8 +982,10 @@ int main (int argc, char **argv)
litest_add_no_device("device:invalid devices", abs_device_no_absy);
litest_add_no_device("device:invalid devices", abs_mt_device_no_absx);
litest_add_no_device("device:invalid devices", abs_mt_device_no_absy);
litest_add_no_device("device:invalid devices", abs_device_no_range);
litest_add_no_device("device:invalid devices", abs_mt_device_no_range);
litest_add_ranged_no_device("device:invalid devices", abs_device_no_range, &abs_range);
litest_add_ranged_no_device("device:invalid devices", abs_mt_device_no_range, &abs_mt_range);
litest_add_no_device("device:invalid devices", abs_device_missing_res);
litest_add_no_device("device:invalid devices", abs_mt_device_missing_res);
return litest_run(argc, argv);
litest_add("device:wheel", device_wheel_only, LITEST_WHEEL, LITEST_RELATIVE|LITEST_ABSOLUTE);
}

View file

@ -61,11 +61,9 @@ START_TEST(keyboard_seat_key_count)
continue;
}
kev = libinput_event_get_keyboard_event(ev);
ck_assert_notnull(kev);
ck_assert_int_eq(libinput_event_keyboard_get_key(kev), KEY_A);
ck_assert_int_eq(libinput_event_keyboard_get_key_state(kev),
LIBINPUT_KEY_STATE_PRESSED);
kev = litest_is_keyboard_event(ev,
KEY_A,
LIBINPUT_KEY_STATE_PRESSED);
++expected_key_button_count;
seat_key_count =
@ -175,31 +173,6 @@ START_TEST(keyboard_ignore_no_pressed_release)
}
END_TEST
static void
test_key_event(struct litest_device *dev, unsigned int key, int state)
{
struct libinput *li = dev->libinput;
struct libinput_event *event;
struct libinput_event_keyboard *kevent;
litest_event(dev, EV_KEY, key, state);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
event = libinput_get_event(li);
ck_assert(event != NULL);
ck_assert_int_eq(libinput_event_get_type(event), LIBINPUT_EVENT_KEYBOARD_KEY);
kevent = libinput_event_get_keyboard_event(event);
ck_assert(kevent != NULL);
ck_assert_int_eq(libinput_event_keyboard_get_key(kevent), key);
ck_assert_int_eq(libinput_event_keyboard_get_key_state(kevent),
state ? LIBINPUT_KEY_STATE_PRESSED :
LIBINPUT_KEY_STATE_RELEASED);
libinput_event_destroy(event);
}
START_TEST(keyboard_key_auto_release)
{
struct libinput *libinput;
@ -244,7 +217,17 @@ START_TEST(keyboard_key_auto_release)
/* Send pressed events, without releasing */
for (i = 0; i < ARRAY_LENGTH(keys); ++i) {
test_key_event(dev, keys[i].code, 1);
key = keys[i].code;
litest_event(dev, EV_KEY, key, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(libinput);
event = libinput_get_event(libinput);
kevent = litest_is_keyboard_event(event,
key,
LIBINPUT_KEY_STATE_PRESSED);
libinput_event_destroy(event);
}
litest_drain_events(libinput);
@ -290,12 +273,49 @@ START_TEST(keyboard_key_auto_release)
}
END_TEST
int
main(int argc, char **argv)
START_TEST(keyboard_has_key)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
unsigned int code;
int evdev_has, libinput_has;
ck_assert(libinput_device_has_capability(
device,
LIBINPUT_DEVICE_CAP_KEYBOARD));
for (code = 0; code < KEY_CNT; code++) {
evdev_has = libevdev_has_event_code(dev->evdev, EV_KEY, code);
libinput_has = libinput_device_keyboard_has_key(device, code);
ck_assert_int_eq(evdev_has, libinput_has);
}
}
END_TEST
START_TEST(keyboard_keys_bad_device)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
unsigned int code;
int has_key;
if (libinput_device_has_capability(device,
LIBINPUT_DEVICE_CAP_KEYBOARD))
return;
for (code = 0; code < KEY_CNT; code++) {
has_key = libinput_device_keyboard_has_key(device, code);
ck_assert_int_eq(has_key, -1);
}
}
END_TEST
void
litest_setup_tests(void)
{
litest_add_no_device("keyboard:seat key count", keyboard_seat_key_count);
litest_add_no_device("keyboard:key counting", keyboard_ignore_no_pressed_release);
litest_add_no_device("keyboard:key counting", keyboard_key_auto_release);
return litest_run(argc, argv);
litest_add("keyboard:keys", keyboard_has_key, LITEST_KEYS, LITEST_ANY);
litest_add("keyboard:keys", keyboard_keys_bad_device, LITEST_ANY, LITEST_ANY);
}

149
test/litest-atmel-hover.c Normal file
View file

@ -0,0 +1,149 @@
/*
* Copyright © 2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <assert.h>
#include "libinput-util.h"
#include "litest.h"
#include "litest-int.h"
static void
atmel_hover_create(struct litest_device *d);
static void
litest_atmel_hover_setup(void)
{
struct litest_device *d = litest_create_device(LITEST_ATMEL_HOVER);
litest_set_current_device(d);
}
static struct input_event down[] = {
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_DISTANCE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static struct input_event move[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_DISTANCE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static struct input_event up[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = -1 },
{ .type = EV_ABS, .code = ABS_MT_DISTANCE, .value = 1 },
{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static struct litest_device_interface interface = {
.touch_down_events = down,
.touch_move_events = move,
.touch_up_events = up,
};
static struct input_id input_id = {
.bustype = 0x18,
.vendor = 0x0,
.product = 0x0,
};
static int events[] = {
EV_KEY, BTN_LEFT,
EV_KEY, BTN_TOOL_FINGER,
EV_KEY, BTN_TOUCH,
EV_KEY, BTN_TOOL_DOUBLETAP,
EV_KEY, BTN_TOOL_TRIPLETAP,
EV_KEY, BTN_TOOL_QUADTAP,
EV_KEY, BTN_TOOL_QUINTTAP,
INPUT_PROP_MAX, INPUT_PROP_POINTER,
INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
-1, -1,
};
static struct input_absinfo absinfo[] = {
{ ABS_X, 0, 960, 0, 0, 10 },
{ ABS_Y, 0, 540, 0, 0, 10 },
{ ABS_PRESSURE, 0, 255, 0, 0, 0 },
{ ABS_MT_SLOT, 0, 9, 0, 0, 0 },
{ ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0, 0 },
{ ABS_MT_ORIENTATION, 0, 255, 0, 0, 0 },
{ ABS_MT_POSITION_X, 0, 960, 0, 0, 10 },
{ ABS_MT_POSITION_Y, 0, 540, 0, 0, 10 },
{ ABS_MT_TOOL_TYPE, 0, 2, 0, 0, 0 },
{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
{ ABS_MT_PRESSURE, 0, 255, 0, 0, 0 },
{ ABS_MT_DISTANCE, 0, 1, 0, 0, 0 },
{ .value = -1 }
};
struct litest_test_device litest_atmel_hover_device = {
.type = LITEST_ATMEL_HOVER,
.features = LITEST_TOUCHPAD | LITEST_BUTTON | LITEST_CLICKPAD | LITEST_HOVER,
.shortname = "atmel hover",
.setup = litest_atmel_hover_setup,
.interface = &interface,
.create = atmel_hover_create,
.name = "Atmel maXTouch Touchpad",
.id = &input_id,
.events = events,
.absinfo = absinfo,
};
static void
atmel_hover_create(struct litest_device *d)
{
struct litest_semi_mt *semi_mt = zalloc(sizeof(*semi_mt));
assert(semi_mt);
d->private = semi_mt;
d->uinput = litest_create_uinput_device_from_description(
litest_atmel_hover_device.name,
litest_atmel_hover_device.id,
absinfo,
events);
d->interface = &interface;
}

View file

@ -33,7 +33,7 @@ static void litest_bcm5974_setup(void)
litest_set_current_device(d);
}
struct input_event down[] = {
static struct input_event down[] = {
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 30 },

View file

@ -0,0 +1,350 @@
/*
* Copyright © 2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "litest.h"
#include "litest-int.h"
/* Recording from https://bugs.freedesktop.org/show_bug.cgi?id=89783
* This is the second of 4 devices exported by this keyboard, the first is
* just a basic keyboard that is identical to the normal litest-keyboard.c
* file.
*/
static void litest_blackwidow_setup(void)
{
struct litest_device *d = litest_create_device(LITEST_KEYBOARD_BLACKWIDOW);
litest_set_current_device(d);
}
static struct input_id input_id = {
.bustype = 0x3,
.vendor = 0x1532,
.product = 0x11b,
};
static int events[] = {
EV_REL, REL_HWHEEL,
EV_KEY, KEY_ESC,
EV_KEY, KEY_1,
EV_KEY, KEY_2,
EV_KEY, KEY_3,
EV_KEY, KEY_4,
EV_KEY, KEY_5,
EV_KEY, KEY_6,
EV_KEY, KEY_7,
EV_KEY, KEY_8,
EV_KEY, KEY_9,
EV_KEY, KEY_0,
EV_KEY, KEY_MINUS,
EV_KEY, KEY_EQUAL,
EV_KEY, KEY_BACKSPACE,
EV_KEY, KEY_TAB,
EV_KEY, KEY_Q,
EV_KEY, KEY_W,
EV_KEY, KEY_E,
EV_KEY, KEY_R,
EV_KEY, KEY_T,
EV_KEY, KEY_Y,
EV_KEY, KEY_U,
EV_KEY, KEY_I,
EV_KEY, KEY_O,
EV_KEY, KEY_P,
EV_KEY, KEY_LEFTBRACE,
EV_KEY, KEY_RIGHTBRACE,
EV_KEY, KEY_ENTER,
EV_KEY, KEY_LEFTCTRL,
EV_KEY, KEY_A,
EV_KEY, KEY_S,
EV_KEY, KEY_D,
EV_KEY, KEY_F,
EV_KEY, KEY_G,
EV_KEY, KEY_H,
EV_KEY, KEY_J,
EV_KEY, KEY_K,
EV_KEY, KEY_L,
EV_KEY, KEY_SEMICOLON,
EV_KEY, KEY_APOSTROPHE,
EV_KEY, KEY_GRAVE,
EV_KEY, KEY_LEFTSHIFT,
EV_KEY, KEY_BACKSLASH,
EV_KEY, KEY_Z,
EV_KEY, KEY_X,
EV_KEY, KEY_C,
EV_KEY, KEY_V,
EV_KEY, KEY_B,
EV_KEY, KEY_N,
EV_KEY, KEY_M,
EV_KEY, KEY_COMMA,
EV_KEY, KEY_DOT,
EV_KEY, KEY_SLASH,
EV_KEY, KEY_RIGHTSHIFT,
EV_KEY, KEY_KPASTERISK,
EV_KEY, KEY_LEFTALT,
EV_KEY, KEY_SPACE,
EV_KEY, KEY_CAPSLOCK,
EV_KEY, KEY_F1,
EV_KEY, KEY_F2,
EV_KEY, KEY_F3,
EV_KEY, KEY_F4,
EV_KEY, KEY_F5,
EV_KEY, KEY_F6,
EV_KEY, KEY_F7,
EV_KEY, KEY_F8,
EV_KEY, KEY_F9,
EV_KEY, KEY_F10,
EV_KEY, KEY_NUMLOCK,
EV_KEY, KEY_SCROLLLOCK,
EV_KEY, KEY_KP7,
EV_KEY, KEY_KP8,
EV_KEY, KEY_KP9,
EV_KEY, KEY_KPMINUS,
EV_KEY, KEY_KP4,
EV_KEY, KEY_KP5,
EV_KEY, KEY_KP6,
EV_KEY, KEY_KPPLUS,
EV_KEY, KEY_KP1,
EV_KEY, KEY_KP2,
EV_KEY, KEY_KP3,
EV_KEY, KEY_KP0,
EV_KEY, KEY_KPDOT,
EV_KEY, KEY_ZENKAKUHANKAKU,
EV_KEY, KEY_102ND,
EV_KEY, KEY_F11,
EV_KEY, KEY_F12,
EV_KEY, KEY_RO,
EV_KEY, KEY_KATAKANA,
EV_KEY, KEY_HIRAGANA,
EV_KEY, KEY_HENKAN,
EV_KEY, KEY_KATAKANAHIRAGANA,
EV_KEY, KEY_MUHENKAN,
EV_KEY, KEY_KPJPCOMMA,
EV_KEY, KEY_KPENTER,
EV_KEY, KEY_RIGHTCTRL,
EV_KEY, KEY_KPSLASH,
EV_KEY, KEY_SYSRQ,
EV_KEY, KEY_RIGHTALT,
EV_KEY, KEY_HOME,
EV_KEY, KEY_UP,
EV_KEY, KEY_PAGEUP,
EV_KEY, KEY_LEFT,
EV_KEY, KEY_RIGHT,
EV_KEY, KEY_END,
EV_KEY, KEY_DOWN,
EV_KEY, KEY_PAGEDOWN,
EV_KEY, KEY_INSERT,
EV_KEY, KEY_DELETE,
EV_KEY, KEY_MUTE,
EV_KEY, KEY_VOLUMEDOWN,
EV_KEY, KEY_VOLUMEUP,
EV_KEY, KEY_POWER,
EV_KEY, KEY_KPEQUAL,
EV_KEY, KEY_PAUSE,
EV_KEY, KEY_KPCOMMA,
EV_KEY, KEY_HANGEUL,
EV_KEY, KEY_HANJA,
EV_KEY, KEY_YEN,
EV_KEY, KEY_LEFTMETA,
EV_KEY, KEY_RIGHTMETA,
EV_KEY, KEY_COMPOSE,
EV_KEY, KEY_STOP,
EV_KEY, KEY_AGAIN,
EV_KEY, KEY_PROPS,
EV_KEY, KEY_UNDO,
EV_KEY, KEY_FRONT,
EV_KEY, KEY_COPY,
EV_KEY, KEY_OPEN,
EV_KEY, KEY_PASTE,
EV_KEY, KEY_FIND,
EV_KEY, KEY_CUT,
EV_KEY, KEY_HELP,
EV_KEY, KEY_MENU,
EV_KEY, KEY_CALC,
EV_KEY, KEY_SLEEP,
EV_KEY, KEY_WAKEUP,
EV_KEY, KEY_FILE,
EV_KEY, KEY_WWW,
EV_KEY, KEY_COFFEE,
EV_KEY, KEY_MAIL,
EV_KEY, KEY_BOOKMARKS,
EV_KEY, KEY_BACK,
EV_KEY, KEY_FORWARD,
EV_KEY, KEY_EJECTCD,
EV_KEY, KEY_NEXTSONG,
EV_KEY, KEY_PLAYPAUSE,
EV_KEY, KEY_PREVIOUSSONG,
EV_KEY, KEY_STOPCD,
EV_KEY, KEY_RECORD,
EV_KEY, KEY_REWIND,
EV_KEY, KEY_PHONE,
EV_KEY, KEY_CONFIG,
EV_KEY, KEY_HOMEPAGE,
EV_KEY, KEY_REFRESH,
EV_KEY, KEY_EXIT,
EV_KEY, KEY_EDIT,
EV_KEY, KEY_SCROLLUP,
EV_KEY, KEY_SCROLLDOWN,
EV_KEY, KEY_KPLEFTPAREN,
EV_KEY, KEY_KPRIGHTPAREN,
EV_KEY, KEY_NEW,
EV_KEY, KEY_F13,
EV_KEY, KEY_F14,
EV_KEY, KEY_F15,
EV_KEY, KEY_F16,
EV_KEY, KEY_F17,
EV_KEY, KEY_F18,
EV_KEY, KEY_F19,
EV_KEY, KEY_F20,
EV_KEY, KEY_F21,
EV_KEY, KEY_F22,
EV_KEY, KEY_F23,
EV_KEY, KEY_F24,
EV_KEY, KEY_CLOSE,
EV_KEY, KEY_PLAY,
EV_KEY, KEY_FASTFORWARD,
EV_KEY, KEY_BASSBOOST,
EV_KEY, KEY_PRINT,
EV_KEY, KEY_CAMERA,
EV_KEY, KEY_CHAT,
EV_KEY, KEY_SEARCH,
EV_KEY, KEY_FINANCE,
EV_KEY, KEY_BRIGHTNESSDOWN,
EV_KEY, KEY_BRIGHTNESSUP,
EV_KEY, KEY_KBDILLUMTOGGLE,
EV_KEY, KEY_SAVE,
EV_KEY, KEY_DOCUMENTS,
EV_KEY, KEY_UNKNOWN,
EV_KEY, KEY_VIDEO_NEXT,
EV_KEY, KEY_BRIGHTNESS_AUTO,
EV_KEY, BTN_0,
EV_KEY, KEY_SELECT,
EV_KEY, KEY_GOTO,
EV_KEY, KEY_INFO,
EV_KEY, KEY_PROGRAM,
EV_KEY, KEY_PVR,
EV_KEY, KEY_SUBTITLE,
EV_KEY, KEY_ZOOM,
EV_KEY, KEY_KEYBOARD,
EV_KEY, KEY_PC,
EV_KEY, KEY_TV,
EV_KEY, KEY_TV2,
EV_KEY, KEY_VCR,
EV_KEY, KEY_VCR2,
EV_KEY, KEY_SAT,
EV_KEY, KEY_CD,
EV_KEY, KEY_TAPE,
EV_KEY, KEY_TUNER,
EV_KEY, KEY_PLAYER,
EV_KEY, KEY_DVD,
EV_KEY, KEY_AUDIO,
EV_KEY, KEY_VIDEO,
EV_KEY, KEY_MEMO,
EV_KEY, KEY_CALENDAR,
EV_KEY, KEY_RED,
EV_KEY, KEY_GREEN,
EV_KEY, KEY_YELLOW,
EV_KEY, KEY_BLUE,
EV_KEY, KEY_CHANNELUP,
EV_KEY, KEY_CHANNELDOWN,
EV_KEY, KEY_LAST,
EV_KEY, KEY_NEXT,
EV_KEY, KEY_RESTART,
EV_KEY, KEY_SLOW,
EV_KEY, KEY_SHUFFLE,
EV_KEY, KEY_PREVIOUS,
EV_KEY, KEY_VIDEOPHONE,
EV_KEY, KEY_GAMES,
EV_KEY, KEY_ZOOMIN,
EV_KEY, KEY_ZOOMOUT,
EV_KEY, KEY_ZOOMRESET,
EV_KEY, KEY_WORDPROCESSOR,
EV_KEY, KEY_EDITOR,
EV_KEY, KEY_SPREADSHEET,
EV_KEY, KEY_GRAPHICSEDITOR,
EV_KEY, KEY_PRESENTATION,
EV_KEY, KEY_DATABASE,
EV_KEY, KEY_NEWS,
EV_KEY, KEY_VOICEMAIL,
EV_KEY, KEY_ADDRESSBOOK,
EV_KEY, KEY_MESSENGER,
EV_KEY, KEY_DISPLAYTOGGLE,
EV_KEY, KEY_SPELLCHECK,
EV_KEY, KEY_LOGOFF,
EV_KEY, KEY_MEDIA_REPEAT,
EV_KEY, KEY_IMAGES,
EV_KEY, KEY_BUTTONCONFIG,
EV_KEY, KEY_TASKMANAGER,
EV_KEY, KEY_JOURNAL,
EV_KEY, KEY_CONTROLPANEL,
EV_KEY, KEY_APPSELECT,
EV_KEY, KEY_SCREENSAVER,
EV_KEY, KEY_VOICECOMMAND,
EV_KEY, KEY_BRIGHTNESS_MIN,
EV_KEY, KEY_BRIGHTNESS_MAX,
EV_MSC, MSC_SCAN,
-1 , -1,
};
static struct input_absinfo absinfo[] = {
{ ABS_VOLUME, 0, 572, 0, 0, 0 },
{ ABS_MISC, 0, 255, 0, 0, 0 },
{ 0x29, 0, 255, 0, 0, 0 },
{ 0x2a, 0, 255, 0, 0, 0 },
{ 0x2b, 0, 255, 0, 0, 0 },
{ 0x2c, 0, 255, 0, 0, 0 },
{ 0x2d, 0, 255, 0, 0, 0 },
{ 0x2e, 0, 255, 0, 0, 0 },
{ 0x2f, 0, 255, 0, 0, 0 },
{ 0x30, 0, 255, 0, 0, 0 },
{ 0x31, 0, 255, 0, 0, 0 },
{ 0x32, 0, 255, 0, 0, 0 },
{ 0x33, 0, 255, 0, 0, 0 },
{ 0x34, 0, 255, 0, 0, 0 },
{ 0x35, 0, 255, 0, 0, 0 },
{ 0x36, 0, 255, 0, 0, 0 },
{ 0x37, 0, 255, 0, 0, 0 },
{ 0x38, 0, 255, 0, 0, 0 },
{ 0x39, 0, 255, 0, 0, 0 },
{ 0x3a, 0, 255, 0, 0, 0 },
{ 0x3b, 0, 255, 0, 0, 0 },
{ 0x3c, 0, 255, 0, 0, 0 },
{ 0x3d, 0, 255, 0, 0, 0 },
{ 0x3e, 0, 255, 0, 0, 0 },
{ 0x3f, 0, 255, 0, 0, 0 },
{ .value = -1 },
};
struct litest_test_device litest_keyboard_blackwidow_device = {
.type = LITEST_KEYBOARD_BLACKWIDOW,
.features = LITEST_KEYS | LITEST_WHEEL,
.shortname = "blackwidow",
.setup = litest_blackwidow_setup,
.interface = NULL,
.name = "Razer Razer BlackWidow 2013",
.id = &input_id,
.absinfo = absinfo,
.events = events,
};

View file

@ -0,0 +1,64 @@
/*
* Copyright © 2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "litest.h"
#include "litest-int.h"
static void litest_logitech_trackball_setup(void)
{
struct litest_device *d = litest_create_device(LITEST_LOGITECH_TRACKBALL);
litest_set_current_device(d);
}
static struct input_id input_id = {
.bustype = 0x3,
.vendor = 0x46d,
.product = 0xc408,
};
static int events[] = {
EV_KEY, BTN_LEFT,
EV_KEY, BTN_RIGHT,
EV_KEY, BTN_MIDDLE,
EV_KEY, BTN_SIDE,
EV_KEY, BTN_EXTRA,
EV_REL, REL_X,
EV_REL, REL_Y,
-1 , -1,
};
struct litest_test_device litest_logitech_trackball_device = {
.type = LITEST_LOGITECH_TRACKBALL,
.features = LITEST_RELATIVE | LITEST_BUTTON,
.shortname = "logitech trackball",
.setup = litest_logitech_trackball_setup,
.interface = NULL,
.name = "Logitech USB Trackball",
.id = &input_id,
.absinfo = NULL,
.events = events,
};

205
test/litest-mouse-roccat.c Normal file
View file

@ -0,0 +1,205 @@
/*
* Copyright © 2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "litest.h"
#include "litest-int.h"
static void litest_mouse_roccat_setup(void)
{
struct litest_device *d = litest_create_device(LITEST_MOUSE_ROCCAT);
litest_set_current_device(d);
}
static struct input_id input_id = {
.bustype = 0x3,
.vendor = 0x1e7d,
.product = 0x2e22,
};
static int events[] = {
EV_REL, REL_X,
EV_REL, REL_Y,
EV_REL, REL_WHEEL,
EV_REL, REL_HWHEEL,
EV_REL, REL_DIAL,
EV_KEY, KEY_ESC,
EV_KEY, KEY_ENTER,
EV_KEY, KEY_KPMINUS,
EV_KEY, KEY_KPPLUS,
EV_KEY, KEY_UP,
EV_KEY, KEY_LEFT,
EV_KEY, KEY_RIGHT,
EV_KEY, KEY_DOWN,
EV_KEY, KEY_MUTE,
EV_KEY, KEY_VOLUMEDOWN,
EV_KEY, KEY_VOLUMEUP,
EV_KEY, KEY_POWER,
EV_KEY, KEY_PAUSE,
EV_KEY, KEY_STOP,
EV_KEY, KEY_PROPS,
EV_KEY, KEY_UNDO,
EV_KEY, KEY_COPY,
EV_KEY, KEY_OPEN,
EV_KEY, KEY_PASTE,
EV_KEY, KEY_FIND,
EV_KEY, KEY_CUT,
EV_KEY, KEY_HELP,
EV_KEY, KEY_MENU,
EV_KEY, KEY_CALC,
EV_KEY, KEY_SLEEP,
EV_KEY, KEY_FILE,
EV_KEY, KEY_WWW,
EV_KEY, KEY_COFFEE,
EV_KEY, KEY_MAIL,
EV_KEY, KEY_BOOKMARKS,
EV_KEY, KEY_BACK,
EV_KEY, KEY_FORWARD,
EV_KEY, KEY_EJECTCD,
EV_KEY, KEY_NEXTSONG,
EV_KEY, KEY_PLAYPAUSE,
EV_KEY, KEY_PREVIOUSSONG,
EV_KEY, KEY_STOPCD,
EV_KEY, KEY_RECORD,
EV_KEY, KEY_REWIND,
EV_KEY, KEY_PHONE,
EV_KEY, KEY_CONFIG,
EV_KEY, KEY_HOMEPAGE,
EV_KEY, KEY_REFRESH,
EV_KEY, KEY_EXIT,
EV_KEY, KEY_SCROLLUP,
EV_KEY, KEY_SCROLLDOWN,
EV_KEY, KEY_NEW,
EV_KEY, KEY_CLOSE,
EV_KEY, KEY_PLAY,
EV_KEY, KEY_FASTFORWARD,
EV_KEY, KEY_BASSBOOST,
EV_KEY, KEY_PRINT,
EV_KEY, KEY_CAMERA,
EV_KEY, KEY_CHAT,
EV_KEY, KEY_SEARCH,
EV_KEY, KEY_FINANCE,
EV_KEY, KEY_BRIGHTNESSDOWN,
EV_KEY, KEY_BRIGHTNESSUP,
EV_KEY, KEY_KBDILLUMTOGGLE,
EV_KEY, KEY_SAVE,
EV_KEY, KEY_DOCUMENTS,
EV_KEY, KEY_UNKNOWN,
EV_KEY, KEY_VIDEO_NEXT,
EV_KEY, KEY_BRIGHTNESS_AUTO,
EV_KEY, BTN_0,
EV_KEY, BTN_LEFT,
EV_KEY, BTN_RIGHT,
EV_KEY, BTN_MIDDLE,
EV_KEY, BTN_SIDE,
EV_KEY, BTN_EXTRA,
EV_KEY, KEY_SELECT,
EV_KEY, KEY_GOTO,
EV_KEY, KEY_INFO,
EV_KEY, KEY_PROGRAM,
EV_KEY, KEY_PVR,
EV_KEY, KEY_SUBTITLE,
EV_KEY, KEY_ZOOM,
EV_KEY, KEY_KEYBOARD,
EV_KEY, KEY_PC,
EV_KEY, KEY_TV,
EV_KEY, KEY_TV2,
EV_KEY, KEY_VCR,
EV_KEY, KEY_VCR2,
EV_KEY, KEY_SAT,
EV_KEY, KEY_CD,
EV_KEY, KEY_TAPE,
EV_KEY, KEY_TUNER,
EV_KEY, KEY_PLAYER,
EV_KEY, KEY_DVD,
EV_KEY, KEY_AUDIO,
EV_KEY, KEY_VIDEO,
EV_KEY, KEY_MEMO,
EV_KEY, KEY_CALENDAR,
EV_KEY, KEY_RED,
EV_KEY, KEY_GREEN,
EV_KEY, KEY_YELLOW,
EV_KEY, KEY_BLUE,
EV_KEY, KEY_CHANNELUP,
EV_KEY, KEY_CHANNELDOWN,
EV_KEY, KEY_LAST,
EV_KEY, KEY_NEXT,
EV_KEY, KEY_RESTART,
EV_KEY, KEY_SLOW,
EV_KEY, KEY_SHUFFLE,
EV_KEY, KEY_PREVIOUS,
EV_KEY, KEY_VIDEOPHONE,
EV_KEY, KEY_GAMES,
EV_KEY, KEY_ZOOMIN,
EV_KEY, KEY_ZOOMOUT,
EV_KEY, KEY_ZOOMRESET,
EV_KEY, KEY_WORDPROCESSOR,
EV_KEY, KEY_EDITOR,
EV_KEY, KEY_SPREADSHEET,
EV_KEY, KEY_GRAPHICSEDITOR,
EV_KEY, KEY_PRESENTATION,
EV_KEY, KEY_DATABASE,
EV_KEY, KEY_NEWS,
EV_KEY, KEY_VOICEMAIL,
EV_KEY, KEY_ADDRESSBOOK,
EV_KEY, KEY_MESSENGER,
EV_KEY, KEY_DISPLAYTOGGLE,
EV_KEY, KEY_SPELLCHECK,
EV_KEY, KEY_LOGOFF,
EV_KEY, KEY_MEDIA_REPEAT,
EV_KEY, KEY_IMAGES,
EV_KEY, KEY_BUTTONCONFIG,
EV_KEY, KEY_TASKMANAGER,
EV_KEY, KEY_JOURNAL,
EV_KEY, KEY_CONTROLPANEL,
EV_KEY, KEY_APPSELECT,
EV_KEY, KEY_SCREENSAVER,
EV_KEY, KEY_VOICECOMMAND,
EV_KEY, KEY_BRIGHTNESS_MIN,
EV_KEY, KEY_BRIGHTNESS_MAX,
-1 , -1,
};
static struct input_absinfo absinfo[] = {
{ ABS_VOLUME, 0, 572, 0, 0, 0 },
{ ABS_MISC, 0, 0, 0, 0, 0 },
{ ABS_MISC + 1, 0, 0, 0, 0, 0 },
{ ABS_MISC + 2, 0, 0, 0, 0, 0 },
{ ABS_MISC + 3, 0, 0, 0, 0, 0 },
{ .value = -1 }
};
struct litest_test_device litest_mouse_roccat_device = {
.type = LITEST_MOUSE_ROCCAT,
.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL | LITEST_KEYS,
.shortname = "mouse_roccat",
.setup = litest_mouse_roccat_setup,
.interface = NULL,
.name = "ROCCAT ROCCAT Kone XTD",
.id = &input_id,
.absinfo = absinfo,
.events = events,
};

View file

@ -35,6 +35,8 @@ litest_ms_surface_cover_setup(void)
}
static struct input_event down[] = {
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
@ -59,6 +61,8 @@ static struct litest_device_interface interface = {
};
static struct input_absinfo absinfo[] = {
{ ABS_X, 0, 1022, 0, 0, 12 },
{ ABS_Y, 0, 487, 0, 0, 12 },
{ ABS_VOLUME, 0, 1023, 0, 0, 0 },
{ ABS_MISC, 0, 255, 0, 0, 0 },
{ 41, 0, 255, 0, 0, 0 },
@ -90,7 +94,7 @@ static struct input_absinfo absinfo[] = {
static struct input_id input_id = {
.bustype = 0x3,
.vendor = 0x45e,
.product = 0x7a9,
.product = 0x7dc,
};
static int events[] = {
@ -368,18 +372,17 @@ static int events[] = {
EV_LED, LED_NUML,
EV_LED, LED_CAPSL,
EV_LED, LED_SCROLLL,
EV_REP, REP_DELAY,
-1, -1,
};
struct litest_test_device litest_ms_surface_cover_device = {
.type = LITEST_MS_SURFACE_COVER,
.features = LITEST_KEYBOARD | LITEST_RELATIVE | LITEST_FAKE_MT,
.features = LITEST_KEYS | LITEST_ABSOLUTE | LITEST_RELATIVE | LITEST_FAKE_MT | LITEST_BUTTON | LITEST_WHEEL,
.shortname = "MS surface cover",
.setup = litest_ms_surface_cover_setup,
.interface = &interface,
.name = "MICROSOFT SAM",
.name = "Microsoft Surface Type Cover",
.id = &input_id,
.events = events,
.absinfo = absinfo,

369
test/litest-selftest.c Normal file
View file

@ -0,0 +1,369 @@
#include <config.h>
#include <check.h>
#include <signal.h>
#include "litest.h"
START_TEST(litest_assert_trigger)
{
litest_assert(1 == 2);
}
END_TEST
START_TEST(litest_assert_notrigger)
{
litest_assert(1 == 1);
}
END_TEST
START_TEST(litest_assert_msg_trigger)
{
litest_assert_msg(1 == 2, "1 is not 2\n");
}
END_TEST
START_TEST(litest_assert_msg_NULL_trigger)
{
litest_assert_msg(1 == 2, NULL);
}
END_TEST
START_TEST(litest_assert_msg_notrigger)
{
litest_assert_msg(1 == 1, "1 is not 2\n");
litest_assert_msg(1 == 1, NULL);
}
END_TEST
START_TEST(litest_abort_msg_trigger)
{
litest_abort_msg("message\n");
}
END_TEST
START_TEST(litest_abort_msg_NULL_trigger)
{
litest_abort_msg(NULL);
}
END_TEST
START_TEST(litest_int_eq_trigger)
{
int a = 10;
int b = 20;
litest_assert_int_eq(a, b);
}
END_TEST
START_TEST(litest_int_eq_notrigger)
{
int a = 10;
int b = 10;
litest_assert_int_eq(a, b);
}
END_TEST
START_TEST(litest_int_ne_trigger)
{
int a = 10;
int b = 10;
litest_assert_int_ne(a, b);
}
END_TEST
START_TEST(litest_int_ne_notrigger)
{
int a = 10;
int b = 20;
litest_assert_int_ne(a, b);
}
END_TEST
START_TEST(litest_int_lt_trigger_eq)
{
int a = 10;
int b = 10;
litest_assert_int_lt(a, b);
}
END_TEST
START_TEST(litest_int_lt_trigger_gt)
{
int a = 11;
int b = 10;
litest_assert_int_lt(a, b);
}
END_TEST
START_TEST(litest_int_lt_notrigger)
{
int a = 10;
int b = 11;
litest_assert_int_lt(a, b);
}
END_TEST
START_TEST(litest_int_le_trigger)
{
int a = 11;
int b = 10;
litest_assert_int_le(a, b);
}
END_TEST
START_TEST(litest_int_le_notrigger)
{
int a = 10;
int b = 11;
int c = 10;
litest_assert_int_le(a, b);
litest_assert_int_le(a, c);
}
END_TEST
START_TEST(litest_int_gt_trigger_eq)
{
int a = 10;
int b = 10;
litest_assert_int_gt(a, b);
}
END_TEST
START_TEST(litest_int_gt_trigger_lt)
{
int a = 9;
int b = 10;
litest_assert_int_gt(a, b);
}
END_TEST
START_TEST(litest_int_gt_notrigger)
{
int a = 10;
int b = 9;
litest_assert_int_gt(a, b);
}
END_TEST
START_TEST(litest_int_ge_trigger)
{
int a = 9;
int b = 10;
litest_assert_int_ge(a, b);
}
END_TEST
START_TEST(litest_int_ge_notrigger)
{
int a = 10;
int b = 9;
int c = 10;
litest_assert_int_ge(a, b);
litest_assert_int_ge(a, c);
}
END_TEST
START_TEST(litest_ptr_eq_notrigger)
{
int v = 10;
int *a = &v;
int *b = &v;
int c = NULL;
int d = NULL;
litest_assert_ptr_eq(a, b);
litest_assert_ptr_eq(c, d);
}
END_TEST
START_TEST(litest_ptr_eq_trigger)
{
int v = 10;
int v2 = 11;
int *a = &v;
int *b = &v2;
litest_assert_ptr_eq(a, b);
}
END_TEST
START_TEST(litest_ptr_eq_trigger_NULL)
{
int v = 10;
int *a = &v;
int *b = NULL;
litest_assert_ptr_eq(a, b);
}
END_TEST
START_TEST(litest_ptr_eq_trigger_NULL2)
{
int v = 10;
int *a = &v;
int *b = NULL;
litest_assert_ptr_eq(b, a);
}
END_TEST
START_TEST(litest_ptr_ne_trigger)
{
int v = 10;
int *a = &v;
int *b = &v;
litest_assert_ptr_ne(a, b);
}
END_TEST
START_TEST(litest_ptr_ne_trigger_NULL)
{
int *a = NULL;
litest_assert_ptr_ne(a, NULL);
}
END_TEST
START_TEST(litest_ptr_ne_trigger_NULL2)
{
int *a = NULL;
litest_assert_ptr_ne(NULL, a);
}
END_TEST
START_TEST(litest_ptr_ne_notrigger)
{
int v1 = 10;
int v2 = 10;
int *a = &v1;
int *b = &v2;
int *c = NULL;
litest_assert_ptr_ne(a, b);
litest_assert_ptr_ne(a, c);
litest_assert_ptr_ne(c, b);
}
END_TEST
START_TEST(litest_ptr_null_notrigger)
{
int *a = NULL;
litest_assert_ptr_null(a);
litest_assert_ptr_null(NULL);
}
END_TEST
START_TEST(litest_ptr_null_trigger)
{
int v;
int *a = &v;
litest_assert_ptr_null(a);
}
END_TEST
START_TEST(litest_ptr_notnull_notrigger)
{
int v;
int *a = &v;
litest_assert_ptr_notnull(a);
}
END_TEST
START_TEST(litest_ptr_notnull_trigger)
{
int *a = NULL;
litest_assert_ptr_notnull(a);
}
END_TEST
START_TEST(litest_ptr_notnull_trigger_NULL)
{
litest_assert_ptr_notnull(NULL);
}
END_TEST
static Suite *
litest_assert_macros_suite(void)
{
TCase *tc;
Suite *s;
s = suite_create("litest:assert macros");
tc = tcase_create("assert");
tcase_add_test_raise_signal(tc, litest_assert_trigger, SIGABRT);
tcase_add_test(tc, litest_assert_notrigger);
tcase_add_test_raise_signal(tc, litest_assert_msg_trigger, SIGABRT);
tcase_add_test_raise_signal(tc, litest_assert_msg_NULL_trigger, SIGABRT);
tcase_add_test(tc, litest_assert_msg_notrigger);
suite_add_tcase(s, tc);
tc = tcase_create("abort");
tcase_add_test_raise_signal(tc, litest_abort_msg_trigger, SIGABRT);
tcase_add_test_raise_signal(tc, litest_abort_msg_NULL_trigger, SIGABRT);
suite_add_tcase(s, tc);
tc = tcase_create("int comparison ");
tcase_add_test_raise_signal(tc, litest_int_eq_trigger, SIGABRT);
tcase_add_test(tc, litest_int_eq_notrigger);
tcase_add_test_raise_signal(tc, litest_int_ne_trigger, SIGABRT);
tcase_add_test(tc, litest_int_ne_notrigger);
tcase_add_test_raise_signal(tc, litest_int_le_trigger, SIGABRT);
tcase_add_test(tc, litest_int_le_notrigger);
tcase_add_test_raise_signal(tc, litest_int_lt_trigger_gt, SIGABRT);
tcase_add_test_raise_signal(tc, litest_int_lt_trigger_eq, SIGABRT);
tcase_add_test(tc, litest_int_lt_notrigger);
tcase_add_test_raise_signal(tc, litest_int_ge_trigger, SIGABRT);
tcase_add_test(tc, litest_int_ge_notrigger);
tcase_add_test_raise_signal(tc, litest_int_gt_trigger_eq, SIGABRT);
tcase_add_test_raise_signal(tc, litest_int_gt_trigger_lt, SIGABRT);
tcase_add_test(tc, litest_int_gt_notrigger);
suite_add_tcase(s, tc);
tc = tcase_create("pointer comparison ");
tcase_add_test_raise_signal(tc, litest_ptr_eq_trigger, SIGABRT);
tcase_add_test_raise_signal(tc, litest_ptr_eq_trigger_NULL, SIGABRT);
tcase_add_test_raise_signal(tc, litest_ptr_eq_trigger_NULL2, SIGABRT);
tcase_add_test(tc, litest_ptr_eq_notrigger);
tcase_add_test_raise_signal(tc, litest_ptr_ne_trigger, SIGABRT);
tcase_add_test_raise_signal(tc, litest_ptr_ne_trigger_NULL, SIGABRT);
tcase_add_test_raise_signal(tc, litest_ptr_ne_trigger_NULL2, SIGABRT);
tcase_add_test(tc, litest_ptr_ne_notrigger);
tcase_add_test_raise_signal(tc, litest_ptr_null_trigger, SIGABRT);
tcase_add_test(tc, litest_ptr_null_notrigger);
tcase_add_test_raise_signal(tc, litest_ptr_notnull_trigger, SIGABRT);
tcase_add_test_raise_signal(tc, litest_ptr_notnull_trigger_NULL, SIGABRT);
tcase_add_test(tc, litest_ptr_notnull_notrigger);
suite_add_tcase(s, tc);
return s;
}
int
main (int argc, char **argv)
{
int nfailed;
Suite *s;
SRunner *sr;
/* when running under valgrind we're using nofork mode, so a signal
* raised by a test will fail in valgrind. There's nothing to
* memcheck here anyway, so just skip the valgrind test */
if (getenv("USING_VALGRIND"))
return EXIT_SUCCESS;
s = litest_assert_macros_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_ENV);
nfailed = srunner_ntests_failed(sr);
srunner_free(sr);
return (nfailed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

68
test/litest-wheel-only.c Normal file
View file

@ -0,0 +1,68 @@
/*
* Copyright © 2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "litest.h"
#include "litest-int.h"
static void litest_wheel_only_setup(void)
{
struct litest_device *d = litest_create_device(LITEST_WHEEL_ONLY);
litest_set_current_device(d);
}
static struct input_id input_id = {
.bustype = 0x3,
.vendor = 0x1,
.product = 0x2,
};
static int events[] = {
EV_REL, REL_WHEEL,
-1 , -1,
};
static const char udev_rule[] =
"ACTION==\"remove\", GOTO=\"wheel_only_end\"\n"
"KERNEL!=\"event*\", GOTO=\"wheel_only_end\"\n"
"\n"
"ATTRS{name}==\"litest wheel only device*\",\\\n"
" ENV{ID_INPUT_KEY}=\"1\"\n"
"\n"
"LABEL=\"wheel_only_end\"";
struct litest_test_device litest_wheel_only_device = {
.type = LITEST_WHEEL_ONLY,
.features = LITEST_WHEEL,
.shortname = "wheel only",
.setup = litest_wheel_only_setup,
.interface = NULL,
.name = "wheel only device",
.id = &input_id,
.absinfo = NULL,
.events = events,
.udev_rule = udev_rule,
};

File diff suppressed because it is too large Load diff

View file

@ -32,6 +32,83 @@
#include <libevdev/libevdev.h>
#include <libevdev/libevdev-uinput.h>
#include <libinput.h>
#include <math.h>
#define litest_assert(cond) \
do { \
if (!(cond)) \
litest_fail_condition(__FILE__, __LINE__, __func__, \
#cond, NULL); \
} while(0)
#define litest_assert_msg(cond, ...) \
do { \
if (!(cond)) \
litest_fail_condition(__FILE__, __LINE__, __func__, \
#cond, __VA_ARGS__); \
} while(0)
#define litest_abort_msg(...) \
litest_fail_condition(__FILE__, __LINE__, __func__, \
"aborting", __VA_ARGS__); \
#define litest_assert_notnull(cond) \
do { \
if ((cond) == NULL) \
litest_fail_condition(__FILE__, __LINE__, __func__, \
#cond, " expected to be not NULL\n"); \
} while(0)
#define litest_assert_comparison_int_(a_, op_, b_) \
do { \
__typeof__(a_) _a = a_; \
__typeof__(b_) _b = b_; \
if (trunc(_a) != _a || trunc(_b) != _b) \
litest_abort_msg("litest_assert_int_* used for non-integer value\n"); \
if (!((_a) op_ (_b))) \
litest_fail_comparison_int(__FILE__, __LINE__, __func__,\
#op_, _a, _b, \
#a_, #b_); \
} while(0)
#define litest_assert_int_eq(a_, b_) \
litest_assert_comparison_int_(a_, ==, b_)
#define litest_assert_int_ne(a_, b_) \
litest_assert_comparison_int_(a_, !=, b_)
#define litest_assert_int_lt(a_, b_) \
litest_assert_comparison_int_(a_, <, b_)
#define litest_assert_int_le(a_, b_) \
litest_assert_comparison_int_(a_, <=, b_)
#define litest_assert_int_ge(a_, b_) \
litest_assert_comparison_int_(a_, >=, b_)
#define litest_assert_int_gt(a_, b_) \
litest_assert_comparison_int_(a_, >, b_)
#define litest_assert_comparison_ptr_(a_, op_, b_) \
do { \
__typeof__(a_) _a = a_; \
__typeof__(b_) _b = b_; \
if (!((_a) op_ (_b))) \
litest_fail_comparison_ptr(__FILE__, __LINE__, __func__,\
#a_ " " #op_ " " #b_); \
} while(0)
#define litest_assert_ptr_eq(a_, b_) \
litest_assert_comparison_ptr_(a_, ==, b_)
#define litest_assert_ptr_ne(a_, b_) \
litest_assert_comparison_ptr_(a_, !=, b_)
#define litest_assert_ptr_null(a_) \
litest_assert_comparison_ptr_(a_, ==, NULL)
#define litest_assert_ptr_notnull(a_) \
litest_assert_comparison_ptr_(a_, !=, NULL)
enum litest_device_type {
LITEST_NO_DEVICE = -1,
@ -53,10 +130,15 @@ enum litest_device_type {
LITEST_SYNAPTICS_TRACKPOINT_BUTTONS = -17,
LITEST_PROTOCOL_A_SCREEN = -18,
LITEST_WACOM_FINGER = -19,
LITEST_WACOM_BAMBOO = -20,
LITEST_WACOM_CINTIQ = -21,
LITEST_WACOM_INTUOS = -22,
LITEST_WACOM_ISDV4 = -23,
LITEST_KEYBOARD_BLACKWIDOW = -20,
LITEST_WHEEL_ONLY = -21,
LITEST_MOUSE_ROCCAT = -22,
LITEST_LOGITECH_TRACKBALL = -23,
LITEST_ATMEL_HOVER = -24,
LITEST_WACOM_BAMBOO = -25,
LITEST_WACOM_CINTIQ = -26,
LITEST_WACOM_INTUOS = -27,
LITEST_WACOM_ISDV4 = -28,
};
enum litest_device_feature {
@ -77,9 +159,10 @@ enum litest_device_feature {
LITEST_FAKE_MT = 1 << 12,
LITEST_ABSOLUTE = 1 << 13,
LITEST_PROTOCOL_A = 1 << 14,
LITEST_TABLET = 1 << 15,
LITEST_DISTANCE = 1 << 16,
LITEST_TOOL_SERIAL = 1 << 17,
LITEST_HOVER = 1 << 15,
LITEST_TABLET = 1 << 16,
LITEST_DISTANCE = 1 << 17,
LITEST_TOOL_SERIAL = 1 << 18,
};
struct litest_device {
@ -103,20 +186,81 @@ struct axis_replacement {
int32_t value;
};
/* A loop range, resolves to:
for (i = lower; i < upper; i++)
*/
struct range {
int lower; /* inclusive */
int upper; /* exclusive */
};
struct libinput *litest_create_context(void);
void litest_disable_log_handler(struct libinput *libinput);
void litest_restore_log_handler(struct libinput *libinput);
void litest_add(const char *name, void *func,
enum litest_device_feature required_feature,
enum litest_device_feature excluded_feature);
void
litest_add_for_device(const char *name,
void *func,
enum litest_device_type type);
void litest_add_no_device(const char *name, void *func);
litest_fail_condition(const char *file,
int line,
const char *func,
const char *condition,
const char *message,
...);
void
litest_fail_comparison_int(const char *file,
int line,
const char *func,
const char *operator,
int a,
int b,
const char *astr,
const char *bstr);
void
litest_fail_comparison_ptr(const char *file,
int line,
const char *func,
const char *comparison);
int litest_run(int argc, char **argv);
#define litest_add(name_, func_, ...) \
_litest_add(name_, #func_, func_, __VA_ARGS__)
#define litest_add_ranged(name_, func_, ...) \
_litest_add_ranged(name_, #func_, func_, __VA_ARGS__)
#define litest_add_for_device(name_, func_, ...) \
_litest_add_for_device(name_, #func_, func_, __VA_ARGS__)
#define litest_add_ranged_for_device(name_, func_, ...) \
_litest_add_ranged_for_device(name_, #func_, func_, __VA_ARGS__)
#define litest_add_no_device(name_, func_) \
_litest_add_no_device(name_, #func_, func_)
#define litest_add_ranged_no_device(name_, func_, ...) \
_litest_add_ranged_no_device(name_, #func_, func_, __VA_ARGS__)
void _litest_add(const char *name,
const char *funcname,
void *func,
enum litest_device_feature required_feature,
enum litest_device_feature excluded_feature);
void _litest_add_ranged(const char *name,
const char *funcname,
void *func,
enum litest_device_feature required,
enum litest_device_feature excluded,
const struct range *range);
void _litest_add_for_device(const char *name,
const char *funcname,
void *func,
enum litest_device_type type);
void _litest_add_ranged_for_device(const char *name,
const char *funcname,
void *func,
enum litest_device_type type,
const struct range *range);
void _litest_add_no_device(const char *name,
const char *funcname,
void *func);
void _litest_add_ranged_no_device(const char *name,
const char *funcname,
void *func,
const struct range *range);
extern void litest_setup_tests(void);
struct litest_device * litest_create_device(enum litest_device_type which);
struct litest_device * litest_add_device(struct libinput *libinput,
enum litest_device_type which);
@ -149,7 +293,8 @@ void litest_event(struct litest_device *t,
int value);
int litest_auto_assign_value(struct litest_device *d,
const struct input_event *ev,
int slot, double x, double y);
int slot, double x, double y,
bool touching);
void litest_touch_up(struct litest_device *d, unsigned int slot);
void litest_touch_move(struct litest_device *d,
unsigned int slot,
@ -178,6 +323,25 @@ void litest_tablet_motion(struct litest_device *d,
int x, int y,
struct axis_replacement *axes);
void litest_hover_start(struct litest_device *d,
unsigned int slot,
double x,
double y);
void litest_hover_end(struct litest_device *d, unsigned int slot);
void litest_hover_move(struct litest_device *d,
unsigned int slot,
double x,
double y);
void litest_hover_move_to(struct litest_device *d,
unsigned int slot,
double x_from, double y_from,
double x_to, double y_to,
int steps, int sleep_ms);
void litest_hover_move_two_touches(struct litest_device *d,
double x0, double y0,
double x1, double y1,
double dx, double dy,
int steps, int sleep_ms);
void litest_button_click(struct litest_device *d,
unsigned int button,
bool is_press);
@ -191,6 +355,23 @@ void litest_wait_for_event(struct libinput *li);
void litest_wait_for_event_of_type(struct libinput *li, ...);
void litest_drain_events(struct libinput *li);
void litest_assert_empty_queue(struct libinput *li);
struct libinput_event_pointer * litest_is_button_event(
struct libinput_event *event,
unsigned int button,
enum libinput_button_state state);
struct libinput_event_pointer * litest_is_axis_event(
struct libinput_event *event,
enum libinput_pointer_axis axis,
enum libinput_pointer_axis_source source);
struct libinput_event_pointer * litest_is_motion_event(
struct libinput_event *event);
struct libinput_event_touch * litest_is_touch_event(
struct libinput_event *event,
enum libinput_event_type type);
struct libinput_event_keyboard * litest_is_keyboard_event(
struct libinput_event *event,
unsigned int key,
enum libinput_key_state state);
void litest_assert_button_event(struct libinput *li,
unsigned int button,
enum libinput_button_state state);
@ -229,10 +410,12 @@ struct libevdev_uinput * litest_create_uinput_abs_device(const char *name,
ck_assert_int_ge((int)((a_) * 256), (int)((b_) * 256))
void litest_timeout_tap(void);
void litest_timeout_tapndrag(void);
void litest_timeout_softbuttons(void);
void litest_timeout_buttonscroll(void);
void litest_timeout_edgescroll(void);
void litest_timeout_finger_switch(void);
void litest_timeout_middlebutton(void);
void litest_push_event_frame(struct litest_device *dev);
void litest_pop_event_frame(struct litest_device *dev);

View file

@ -26,7 +26,6 @@
#include <errno.h>
#include <fcntl.h>
#include <libinput.h>
#include <libudev.h>
#include <unistd.h>
#include "litest.h"
@ -58,8 +57,8 @@ simple_log_handler(struct libinput *libinput,
{
log_handler_called++;
if (log_handler_context)
ck_assert(libinput == log_handler_context);
ck_assert(format != NULL);
litest_assert_ptr_eq(libinput, log_handler_context);
litest_assert_notnull(format);
}
START_TEST(log_default_priority)
@ -140,11 +139,11 @@ START_TEST(log_priority)
}
END_TEST
int main (int argc, char **argv) {
void
litest_setup_tests(void)
{
litest_add_no_device("log:defaults", log_default_priority);
litest_add_no_device("log:logging", log_handler_invoked);
litest_add_no_device("log:logging", log_handler_NULL);
litest_add_no_device("log:logging", log_priority);
return litest_run(argc, argv);
}

View file

@ -65,7 +65,7 @@ create_simple_test_device(const char *name, ...)
};
evdev = libevdev_new();
ck_assert(evdev != NULL);
litest_assert_notnull(evdev);
libevdev_set_name(evdev, name);
va_start(args, name);
@ -83,7 +83,7 @@ create_simple_test_device(const char *name, ...)
rc = libevdev_uinput_create_from_device(evdev,
LIBEVDEV_UINPUT_OPEN_MANAGED,
&uinput);
ck_assert_int_eq(rc, 0);
litest_assert_int_eq(rc, 0);
libevdev_free(evdev);
return uinput;
@ -595,7 +595,35 @@ START_TEST(wheel_click_parser)
}
END_TEST
int main (int argc, char **argv) {
struct parser_test_float {
char *tag;
double expected_value;
};
START_TEST(trackpoint_accel_parser)
{
struct parser_test_float tests[] = {
{ "0.5", 0.5 },
{ "1.0", 1.0 },
{ "2.0", 2.0 },
{ "fail1.0", 0.0 },
{ "1.0fail", 0.0 },
{ "0,5", 0.0 },
{ NULL, 0.0 }
};
int i;
double accel;
for (i = 0; tests[i].tag != NULL; i++) {
accel = parse_trackpoint_accel_property(tests[i].tag);
ck_assert(accel == tests[i].expected_value);
}
}
END_TEST
void
litest_setup_tests(void)
{
litest_add_no_device("events:conversion", event_conversion_device_notify);
litest_add_for_device("events:conversion", event_conversion_pointer, LITEST_MOUSE);
litest_add_for_device("events:conversion", event_conversion_pointer, LITEST_MOUSE);
@ -611,6 +639,5 @@ int main (int argc, char **argv) {
litest_add_no_device("misc:ratelimit", ratelimit_helpers);
litest_add_no_device("misc:dpi parser", dpi_parser);
litest_add_no_device("misc:wheel click parser", wheel_click_parser);
return litest_run(argc, argv);
litest_add_no_device("misc:trackpoint accel parser", trackpoint_accel_parser);
}

View file

@ -26,7 +26,6 @@
#include <errno.h>
#include <fcntl.h>
#include <libinput.h>
#include <libudev.h>
#include <unistd.h>
#include "litest.h"
@ -875,8 +874,8 @@ START_TEST(path_seat_recycle)
}
END_TEST
int
main(int argc, char **argv)
void
litest_setup_tests(void)
{
litest_add_no_device("path:create", path_create_NULL);
litest_add_no_device("path:create", path_create_invalid);
@ -897,6 +896,4 @@ main(int argc, char **argv)
litest_add_for_device("path:device events", path_remove_device, LITEST_SYNAPTICS_CLICKPAD);
litest_add_for_device("path:device events", path_double_remove_device, LITEST_SYNAPTICS_CLICKPAD);
litest_add_no_device("path:seat", path_seat_recycle);
return litest_run(argc, argv);
}

View file

@ -42,12 +42,7 @@ get_accelerated_motion_event(struct libinput *li)
while (1) {
event = libinput_get_event(li);
ck_assert_notnull(event);
ck_assert_int_eq(libinput_event_get_type(event),
LIBINPUT_EVENT_POINTER_MOTION);
ptrev = libinput_event_get_pointer_event(event);
ck_assert_notnull(ptrev);
ptrev = litest_is_motion_event(event);
if (fabs(libinput_event_pointer_get_dx(ptrev)) < DBL_MIN &&
fabs(libinput_event_pointer_get_dy(ptrev)) < DBL_MIN) {
@ -58,7 +53,7 @@ get_accelerated_motion_event(struct libinput *li)
return ptrev;
}
ck_abort_msg("No accelerated pointer motion event found");
litest_abort_msg("No accelerated pointer motion event found");
return NULL;
}
@ -95,17 +90,31 @@ test_relative_event(struct litest_device *dev, int dx, int dy)
actual_dir = atan2(ev_dx, ev_dy);
/* Check the length of the motion vector (tolerate 1.0 indifference). */
ck_assert(fabs(expected_length) >= actual_length);
litest_assert(fabs(expected_length) >= actual_length);
/* Check the direction of the motion vector (tolerate 2π/4 radians
* indifference). */
ck_assert(fabs(expected_dir - actual_dir) < M_PI_2);
litest_assert(fabs(expected_dir - actual_dir) < M_PI_2);
libinput_event_destroy(libinput_event_pointer_get_base_event(ptrev));
litest_drain_events(dev->libinput);
}
static void
disable_button_scrolling(struct litest_device *device)
{
struct libinput_device *dev = device->libinput_device;
enum libinput_config_status status,
expected;
status = libinput_device_config_scroll_set_method(dev,
LIBINPUT_CONFIG_SCROLL_NO_SCROLL);
expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
litest_assert_int_eq(status, expected);
}
START_TEST(pointer_motion_relative)
{
struct litest_device *dev = litest_current_device();
@ -131,21 +140,22 @@ test_absolute_event(struct litest_device *dev, double x, double y)
struct libinput_event *event;
struct libinput_event_pointer *ptrev;
double ex, ey;
enum libinput_event_type type = LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE;
litest_touch_down(dev, 0, x, y);
libinput_dispatch(li);
event = libinput_get_event(li);
ck_assert_int_eq(libinput_event_get_type(event),
LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE);
litest_assert_notnull(event);
litest_assert_int_eq(libinput_event_get_type(event), type);
ptrev = libinput_event_get_pointer_event(event);
ck_assert(ptrev != NULL);
litest_assert(ptrev != NULL);
ex = libinput_event_pointer_get_absolute_x_transformed(ptrev, 100);
ey = libinput_event_pointer_get_absolute_y_transformed(ptrev, 100);
ck_assert_int_eq(ex + 0.5, x);
ck_assert_int_eq(ey + 0.5, y);
litest_assert_int_eq((int)(ex + 0.5), (int)x);
litest_assert_int_eq((int)(ey + 0.5), (int)y);
libinput_event_destroy(event);
}
@ -162,6 +172,61 @@ START_TEST(pointer_motion_absolute)
}
END_TEST
START_TEST(pointer_absolute_initial_state)
{
struct litest_device *dev = litest_current_device();
struct libinput *libinput1, *libinput2;
struct libinput_event *ev1, *ev2;
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);
/* device is now on some x/y value */
litest_drain_events(libinput1);
libinput2 = litest_create_context();
libinput_path_add_device(libinput2,
libevdev_uinput_get_devnode(dev->uinput));
litest_drain_events(libinput2);
if (axis == ABS_X)
litest_touch_down(dev, 0, 40, 70);
else
litest_touch_down(dev, 0, 70, 60);
litest_touch_up(dev, 0);
litest_wait_for_event(libinput1);
litest_wait_for_event(libinput2);
while (libinput_next_event_type(libinput1)) {
ev1 = libinput_get_event(libinput1);
ev2 = libinput_get_event(libinput2);
ck_assert_int_eq(libinput_event_get_type(ev1),
LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE);
ck_assert_int_eq(libinput_event_get_type(ev1),
libinput_event_get_type(ev2));
p1 = libinput_event_get_pointer_event(ev1);
p2 = libinput_event_get_pointer_event(ev2);
ck_assert_int_eq(libinput_event_pointer_get_absolute_x(p1),
libinput_event_pointer_get_absolute_x(p2));
ck_assert_int_eq(libinput_event_pointer_get_absolute_y(p1),
libinput_event_pointer_get_absolute_y(p2));
libinput_event_destroy(ev1);
libinput_event_destroy(ev2);
}
libinput_unref(libinput2);
}
END_TEST
static void
test_unaccel_event(struct litest_device *dev, int dx, int dy)
{
@ -177,18 +242,13 @@ test_unaccel_event(struct litest_device *dev, int dx, int dy)
libinput_dispatch(li);
event = libinput_get_event(li);
ck_assert_notnull(event);
ck_assert_int_eq(libinput_event_get_type(event),
LIBINPUT_EVENT_POINTER_MOTION);
ptrev = libinput_event_get_pointer_event(event);
ck_assert(ptrev != NULL);
ptrev = litest_is_motion_event(event);
ev_dx = libinput_event_pointer_get_dx_unaccelerated(ptrev);
ev_dy = libinput_event_pointer_get_dy_unaccelerated(ptrev);
ck_assert_int_eq(dx, ev_dx);
ck_assert_int_eq(dy, ev_dy);
litest_assert_int_eq(dx, ev_dx);
litest_assert_int_eq(dy, ev_dy);
libinput_event_destroy(event);
@ -230,6 +290,8 @@ START_TEST(pointer_button)
{
struct litest_device *dev = litest_current_device();
disable_button_scrolling(dev);
litest_drain_events(dev->libinput);
test_button_event(dev, BTN_LEFT, 1);
@ -245,8 +307,7 @@ START_TEST(pointer_button)
}
/* Skip middle button test on trackpoints (used for scrolling) */
if (!libevdev_has_property(dev->evdev, INPUT_PROP_POINTING_STICK) &&
libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) {
if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) {
test_button_event(dev, BTN_MIDDLE, 1);
test_button_event(dev, BTN_MIDDLE, 0);
}
@ -369,24 +430,18 @@ test_wheel_event(struct litest_device *dev, int which, int amount)
libinput_dispatch(li);
event = libinput_get_event(li);
ck_assert(event != NULL);
ck_assert_int_eq(libinput_event_get_type(event),
LIBINPUT_EVENT_POINTER_AXIS);
ptrev = libinput_event_get_pointer_event(event);
ck_assert(ptrev != NULL);
axis = (which == REL_WHEEL) ?
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL :
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
event = libinput_get_event(li);
ptrev = litest_is_axis_event(event,
axis,
LIBINPUT_POINTER_AXIS_SOURCE_WHEEL);
ck_assert_int_eq(libinput_event_pointer_get_axis_value(ptrev, axis),
litest_assert_int_eq(libinput_event_pointer_get_axis_value(ptrev, axis),
expected);
ck_assert_int_eq(libinput_event_pointer_get_axis_source(ptrev),
LIBINPUT_POINTER_AXIS_SOURCE_WHEEL);
ck_assert_int_eq(libinput_event_pointer_get_axis_value_discrete(ptrev, axis),
discrete);
litest_assert_int_eq(libinput_event_pointer_get_axis_value_discrete(ptrev, axis),
discrete);
libinput_event_destroy(event);
}
@ -396,11 +451,17 @@ START_TEST(pointer_scroll_wheel)
litest_drain_events(dev->libinput);
test_wheel_event(dev, REL_WHEEL, -1);
test_wheel_event(dev, REL_WHEEL, 1);
/* make sure we hit at least one of the below two conditions */
ck_assert(libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL) ||
libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL));
test_wheel_event(dev, REL_WHEEL, -5);
test_wheel_event(dev, REL_WHEEL, 6);
if (libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL)) {
test_wheel_event(dev, REL_WHEEL, -1);
test_wheel_event(dev, REL_WHEEL, 1);
test_wheel_event(dev, REL_WHEEL, -5);
test_wheel_event(dev, REL_WHEEL, 6);
}
if (libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL)) {
test_wheel_event(dev, REL_HWHEEL, -1);
@ -446,11 +507,17 @@ START_TEST(pointer_scroll_natural_wheel)
libinput_device_config_scroll_set_natural_scroll_enabled(device, 1);
test_wheel_event(dev, REL_WHEEL, -1);
test_wheel_event(dev, REL_WHEEL, 1);
/* make sure we hit at least one of the below two conditions */
ck_assert(libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL) ||
libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL));
test_wheel_event(dev, REL_WHEEL, -5);
test_wheel_event(dev, REL_WHEEL, 6);
if (libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL)) {
test_wheel_event(dev, REL_WHEEL, -1);
test_wheel_event(dev, REL_WHEEL, 1);
test_wheel_event(dev, REL_WHEEL, -5);
test_wheel_event(dev, REL_WHEEL, 6);
}
if (libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL)) {
test_wheel_event(dev, REL_HWHEEL, -1);
@ -732,6 +799,26 @@ START_TEST(pointer_scroll_button)
}
END_TEST
START_TEST(pointer_scroll_nowheel_defaults)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
enum libinput_config_scroll_method method;
uint32_t button;
method = libinput_device_config_scroll_get_method(device);
ck_assert_int_eq(method, LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
method = libinput_device_config_scroll_get_default_method(device);
ck_assert_int_eq(method, LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
button = libinput_device_config_scroll_get_button(device);
ck_assert_int_eq(button, BTN_MIDDLE);
button = libinput_device_config_scroll_get_default_button(device);
ck_assert_int_eq(button, BTN_MIDDLE);
}
END_TEST
START_TEST(pointer_accel_defaults)
{
struct litest_device *dev = litest_current_device();
@ -812,6 +899,17 @@ START_TEST(pointer_accel_defaults_absolute)
}
END_TEST
START_TEST(pointer_accel_defaults_absolute_relative)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
ck_assert(libinput_device_config_accel_is_available(device));
ck_assert(libinput_device_config_accel_get_default_speed(device) == 0.0);
ck_assert(libinput_device_config_accel_get_speed(device) == 0.0);
}
END_TEST
START_TEST(pointer_accel_direction_change)
{
struct litest_device *dev = litest_current_device();
@ -854,19 +952,389 @@ START_TEST(pointer_accel_direction_change)
}
END_TEST
int main (int argc, char **argv) {
START_TEST(middlebutton)
{
struct litest_device *device = litest_current_device();
struct libinput *li = device->libinput;
enum libinput_config_status status;
unsigned int i;
const int btn[][4] = {
{ BTN_LEFT, BTN_RIGHT, BTN_LEFT, BTN_RIGHT },
{ BTN_LEFT, BTN_RIGHT, BTN_RIGHT, BTN_LEFT },
{ BTN_RIGHT, BTN_LEFT, BTN_LEFT, BTN_RIGHT },
{ BTN_RIGHT, BTN_LEFT, BTN_RIGHT, BTN_LEFT },
};
disable_button_scrolling(device);
status = libinput_device_config_middle_emulation_set_enabled(
device->libinput_device,
LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
return;
litest_drain_events(li);
for (i = 0; i < ARRAY_LENGTH(btn); i++) {
litest_button_click(device, btn[i][0], true);
litest_button_click(device, btn[i][1], true);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_assert_empty_queue(li);
litest_button_click(device, btn[i][2], false);
litest_button_click(device, btn[i][3], false);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
litest_assert_empty_queue(li);
}
}
END_TEST
START_TEST(middlebutton_timeout)
{
struct litest_device *device = litest_current_device();
struct libinput *li = device->libinput;
enum libinput_config_status status;
unsigned int button;
disable_button_scrolling(device);
status = libinput_device_config_middle_emulation_set_enabled(
device->libinput_device,
LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
return;
for (button = BTN_LEFT; button <= BTN_RIGHT; button++) {
litest_drain_events(li);
litest_button_click(device, button, true);
litest_assert_empty_queue(li);
litest_timeout_middlebutton();
litest_assert_button_event(li,
button,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_button_click(device, button, false);
litest_assert_button_event(li,
button,
LIBINPUT_BUTTON_STATE_RELEASED);
litest_assert_empty_queue(li);
}
}
END_TEST
START_TEST(middlebutton_doubleclick)
{
struct litest_device *device = litest_current_device();
struct libinput *li = device->libinput;
enum libinput_config_status status;
unsigned int i;
const int btn[][4] = {
{ BTN_LEFT, BTN_RIGHT, BTN_LEFT, BTN_RIGHT },
{ BTN_LEFT, BTN_RIGHT, BTN_RIGHT, BTN_LEFT },
{ BTN_RIGHT, BTN_LEFT, BTN_LEFT, BTN_RIGHT },
{ BTN_RIGHT, BTN_LEFT, BTN_RIGHT, BTN_LEFT },
};
disable_button_scrolling(device);
status = libinput_device_config_middle_emulation_set_enabled(
device->libinput_device,
LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
return;
litest_drain_events(li);
for (i = 0; i < ARRAY_LENGTH(btn); i++) {
litest_button_click(device, btn[i][0], true);
litest_button_click(device, btn[i][1], true);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_assert_empty_queue(li);
litest_button_click(device, btn[i][2], false);
litest_button_click(device, btn[i][2], true);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_button_click(device, btn[i][3], false);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
litest_assert_empty_queue(li);
}
}
END_TEST
START_TEST(middlebutton_middleclick)
{
struct litest_device *device = litest_current_device();
struct libinput *li = device->libinput;
enum libinput_config_status status;
unsigned int button;
disable_button_scrolling(device);
if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE))
return;
status = libinput_device_config_middle_emulation_set_enabled(
device->libinput_device,
LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
return;
/* one button down, then middle -> release buttons */
for (button = BTN_LEFT; button <= BTN_RIGHT; button++) {
/* release button before middle */
litest_drain_events(li);
litest_button_click(device, button, true);
litest_button_click(device, BTN_MIDDLE, true);
litest_assert_button_event(li,
button,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_assert_empty_queue(li);
litest_button_click(device, button, false);
litest_assert_button_event(li,
button,
LIBINPUT_BUTTON_STATE_RELEASED);
litest_button_click(device, BTN_MIDDLE, false);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
litest_assert_empty_queue(li);
/* release middle before button */
litest_button_click(device, button, true);
litest_button_click(device, BTN_MIDDLE, true);
litest_assert_button_event(li,
button,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_assert_empty_queue(li);
litest_button_click(device, BTN_MIDDLE, false);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
litest_button_click(device, button, false);
litest_assert_button_event(li,
button,
LIBINPUT_BUTTON_STATE_RELEASED);
litest_assert_empty_queue(li);
}
}
END_TEST
START_TEST(middlebutton_middleclick_during)
{
struct litest_device *device = litest_current_device();
struct libinput *li = device->libinput;
enum libinput_config_status status;
unsigned int button;
disable_button_scrolling(device);
if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE))
return;
status = libinput_device_config_middle_emulation_set_enabled(
device->libinput_device,
LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
return;
litest_drain_events(li);
/* trigger emulation, then real middle */
for (button = BTN_LEFT; button <= BTN_RIGHT; button++) {
litest_button_click(device, BTN_LEFT, true);
litest_button_click(device, BTN_RIGHT, true);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_button_click(device, BTN_MIDDLE, true);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_assert_empty_queue(li);
/* middle still down, release left/right */
litest_button_click(device, button, false);
litest_assert_empty_queue(li);
litest_button_click(device, button, true);
litest_assert_button_event(li,
button,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_assert_empty_queue(li);
/* release both */
litest_button_click(device, BTN_LEFT, false);
litest_button_click(device, BTN_RIGHT, false);
litest_assert_button_event(li,
button,
LIBINPUT_BUTTON_STATE_RELEASED);
litest_assert_empty_queue(li);
litest_button_click(device, BTN_MIDDLE, false);
litest_assert_button_event(li,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
litest_assert_empty_queue(li);
}
}
END_TEST
START_TEST(middlebutton_default_enabled)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
enum libinput_config_status status;
int available;
enum libinput_config_middle_emulation_state deflt, state;
available = libinput_device_config_middle_emulation_is_available(device);
ck_assert(available);
if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE))
deflt = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
else
deflt = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED;
state = libinput_device_config_middle_emulation_get_enabled(device);
ck_assert_int_eq(state, deflt);
state = libinput_device_config_middle_emulation_get_default_enabled(
device);
ck_assert_int_eq(state, deflt);
status = libinput_device_config_middle_emulation_set_enabled(device,
LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
status = libinput_device_config_middle_emulation_set_enabled(device,
LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
status = libinput_device_config_middle_emulation_set_enabled(device, 3);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
}
END_TEST
START_TEST(middlebutton_default_clickpad)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
enum libinput_config_status status;
enum libinput_config_middle_emulation_state state;
int available;
available = libinput_device_config_middle_emulation_is_available(device);
ck_assert(!available);
state = libinput_device_config_middle_emulation_get_enabled(device);
ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
state = libinput_device_config_middle_emulation_get_default_enabled(
device);
ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
status = libinput_device_config_middle_emulation_set_enabled(device,
LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
status = libinput_device_config_middle_emulation_set_enabled(device,
LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
status = libinput_device_config_middle_emulation_set_enabled(device, 3);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
}
END_TEST
START_TEST(middlebutton_default_touchpad)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
enum libinput_config_middle_emulation_state state;
int available;
available = libinput_device_config_middle_emulation_is_available(device);
ck_assert(!available);
if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE))
return;
state = libinput_device_config_middle_emulation_get_enabled(
device);
ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
state = libinput_device_config_middle_emulation_get_default_enabled(
device);
ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
}
END_TEST
START_TEST(middlebutton_default_disabled)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
enum libinput_config_middle_emulation_state state;
enum libinput_config_status status;
int available;
available = libinput_device_config_middle_emulation_is_available(device);
ck_assert(!available);
state = libinput_device_config_middle_emulation_get_enabled(device);
ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
state = libinput_device_config_middle_emulation_get_default_enabled(
device);
ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
status = libinput_device_config_middle_emulation_set_enabled(device,
LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
status = libinput_device_config_middle_emulation_set_enabled(device,
LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
}
END_TEST
void
litest_setup_tests(void)
{
struct range axis_range = {ABS_X, ABS_Y + 1};
litest_add("pointer:motion", pointer_motion_relative, LITEST_RELATIVE, LITEST_ANY);
litest_add("pointer:motion", pointer_motion_absolute, LITEST_ABSOLUTE, LITEST_ANY);
litest_add("pointer:motion", pointer_motion_unaccel, LITEST_RELATIVE, LITEST_ANY);
litest_add("pointer:button", pointer_button, LITEST_BUTTON, LITEST_CLICKPAD);
litest_add_no_device("pointer:button_auto_release", pointer_button_auto_release);
litest_add_no_device("pointer:button", pointer_button_auto_release);
litest_add_no_device("pointer:button", pointer_seat_button_count);
litest_add("pointer:scroll", pointer_scroll_wheel, LITEST_WHEEL, LITEST_ANY);
litest_add("pointer:scroll", pointer_scroll_button, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY);
litest_add("pointer:scroll", pointer_scroll_nowheel_defaults, LITEST_RELATIVE|LITEST_BUTTON, LITEST_WHEEL);
litest_add("pointer:scroll", pointer_scroll_natural_defaults, LITEST_WHEEL, LITEST_ANY);
litest_add("pointer:scroll", pointer_scroll_natural_enable_config, LITEST_WHEEL, LITEST_ANY);
litest_add("pointer:scroll", pointer_scroll_natural_wheel, LITEST_WHEEL, LITEST_ANY);
litest_add_no_device("pointer:seat button count", pointer_seat_button_count);
litest_add("pointer:calibration", pointer_no_calibration, LITEST_ANY, LITEST_TOUCH|LITEST_SINGLE_TOUCH|LITEST_ABSOLUTE|LITEST_PROTOCOL_A);
@ -878,8 +1346,19 @@ int main (int argc, char **argv) {
litest_add("pointer:accel", pointer_accel_defaults, LITEST_RELATIVE, LITEST_ANY);
litest_add("pointer:accel", pointer_accel_invalid, LITEST_RELATIVE, LITEST_ANY);
litest_add("pointer:accel", pointer_accel_defaults_absolute, LITEST_ABSOLUTE, LITEST_ANY);
litest_add("pointer:accel", pointer_accel_defaults_absolute, LITEST_ABSOLUTE, LITEST_RELATIVE);
litest_add("pointer:accel", pointer_accel_defaults_absolute_relative, LITEST_ABSOLUTE|LITEST_RELATIVE, LITEST_ANY);
litest_add("pointer:accel", pointer_accel_direction_change, LITEST_RELATIVE, LITEST_ANY);
return litest_run(argc, argv);
litest_add("pointer:middlebutton", middlebutton, LITEST_BUTTON, LITEST_ANY);
litest_add("pointer:middlebutton", middlebutton_timeout, LITEST_BUTTON, LITEST_ANY);
litest_add("pointer:middlebutton", middlebutton_doubleclick, LITEST_BUTTON, LITEST_ANY);
litest_add("pointer:middlebutton", middlebutton_middleclick, LITEST_BUTTON, LITEST_ANY);
litest_add("pointer:middlebutton", middlebutton_middleclick_during, LITEST_BUTTON, LITEST_ANY);
litest_add("pointer:middlebutton", middlebutton_default_enabled, LITEST_BUTTON, LITEST_TOUCHPAD|LITEST_POINTINGSTICK);
litest_add("pointer:middlebutton", middlebutton_default_clickpad, LITEST_CLICKPAD, LITEST_ANY);
litest_add("pointer:middlebutton", middlebutton_default_touchpad, LITEST_TOUCHPAD, LITEST_CLICKPAD);
litest_add("pointer:middlebutton", middlebutton_default_disabled, LITEST_ANY, LITEST_BUTTON);
litest_add_ranged("pointer:state", pointer_absolute_initial_state, LITEST_ABSOLUTE, LITEST_ANY, &axis_range);
}

View file

@ -1662,8 +1662,8 @@ START_TEST(artpen_rotation)
}
END_TEST
int
main(int argc, char **argv)
void
litest_setup_tests(void)
{
litest_add("tablet:tool", tool_ref, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY);
litest_add_no_device("tablet:tool", tool_capabilities);
@ -1692,6 +1692,4 @@ main(int argc, char **argv)
litest_add("tablet:airbrush", airbrush_wheel, LITEST_TABLET, LITEST_ANY);
litest_add("tablet:artpen", artpen_tool, LITEST_TABLET, LITEST_ANY);
litest_add("tablet:artpen", artpen_rotation, LITEST_TABLET, LITEST_ANY);
return litest_run(argc, argv);
}

View file

@ -182,6 +182,9 @@ START_TEST(touch_double_touch_down_up)
dev = litest_current_device();
libinput = dev->libinput;
/* note: this test is a false negative, libevdev will filter
* tracking IDs re-used in the same slot. */
litest_touch_down(dev, 0, 0, 0);
litest_touch_down(dev, 0, 0, 0);
litest_touch_up(dev, 0);
@ -241,9 +244,7 @@ START_TEST(touch_calibration_scale)
litest_wait_for_event(li);
ev = libinput_get_event(li);
ck_assert_int_eq(libinput_event_get_type(ev),
LIBINPUT_EVENT_TOUCH_DOWN);
tev = libinput_event_get_touch_event(ev);
tev = litest_is_touch_event(ev, LIBINPUT_EVENT_TOUCH_DOWN);
x = libinput_event_touch_get_x_transformed(tev, width);
y = libinput_event_touch_get_y_transformed(tev, height);
@ -312,9 +313,7 @@ START_TEST(touch_calibration_rotation)
litest_touch_up(dev, 0);
litest_wait_for_event(li);
ev = libinput_get_event(li);
ck_assert_int_eq(libinput_event_get_type(ev),
LIBINPUT_EVENT_TOUCH_DOWN);
tev = libinput_event_get_touch_event(ev);
tev = litest_is_touch_event(ev, LIBINPUT_EVENT_TOUCH_DOWN);
x = libinput_event_touch_get_x_transformed(tev, width);
y = libinput_event_touch_get_y_transformed(tev, height);
@ -378,9 +377,7 @@ START_TEST(touch_calibration_translation)
litest_wait_for_event(li);
ev = libinput_get_event(li);
ck_assert_int_eq(libinput_event_get_type(ev),
LIBINPUT_EVENT_TOUCH_DOWN);
tev = libinput_event_get_touch_event(ev);
tev = litest_is_touch_event(ev, LIBINPUT_EVENT_TOUCH_DOWN);
x = libinput_event_touch_get_x_transformed(tev, width);
y = libinput_event_touch_get_y_transformed(tev, height);
@ -439,6 +436,8 @@ START_TEST(fake_mt_exists)
* have different capabilities */
ck_assert(libinput_device_has_capability(device,
LIBINPUT_DEVICE_CAP_POINTER));
libinput_event_destroy(event);
}
END_TEST
@ -460,7 +459,8 @@ START_TEST(fake_mt_no_touch_events)
litest_touch_up(dev, 0);
litest_touch_up(dev, 1);
litest_assert_empty_queue(li);
litest_assert_only_typed_events(li,
LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE);
}
END_TEST
@ -493,7 +493,7 @@ START_TEST(touch_protocol_a_touch)
litest_wait_for_event_of_type(li, LIBINPUT_EVENT_TOUCH_DOWN, -1);
ev = libinput_get_event(li);
tev = libinput_event_get_touch_event(ev);
tev = litest_is_touch_event(ev, LIBINPUT_EVENT_TOUCH_DOWN);
oldx = libinput_event_touch_get_x(tev);
oldy = libinput_event_touch_get_y(tev);
@ -582,9 +582,79 @@ START_TEST(touch_protocol_a_2fg_touch)
}
END_TEST
int
main(int argc, char **argv)
START_TEST(touch_initial_state)
{
struct litest_device *dev;
struct libinput *libinput1, *libinput2;
struct libinput_event *ev1, *ev2;
struct libinput_event_touch *t1, *t2;
struct libinput_device *device1, *device2;
int axis = _i; /* looped test */
dev = litest_current_device();
device1 = dev->libinput_device;
libinput_device_config_tap_set_enabled(device1,
LIBINPUT_CONFIG_TAP_DISABLED);
libinput1 = dev->libinput;
litest_touch_down(dev, 0, 40, 60);
litest_touch_up(dev, 0);
/* device is now on some x/y value */
litest_drain_events(libinput1);
libinput2 = litest_create_context();
device2 = libinput_path_add_device(libinput2,
libevdev_uinput_get_devnode(
dev->uinput));
libinput_device_config_tap_set_enabled(device2,
LIBINPUT_CONFIG_TAP_DISABLED);
litest_drain_events(libinput2);
if (axis == ABS_X)
litest_touch_down(dev, 0, 40, 70);
else
litest_touch_down(dev, 0, 70, 60);
litest_touch_up(dev, 0);
litest_wait_for_event(libinput1);
litest_wait_for_event(libinput2);
while (libinput_next_event_type(libinput1)) {
ev1 = libinput_get_event(libinput1);
ev2 = libinput_get_event(libinput2);
t1 = litest_is_touch_event(ev1, 0);
t2 = litest_is_touch_event(ev2, 0);
ck_assert_int_eq(libinput_event_get_type(ev1),
libinput_event_get_type(ev2));
if (libinput_event_get_type(ev1) == LIBINPUT_EVENT_TOUCH_UP ||
libinput_event_get_type(ev1) == LIBINPUT_EVENT_TOUCH_FRAME)
break;
ck_assert_int_eq(libinput_event_touch_get_x(t1),
libinput_event_touch_get_x(t2));
ck_assert_int_eq(libinput_event_touch_get_y(t1),
libinput_event_touch_get_y(t2));
libinput_event_destroy(ev1);
libinput_event_destroy(ev2);
}
libinput_event_destroy(ev1);
libinput_event_destroy(ev2);
libinput_unref(libinput2);
}
END_TEST
void
litest_setup_tests(void)
{
struct range axes = { ABS_X, ABS_Y + 1};
litest_add("touch:frame", touch_frame_events, LITEST_TOUCH, LITEST_ANY);
litest_add_no_device("touch:abs-transform", touch_abs_transform);
litest_add_no_device("touch:many-slots", touch_many_slots);
@ -605,5 +675,5 @@ main(int argc, char **argv)
litest_add("touch:protocol a", touch_protocol_a_touch, LITEST_PROTOCOL_A, LITEST_ANY);
litest_add("touch:protocol a", touch_protocol_a_2fg_touch, LITEST_PROTOCOL_A, LITEST_ANY);
return litest_run(argc, argv);
litest_add_ranged("touch:state", touch_initial_state, LITEST_TOUCH, LITEST_PROTOCOL_A, &axes);
}

File diff suppressed because it is too large Load diff

View file

@ -130,12 +130,11 @@ START_TEST(trackpoint_scroll_source)
}
END_TEST
int main(int argc, char **argv) {
void
litest_setup_tests(void)
{
litest_add("trackpoint:middlebutton", trackpoint_middlebutton, LITEST_POINTINGSTICK, LITEST_ANY);
litest_add("trackpoint:middlebutton", trackpoint_middlebutton_noscroll, LITEST_POINTINGSTICK, LITEST_ANY);
litest_add("trackpoint:scroll", trackpoint_scroll, LITEST_POINTINGSTICK, LITEST_ANY);
litest_add("trackpoint:scroll", trackpoint_scroll_source, LITEST_POINTINGSTICK, LITEST_ANY);
return litest_run(argc, argv);
}

View file

@ -502,8 +502,8 @@ START_TEST(udev_seat_recycle)
}
END_TEST
int
main(int argc, char **argv)
void
litest_setup_tests(void)
{
litest_add_no_device("udev:create", udev_create_NULL);
litest_add_no_device("udev:create", udev_create_seat0);
@ -518,6 +518,4 @@ main(int argc, char **argv)
litest_add_for_device("udev:suspend", udev_suspend_resume, LITEST_SYNAPTICS_CLICKPAD);
litest_add_for_device("udev:device events", udev_device_sysname, LITEST_SYNAPTICS_CLICKPAD);
litest_add_for_device("udev:seat", udev_seat_recycle, LITEST_SYNAPTICS_CLICKPAD);
return litest_run(argc, argv);
}

View file

@ -31,3 +31,11 @@
...
fun:g_malloc0
}
{
libunwind:msync_uninitialized_bytes
Memcheck:Param
msync(start)
fun:__msync_nocancel
...
fun:litest_backtrace
}

3
tools/.gitignore vendored
View file

@ -1,2 +1,5 @@
event-debug
event-gui
ptraccel-debug
libinput-list-devices
libinput-debug-events

View file

@ -1,19 +1,36 @@
noinst_PROGRAMS = event-debug
noinst_PROGRAMS = event-debug ptraccel-debug
bin_PROGRAMS = libinput-list-devices libinput-debug-events
noinst_LTLIBRARIES = libshared.la
AM_CPPFLAGS = -I$(top_srcdir)/include \
-I$(top_srcdir)/src
-I$(top_srcdir)/src \
-I$(top_builddir)/src # for libinput-version.h
libshared_la_SOURCES = \
shared.c \
shared.h
libshared_la_CFLAGS = $(LIBEVDEV_CFLAGS)
libshared_la_LIBADD = $(LIBEVDEV_LIBS)
event_debug_SOURCES = event-debug.c
event_debug_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS)
event_debug_LDFLAGS = -no-install
event_debug_CFLAGS = $(LIBUDEV_CFLAGS)
ptraccel_debug_SOURCES = ptraccel-debug.c
ptraccel_debug_LDADD = ../src/libfilter.la
ptraccel_debug_LDFLAGS = -no-install
libinput_list_devices_SOURCES = libinput-list-devices.c
libinput_list_devices_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS)
libinput_list_devices_CFLAGS = $(LIBUDEV_CFLAGS)
dist_man1_MANS = libinput-list-devices.man
libinput_debug_events_SOURCES = $(event_debug_SOURCES)
libinput_debug_events_LDADD = $(event_debug_LDADD)
libinput_debug_events_CFLAGS = $(event_debug_CFLAGS)
dist_man1_MANS += libinput-debug-events.man
if BUILD_EVENTGUI
noinst_PROGRAMS += event-gui

View file

@ -62,8 +62,8 @@ struct window {
int absx, absy;
/* scroll bar positions */
int vx, vy;
int hx, hy;
double vx, vy;
double hx, hy;
/* touch positions */
struct touch touches[32];
@ -279,14 +279,8 @@ handle_event_device_notify(struct libinput_event *ev)
libinput_device_get_name(dev),
type);
if (libinput_device_config_tap_get_finger_count(dev) > 0) {
enum libinput_config_status status;
status = libinput_device_config_tap_set_enabled(dev,
LIBINPUT_CONFIG_TAP_ENABLED);
if (status != LIBINPUT_CONFIG_STATUS_SUCCESS)
error("%s: Failed to enable tapping\n",
libinput_device_get_sysname(dev));
}
tools_device_apply_config(libinput_event_get_device(ev),
&options);
li = libinput_event_get_context(ev);
w = libinput_get_user_data(li);
@ -369,7 +363,7 @@ handle_event_axis(struct libinput_event *ev, struct window *w)
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
value = libinput_event_pointer_get_axis_value(p,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
w->vy += (int)value;
w->vy += value;
w->vy = clip(w->vy, 0, w->height);
}
@ -377,7 +371,7 @@ handle_event_axis(struct libinput_event *ev, struct window *w)
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) {
value = libinput_event_pointer_get_axis_value(p,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
w->hx += (int)value;
w->hx += value;
w->hx = clip(w->hx, 0, w->width);
}
}

View file

@ -0,0 +1,31 @@
.TH LIBINPUT-DEBUG-EVENTS "1"
.SH NAME
libinput-debug-events \- debug helper for libinput
.SH SYNOPSIS
.B libinput-debug-events [--help]
.SH DESCRIPTION
.PP
The
.I libinput-debug-events
tool creates a libinput context and prints all events from these devices.
.PP
This is a debugging tool only, its output may change at any time. Do not
rely on the output.
.PP
This tool usually needs to be run as root to have access to the
/dev/input/eventX nodes.
.SH OPTIONS
.TP 8
.B --help
Print help
.PP
For all other options, see the output from --help. Options may be added or
removed at any time.
.SH NOTES
.PP
Events shown by this tool may not correspond to the events seen by a
different user of libinput. This tool initializes a separate context.
.PP
Events shown by this tool include key codes in plain text. Anything you type
while this tool is running will show up in the output, including your
passwords.

View file

@ -0,0 +1,313 @@
/*
* Copyright © 2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The copyright holders make
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <libudev.h>
#include <libinput.h>
#include <libinput-version.h>
#include "shared.h"
static int
open_restricted(const char *path, int flags, void *user_data)
{
int fd = open(path, flags);
if (fd < 0)
fprintf(stderr, "Failed to open %s (%s)\n",
path, strerror(errno));
return fd < 0 ? -errno : fd;
}
static void
close_restricted(int fd, void *user_data)
{
close(fd);
}
static const struct libinput_interface interface = {
.open_restricted = open_restricted,
.close_restricted = close_restricted,
};
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)
{
if (!libinput_device_config_tap_get_finger_count(device))
return "n/a";
if (libinput_device_config_tap_get_default_enabled(device))
return "enabled";
else
return "disabled";
}
static const char*
left_handed_default(struct libinput_device *device)
{
if (!libinput_device_config_left_handed_is_available(device))
return "n/a";
if (libinput_device_config_left_handed_get_default(device))
return "enabled";
else
return "disabled";
}
static const char *
nat_scroll_default(struct libinput_device *device)
{
if (!libinput_device_config_scroll_has_natural_scroll(device))
return "n/a";
if (libinput_device_config_scroll_get_default_natural_scroll_enabled(device))
return "enabled";
else
return "disabled";
}
static const char *
middle_emulation_default(struct libinput_device *device)
{
if (!libinput_device_config_middle_emulation_is_available(device))
return "n/a";
if (libinput_device_config_middle_emulation_get_default_enabled(device))
return "enabled";
else
return "disabled";
}
static char *
calibration_default(struct libinput_device *device)
{
char *str;
float calibration[6];
if (!libinput_device_config_calibration_has_matrix(device)) {
asprintf(&str, "n/a");
return str;
}
if (libinput_device_config_calibration_get_default_matrix(device,
calibration) == 0) {
asprintf(&str, "identity matrix");
return str;
}
asprintf(&str,
"%.2f %.2f %.2f %.2f %.2f %.2f",
calibration[0],
calibration[1],
calibration[2],
calibration[3],
calibration[4],
calibration[5]);
return str;
}
static char *
scroll_defaults(struct libinput_device *device)
{
uint32_t scroll_methods;
char *str;
enum libinput_config_scroll_method method;
scroll_methods = libinput_device_config_scroll_get_methods(device);
if (scroll_methods == LIBINPUT_CONFIG_SCROLL_NO_SCROLL) {
asprintf(&str, "none");
return str;
}
method = libinput_device_config_scroll_get_default_method(device);
asprintf(&str,
"%s%s%s%s%s%s",
(method == LIBINPUT_CONFIG_SCROLL_2FG) ? "*" : "",
(scroll_methods & LIBINPUT_CONFIG_SCROLL_2FG) ? "two-finger " : "",
(method == LIBINPUT_CONFIG_SCROLL_EDGE) ? "*" : "",
(scroll_methods & LIBINPUT_CONFIG_SCROLL_EDGE) ? "edge " : "",
(method == LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) ? "*" : "",
(scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) ? "button" : "");
return str;
}
static char*
click_defaults(struct libinput_device *device)
{
uint32_t click_methods;
char *str;
enum libinput_config_click_method method;
click_methods = libinput_device_config_click_get_methods(device);
if (click_methods == LIBINPUT_CONFIG_CLICK_METHOD_NONE) {
asprintf(&str, "none");
return str;
}
method = libinput_device_config_click_get_default_method(device);
asprintf(&str,
"%s%s%s%s",
(method == LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "*" : "",
(click_methods & LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "button-areas " : "",
(method == LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) ? "*" : "",
(click_methods & LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) ? "clickfinger " : "");
return str;
}
static void
print_device_notify(struct libinput_event *ev)
{
struct libinput_device *dev = libinput_event_get_device(ev);
struct libinput_seat *seat = libinput_device_get_seat(dev);
struct libinput_device_group *group;
double w, h;
static int next_group_id = 0;
intptr_t group_id;
const char *devnode;
char *str;
group = libinput_device_get_device_group(dev);
group_id = (intptr_t)libinput_device_group_get_user_data(group);
if (!group_id) {
group_id = ++next_group_id;
libinput_device_group_set_user_data(group, (void*)group_id);
}
devnode = udev_device_get_devnode(
libinput_device_get_udev_device(dev));
printf("Device: %s\n"
"Kernel: %s\n"
"Group: %d\n"
"Seat: %s, %s\n",
libinput_device_get_name(dev),
devnode,
(int)group_id,
libinput_seat_get_physical_name(seat),
libinput_seat_get_logical_name(seat));
if (libinput_device_get_size(dev, &w, &h) == 0)
printf("Size: %.2fx%.2fmm\n", w, h);
printf("Capabilities: ");
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_KEYBOARD))
printf("keyboard ");
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_POINTER))
printf("pointer ");
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_TOUCH))
printf("touch");
printf("\n");
printf("Tap-to-click: %s\n", tap_default(dev));
printf("Left-handed: %s\n", left_handed_default(dev));
printf("Nat.scrolling: %s\n", nat_scroll_default(dev));
printf("Middle emulation: %s\n", middle_emulation_default(dev));
str = calibration_default(dev);
printf("Calibration: %s\n", str);
free(str);
str = scroll_defaults(dev);
printf("Scroll methods: %s\n", str);
free(str);
str = click_defaults(dev);
printf("Click methods: %s\n", str);
free(str);
printf("\n");
}
static inline void
usage(void)
{
printf("Usage: %s [--help|--version]\n"
"\n"
"This tool creates a libinput context on the default seat \"seat0\"\n"
"and lists all devices recognized by libinput and the configuration options.\n"
"Where multiple options are possible, the default is prefixed with \"*\".\n"
"\n"
"Options:\n"
"--help ...... show this help\n"
"--version ... show version information\n"
"\n"
"This tool requires access to the /dev/input/eventX nodes.\n",
program_invocation_short_name);
}
int
main(int argc, char **argv)
{
struct libinput *li;
struct tools_options options;
struct libinput_event *ev;
if (argc > 1) {
if (strcmp(argv[1], "--help") == 0) {
usage();
return 0;
} else if (strcmp(argv[1], "--version") == 0) {
printf("%s\n", LIBINPUT_VERSION);
return 0;
} else {
usage();
return 1;
}
}
tools_init_options(&options);
li = tools_open_backend(&options, NULL, &interface);
if (!li)
return 1;
libinput_dispatch(li);
while ((ev = libinput_get_event(li))) {
if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED)
print_device_notify(ev);
libinput_event_destroy(ev);
libinput_dispatch(li);
}
libinput_unref(li);
return 0;
}

View file

@ -0,0 +1,37 @@
.TH LIBINPUT-LIST-DEVICES "1"
.SH NAME
libinput-list-devices \- list local devices as recognized by libinput
.SH SYNOPSIS
.B libinput-list-devices [--help]
.SH DESCRIPTION
.PP
The
.I libinput-list-devices
tool creates a libinput context on the default seat "seat0" and lists all
devices regonized by libinput. Each device shows available configurations
the respective default configuration setting.
.PP
For configuration options that allow multiple different settings (e.g.
scrolling), all available settings are listed. The default setting is
prefixed by an asterisk (*).
.PP
This tool usually needs to be run as root to have access to the
/dev/input/eventX nodes.
.SH OPTIONS
.TP 8
.B --help
Print help
.SH NOTES
.PP
Some specific feature may still be available on a device even when
no configuration is exposed, a lack of a configuration option does not
necessarily mean that this feature does not work.
.PP
A device may be recognized by libinput but not handled by the X.Org libinput
driver or the Wayland compositor.
.PP
An xorg.conf(5) configuration entry or Wayland compositor setting may have
changed configurations on a device. The
.I libinput-list-devices
tool only shows the device's default configuration, not the current
configuration.

300
tools/ptraccel-debug.c Normal file
View file

@ -0,0 +1,300 @@
/*
* Copyright © 2015 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The copyright holders make
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define _GNU_SOURCE
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <filter.h>
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static void
print_ptraccel_deltas(struct motion_filter *filter, double step)
{
struct normalized_coords motion;
uint64_t time = 0;
double i;
printf("# gnuplot:\n");
printf("# set xlabel dx unaccelerated\n");
printf("# set ylabel dx accelerated\n");
printf("# set style data lines\n");
printf("# plot \"gnuplot.data\" using 1:2 title \"step %.2f\"\n", step);
printf("#\n");
/* Accel flattens out after 15 and becomes linear */
for (i = 0.0; i < 15.0; i += step) {
motion.x = i;
motion.y = 0;
time += 12; /* pretend 80Hz data */
motion = filter_dispatch(filter, &motion, NULL, time);
printf("%.2f %.3f\n", i, motion.x);
}
}
static void
print_ptraccel_movement(struct motion_filter *filter,
int nevents,
double max_dx,
double step)
{
struct normalized_coords motion;
uint64_t time = 0;
double dx;
int i;
printf("# gnuplot:\n");
printf("# set xlabel \"event number\"\n");
printf("# set ylabel \"delta motion\"\n");
printf("# set style data lines\n");
printf("# plot \"gnuplot.data\" using 1:2 title \"dx out\", \\\n");
printf("# \"gnuplot.data\" using 1:3 title \"dx in\"\n");
printf("#\n");
if (nevents == 0) {
if (step > 1.0)
nevents = max_dx;
else
nevents = 1.0 * max_dx/step + 0.5;
/* Print more events than needed so we see the curve
* flattening out */
nevents *= 1.5;
}
dx = 0;
for (i = 0; i < nevents; i++) {
motion.x = dx;
motion.y = 0;
time += 12; /* pretend 80Hz data */
filter_dispatch(filter, &motion, NULL, time);
printf("%d %.3f %.3f\n", i, motion.x, dx);
if (dx < max_dx)
dx += step;
}
}
static void
print_ptraccel_sequence(struct motion_filter *filter,
int nevents,
double *deltas)
{
struct normalized_coords motion;
uint64_t time = 0;
double *dx;
int i;
printf("# gnuplot:\n");
printf("# set xlabel \"event number\"\n");
printf("# set ylabel \"delta motion\"\n");
printf("# set style data lines\n");
printf("# plot \"gnuplot.data\" using 1:2 title \"dx out\", \\\n");
printf("# \"gnuplot.data\" using 1:3 title \"dx in\"\n");
printf("#\n");
dx = deltas;
for (i = 0; i < nevents; i++, dx++) {
motion.x = *dx;
motion.y = 0;
time += 12; /* pretend 80Hz data */
filter_dispatch(filter, &motion, NULL, time);
printf("%d %.3f %.3f\n", i, motion.x, *dx);
}
}
static void
print_accel_func(struct motion_filter *filter)
{
double vel;
printf("# gnuplot:\n");
printf("# set xlabel \"speed\"\n");
printf("# set ylabel \"raw accel factor\"\n");
printf("# set style data lines\n");
printf("# plot \"gnuplot.data\" using 1:2\n");
for (vel = 0.0; vel < 3.0; vel += .0001) {
double result = pointer_accel_profile_linear(filter,
NULL,
vel,
0 /* time */);
printf("%.4f\t%.4f\n", vel, result);
}
}
static void
usage(void)
{
printf("Usage: %s [options] [dx1] [dx2] [...] > gnuplot.data\n", program_invocation_short_name);
printf("\n"
"Options:\n"
"--mode=<motion|accel|delta|sequence> \n"
" motion ... print motion to accelerated motion (default)\n"
" delta ... print delta to accelerated delta\n"
" accel ... print accel factor\n"
" sequence ... print motion for custom delta sequence\n"
"--maxdx=<double>\n ... in motion mode only. Stop increasing dx at maxdx\n"
"--steps=<double>\n ... in motion and delta modes only. Increase dx by step each round\n"
"--speed=<double>\n ... accel speed [-1, 1], default 0\n"
"\n"
"If extra arguments are present and mode is not given, mode defaults to 'sequence'\n"
"and the arguments are interpreted as sequence of delta x coordinates\n"
"\n"
"If stdin is a pipe, mode defaults to 'sequence' and the pipe is read \n"
"for delta coordinates\n"
"\n"
"Output best viewed with gnuplot. See output for gnuplot commands\n");
}
int
main(int argc, char **argv)
{
struct motion_filter *filter;
double step = 0.1,
max_dx = 10;
int nevents = 0;
bool print_accel = false,
print_motion = true,
print_delta = false,
print_sequence = false;
double custom_deltas[1024];
double speed = 0.0;
enum {
OPT_MODE = 1,
OPT_NEVENTS,
OPT_MAXDX,
OPT_STEP,
OPT_SPEED,
};
filter = create_pointer_accelerator_filter(pointer_accel_profile_linear);
assert(filter != NULL);
while (1) {
int c;
int option_index = 0;
static struct option long_options[] = {
{"mode", 1, 0, OPT_MODE },
{"nevents", 1, 0, OPT_NEVENTS },
{"maxdx", 1, 0, OPT_MAXDX },
{"step", 1, 0, OPT_STEP },
{"speed", 1, 0, OPT_SPEED },
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case OPT_MODE:
if (strcmp(optarg, "accel") == 0)
print_accel = true;
else if (strcmp(optarg, "motion") == 0)
print_motion = true;
else if (strcmp(optarg, "delta") == 0)
print_delta = true;
else if (strcmp(optarg, "sequence") == 0)
print_sequence = true;
else {
usage();
return 1;
}
break;
case OPT_NEVENTS:
nevents = atoi(optarg);
if (nevents == 0) {
usage();
return 1;
}
break;
case OPT_MAXDX:
max_dx = strtod(optarg, NULL);
if (max_dx == 0.0) {
usage();
return 1;
}
break;
case OPT_STEP:
step = strtod(optarg, NULL);
if (step == 0.0) {
usage();
return 1;
}
break;
case OPT_SPEED:
speed = strtod(optarg, NULL);
break;
default:
usage();
exit(1);
break;
}
}
filter_set_speed(filter, speed);
if (!isatty(STDIN_FILENO)) {
char buf[12];
print_sequence = true;
print_motion = false;
nevents = 0;
memset(custom_deltas, 0, sizeof(custom_deltas));
while(fgets(buf, sizeof(buf), stdin) && nevents < 1024) {
custom_deltas[nevents++] = strtod(buf, NULL);
}
} else if (optind < argc) {
print_sequence = true;
print_motion = false;
nevents = 0;
memset(custom_deltas, 0, sizeof(custom_deltas));
while (optind < argc)
custom_deltas[nevents++] = strtod(argv[optind++], NULL);
}
if (print_accel)
print_accel_func(filter);
else if (print_delta)
print_ptraccel_deltas(filter, step);
else if (print_motion)
print_ptraccel_movement(filter, nevents, max_dx, step);
else if (print_sequence)
print_ptraccel_sequence(filter, nevents, custom_deltas);
filter_destroy(filter);
return 0;
}

View file

@ -30,6 +30,8 @@
#include <string.h>
#include <libudev.h>
#include <libevdev/libevdev.h>
#include "shared.h"
enum options {
@ -43,7 +45,12 @@ enum options {
OPT_NATURAL_SCROLL_DISABLE,
OPT_LEFT_HANDED_ENABLE,
OPT_LEFT_HANDED_DISABLE,
OPT_MIDDLEBUTTON_ENABLE,
OPT_MIDDLEBUTTON_DISABLE,
OPT_CLICK_METHOD,
OPT_SCROLL_METHOD,
OPT_SCROLL_BUTTON,
OPT_SPEED,
};
static void
@ -70,7 +77,12 @@ tools_usage()
"--disable-natural-scrolling.... enable/disable natural scrolling\n"
"--enable-left-handed\n"
"--disable-left-handed.... enable/disable left-handed button configuration\n"
"--enable-middlebutton\n"
"--disable-middlebutton.... enable/disable middle button emulation\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"
"--set-speed=<value>.... set pointer acceleration speed\n"
"\n"
"These options apply to all applicable devices, if a feature\n"
"is not explicitly specified it is left at each device's default.\n"
@ -88,9 +100,13 @@ tools_init_options(struct tools_options *options)
options->tapping = -1;
options->natural_scroll = -1;
options->left_handed = -1;
options->middlebutton = -1;
options->click_method = -1;
options->scroll_method = -1;
options->scroll_button = -1;
options->backend = BACKEND_UDEV;
options->seat = "seat0";
options->speed = 0.0;
}
int
@ -110,7 +126,12 @@ tools_parse_args(int argc, char **argv, struct tools_options *options)
{ "disable-natural-scrolling", 0, 0, OPT_NATURAL_SCROLL_DISABLE },
{ "enable-left-handed", 0, 0, OPT_LEFT_HANDED_ENABLE },
{ "disable-left-handed", 0, 0, OPT_LEFT_HANDED_DISABLE },
{ "enable-middlebutton", 0, 0, OPT_MIDDLEBUTTON_ENABLE },
{ "disable-middlebutton", 0, 0, OPT_MIDDLEBUTTON_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 },
{ "speed", 1, 0, OPT_SPEED },
{ 0, 0, 0, 0}
};
@ -157,6 +178,12 @@ tools_parse_args(int argc, char **argv, struct tools_options *options)
case OPT_LEFT_HANDED_DISABLE:
options->left_handed = 0;
break;
case OPT_MIDDLEBUTTON_ENABLE:
options->middlebutton = 1;
break;
case OPT_MIDDLEBUTTON_DISABLE:
options->middlebutton = 0;
break;
case OPT_CLICK_METHOD:
if (!optarg) {
tools_usage();
@ -176,6 +203,50 @@ tools_parse_args(int argc, char **argv, struct tools_options *options)
return 1;
}
break;
case OPT_SCROLL_METHOD:
if (!optarg) {
tools_usage();
return 1;
}
if (strcmp(optarg, "none") == 0) {
options->scroll_method =
LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
} else if (strcmp(optarg, "twofinger") == 0) {
options->scroll_method =
LIBINPUT_CONFIG_SCROLL_2FG;
} else if (strcmp(optarg, "edge") == 0) {
options->scroll_method =
LIBINPUT_CONFIG_SCROLL_EDGE;
} else if (strcmp(optarg, "button") == 0) {
options->scroll_method =
LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
} else {
tools_usage();
return 1;
}
break;
case OPT_SCROLL_BUTTON:
if (!optarg) {
tools_usage();
return 1;
}
options->scroll_button =
libevdev_event_code_from_name(EV_KEY,
optarg);
if (options->scroll_button == -1) {
fprintf(stderr,
"Invalid button %s\n",
optarg);
return 1;
}
break;
case OPT_SPEED:
if (!optarg) {
tools_usage();
return 1;
}
options->speed = atof(optarg);
break;
default:
tools_usage();
return 1;
@ -286,7 +357,21 @@ tools_device_apply_config(struct libinput_device *device,
options->natural_scroll);
if (options->left_handed != -1)
libinput_device_config_left_handed_set(device, options->left_handed);
if (options->middlebutton != -1)
libinput_device_config_middle_emulation_set_enabled(device,
options->middlebutton);
if (options->click_method != -1)
libinput_device_config_click_set_method(device, options->click_method);
if (options->scroll_method != -1)
libinput_device_config_scroll_set_method(device,
options->scroll_method);
if (options->scroll_button != -1)
libinput_device_config_scroll_set_button(device,
options->scroll_button);
if (libinput_device_config_accel_is_available(device))
libinput_device_config_accel_set_speed(device,
options->speed);
}

View file

@ -39,7 +39,11 @@ struct tools_options {
int tapping;
int natural_scroll;
int left_handed;
int middlebutton;
enum libinput_config_click_method click_method;
enum libinput_config_scroll_method scroll_method;
int scroll_button;
double speed;
};
void tools_init_options(struct tools_options *options);

View file

@ -0,0 +1,66 @@
# Do not edit this file, it will be overwritten on update
#
# This file contains hwdb matches for libinput model-specific quirks.
# The contents of this file are a contract between libinput, udev rules and
# the hwdb.
# IT IS NOT A STABLE API AND SUBJECT TO CHANGE AT ANY TIME
# The lookup keys are composed in:
# 90-libinput-model-quirks.rules
#
# Match string formats:
# libinput:<modalias>
# libinput:name:<name>:dmi:<dmi string>
#
# Sort by brand, model
##########################################
# Google
##########################################
# The various chromebooks, info from modinfo chromeos_laptop, touchpad names
# extrapolated from the chromiumos touchad-tests repo
# https://chromium.googlesource.com/chromiumos/platform/touchpad-tests
libinput:name:Cypress APA Trackpad (cyapa):dmi:*pnFalco:pvr*
libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pn*Mario*:
libinput:name:Cypress APA Trackpad (cyapa):dmi:*pn*Butterfly*:
libinput:name:Cypress APA Trackpad (cyapa):dmi:*pn*Peppy*:
libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pn*ZGB*:
libinput:name:Cypress APA Trackpad (cyapa):dmi:*pn*Parrot*:
libinput:name:Cypress APA Trackpad (cyapa):dmi:*bvn*coreboot*:pn*Leon*:
libinput:name:Cypress APA Trackpad (cyapa):dmi:*bvn*coreboot*:pn*Falco*:
libinput:name:Cypress APA Trackpad (cyapa):dmi:*bvn*coreboot*:pn*Wolf*:
libinput:name:Cypress APA Trackpad (cyapa):dmi:*svn*GOOGLE*:pn*Link*:
libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pn*Alex*:
libinput:name:Cypress APA Trackpad (cyapa):dmi:*svn*SAMSUNG*:pn*Lumpy*:
libinput:name:Atmel maXTouch Touchpad:dmi:*svn*GOOGLE*:pn*Samus*:
LIBINPUT_MODEL_CHROMEBOOK=1
##########################################
# LENOVO
##########################################
# X230 (Tablet)
libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO:*:pvrThinkPadX230*
LIBINPUT_MODEL_LENOVO_X230=1
##########################################
# System76
##########################################
# Bonobo Professional
libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnSystem76*pvrbonp5*
LIBINPUT_MODEL_SYSTEM76_BONOBO=1
# Clevo
libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pnW740SU*rnW740SU*
LIBINPUT_MODEL_CLEVO_W740SU=1
# Galago Ultra Pro
libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnSystem76*pvrgalu1*
LIBINPUT_MODEL_SYSTEM76_GALAGO=1
# Kudu Professional
libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnSystem76*pvrkudp1*
LIBINPUT_MODEL_SYSTEM76_KUDU=1

View file

@ -0,0 +1,25 @@
# Do not edit this file, it will be overwritten on update
#
# This file contains lookup rules for libinput model-specific quirks.
# The contents of this file are a contract between libinput, udev rules and
# the hwdb.
# IT IS NOT A STABLE API AND SUBJECT TO CHANGE AT ANY TIME
#
# The hwdb database is in:
# 90-libinput-model-quirks.hwdb
ACTION!="add|change", GOTO="libinput_model_quirks_end"
KERNEL!="event*", GOTO="libinput_model_quirks_end"
# hwdb matches:
#
# libinput:<modalias>
IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=libinput:", \
GOTO="libinput_model_quirks_end"
# libinput:name:<name>:dmi:<dmi string>
KERNELS=="input*", \
IMPORT{builtin}="hwdb 'libinput:name:$attr{name}:$attr{[dmi/id]modalias}'", \
GOTO="libinput_model_quirks_end"
LABEL="libinput_model_quirks_end"

View file

@ -6,4 +6,10 @@ libinput_device_group_CFLAGS = $(LIBUDEV_CFLAGS) $(GCC_CFLAGS)
libinput_device_group_LDADD = $(LIBUDEV_LIBS)
udev_rulesdir=$(UDEV_DIR)/rules.d
dist_udev_rules_DATA = 80-libinput-device-groups.rules
dist_udev_rules_DATA = \
80-libinput-device-groups.rules \
90-libinput-model-quirks.rules
udev_hwdbdir=$(UDEV_DIR)/hwdb.d
dist_udev_hwdb_DATA = \
90-libinput-model-quirks.hwdb