Merge branch 'master' into tablet-support

Conflicts:
	src/libinput.h
This commit is contained in:
Peter Hutterer 2015-01-16 07:55:23 +10:00
commit 395c449c8e
49 changed files with 2727 additions and 716 deletions

View file

@ -1,7 +1,7 @@
AC_PREREQ([2.64])
m4_define([libinput_major_version], [0])
m4_define([libinput_minor_version], [7])
m4_define([libinput_minor_version], [8])
m4_define([libinput_micro_version], [0])
m4_define([libinput_version],
[libinput_major_version.libinput_minor_version.libinput_micro_version])
@ -30,7 +30,7 @@ AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
# - If binary compatibility has been broken (eg removed or changed interfaces)
# change to C+1:0:0
# - If the interface is the same as the previous version, change to C:R+1:A
LIBINPUT_LT_VERSION=6:0:1
LIBINPUT_LT_VERSION=7:0:0
AC_SUBST(LIBINPUT_LT_VERSION)
AM_SILENT_RULES([yes])
@ -38,6 +38,7 @@ AM_SILENT_RULES([yes])
# Check for programs
AC_PROG_CC_C99
AC_PROG_CXX # Only used by build C++ test
AC_PROG_GREP
# Initialize libtool
LT_PREREQ([2.2])
@ -67,14 +68,55 @@ fi
AC_SUBST(GCC_CFLAGS)
AC_SUBST(GCC_CXXFLAGS)
AC_PATH_PROG(DOXYGEN, [doxygen])
if test "x$DOXYGEN" = "x"; then
AC_MSG_WARN([doxygen not found - required for documentation])
have_doxygen="no"
else
have_doxygen="yes"
AC_ARG_ENABLE([documentation],
[AC_HELP_STRING([--enable-documentation],
[Enable building the documentation (default=auto)])],
[build_documentation="$enableval"],
[build_documentation="auto"])
if test "x$build_documentation" = "xyes" -o "x$build_documentation" = "xauto"; then
AC_PATH_PROG(DOXYGEN, doxygen)
if test "x$DOXYGEN" = "x"; then
if test "x$build_documentation" = "xyes"; then
AC_MSG_ERROR([Documentation build requested but doxygen not found. Install doxygen or disable the documentation using --disable-documentation])
fi
else
AC_MSG_CHECKING([for compatible doxygen version])
doxygen_version=`$DOXYGEN --version`
AS_VERSION_COMPARE([$doxygen_version], [1.6.0],
[AC_MSG_RESULT([no])
DOXYGEN=""],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([yes])])
if test "x$DOXYGEN" = "x" -a "x$build_documentation" = "xyes"; then
AC_MSG_ERROR([Doxygen $doxygen_version too old. Doxygen 1.6+ required for documentation build. Install required doxygen version or disable the documentation using --disable-documentation])
fi
fi
AC_PATH_PROG(DOT, dot)
if test "x$DOT" = "x"; then
if test "x$build_documentation" = "xyes"; then
AC_MSG_ERROR([Documentation build requested but graphviz's dot not found. Install graphviz or disable the documentation using --disable-documentation])
fi
else
AC_MSG_CHECKING([for compatible dot version])
dot_version=`$DOT -V 2>&1|$GREP -oP '(?<=version\W)@<:@0-9.@:>@*(?=\W(.*))'`
AS_VERSION_COMPARE([$dot_version], [2.26.0],
[AC_MSG_RESULT([no])
DOT=""],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([yes])])
if test "x$DOT" = "x" -a "x$build_documentation" = "xyes"; then
AC_MSG_ERROR([Graphviz dot $dot_version too old. Graphviz 2.26+ required for documentation build. Install required graphviz version or disable the documentation using --disable-documentation])
fi
fi
if test "x$DOXYGEN" != "x" -a "x$DOT" != "x"; then
build_documentation="yes"
else
build_documentation="no"
fi
fi
AM_CONDITIONAL([HAVE_DOXYGEN], [test "x$have_doxygen" = "xyes"])
AC_ARG_ENABLE(event-gui,
AS_HELP_STRING([--enable-event-gui], [Build the GUI event viewer (default=auto)]),
@ -111,6 +153,7 @@ fi
AM_CONDITIONAL(HAVE_VALGRIND, [test "x$VALGRIND" != "x"])
AM_CONDITIONAL(BUILD_TESTS, [test "x$build_tests" = "xyes"])
AM_CONDITIONAL(BUILD_DOCS, [test "x$build_documentation" = "xyes"])
AC_CONFIG_FILES([Makefile
doc/Makefile
@ -125,7 +168,7 @@ AC_OUTPUT
AC_MSG_RESULT([
Prefix ${prefix}
Build documentation ${have_doxygen}
Build documentation ${build_documentation}
Build tests ${build_tests}
Tests use valgrind ${VALGRIND}
Build GUI event tool ${build_eventgui}

View file

@ -1,21 +1,40 @@
EXTRA_DIST = touchpad-tap-state-machine.svg touchpad-softbutton-state-machine.svg
if HAVE_DOXYGEN
if BUILD_DOCS
noinst_DATA = html/index.html
header_files = \
$(top_srcdir)/src/libinput.h \
$(top_srcdir)/README.txt
$(top_srcdir)/README.txt \
$(srcdir)/absolute-axes.dox \
$(srcdir)/clickpad-softbuttons.dox \
$(srcdir)/device-configuration-via-udev.dox \
$(srcdir)/normalization-of-relative-motion.dox \
$(srcdir)/palm-detection.dox \
$(srcdir)/scrolling.dox \
$(srcdir)/seats.dox \
$(srcdir)/t440-support.dox \
$(srcdir)/tapping.dox
html/index.html: libinput.doxygen $(header_files)
$(AM_V_GEN)$(DOXYGEN) $<
diagram_files = \
$(srcdir)/dot/seats-sketch.gv \
$(srcdir)/dot/seats-sketch-libinput.gv \
$(srcdir)/svg/button-scrolling.svg \
$(srcdir)/svg/edge-scrolling.svg \
$(srcdir)/svg/palm-detection.svg \
$(srcdir)/svg/twofinger-scrolling.svg
html/index.html: libinput.doxygen $(header_files) $(diagram_files)
$(AM_V_GEN)(cat $<; \
echo "INPUT = $(header_files)"; \
) | $(DOXYGEN) -
clean-local:
$(AM_V_at)rm -rf html
doc_src= $(shell find html -type f -printf "html/%P\n" 2>/dev/null)
EXTRA_DIST += $(builddir)/html/index.html $(doc_src)
EXTRA_DIST += $(builddir)/html/index.html $(doc_src) $(diagram_files) $(header_files)
endif

130
doc/absolute-axes.dox Normal file
View file

@ -0,0 +1,130 @@
/**
@page absolute_axes Absolute axes
Devices with absolute axes are those that send positioning data for an axis in
a device-specific coordinate range, defined by a minimum and a maximum value.
Compare this to relative devices (e.g. a mouse) that can only detect
directional data, not positional data.
libinput supports three types of devices with absolute axes:
- multi-touch screens
- single-touch screens
- graphics tablets (currently WIP)
Touchpads are technically absolute devices but libinput converts the axis values
to directional motion and posts events as relative events. Touchpads do not count
as absolute devices in libinput.
For all absolute devices in libinput, the default unit for x/y coordinates is
in mm off the top left corner on the device, or more specifically off the
device's sensor.
@section absolute_axes_handling Handling of absolute coordinates
In most use-cases, absolute input devices are mapped to a single screen. For
direct input devices such as touchscreens the aspect ratio of the screen and
the device match. Mapping the input device position to the output position is
thus a simple mapping between two coordinates. libinput provides the API for
this with
- libinput_event_pointer_get_absolute_x_transformed() for pointer events
- libinput_event_touch_get_x_transformed() for touch events
libinput's API only provides the call to map into a single coordinate range.
If the coordinate range has an offset, the compositor is responsible for
applying that offset after the mapping. For example, if the device is mapped
to the right of two outputs, add the output offset to the transformed
coordinate.
@section absolute_axes_nores Devices without x/y resolution
An absolute device that does not provide a valid resolution is considered
buggy and must be fixed in the kernel. Some touchpad devices that do not
provide resolution, those devices are correctly handled within libinput
(touchpads are not absolute devices, as mentioned above).
@section calibration Calibration of absolute devices
Absolute devices may require calibration to map precisely into the output
range required. This is done by setting a transformation matrix, see
libinput_device_config_calibration_set_matrix() which is applied to
each input coordinate.
@f[
\begin{pmatrix}
cos\theta & -sin\theta & xoff \\
sin\theta & cos\theta & yoff \\
0 & 0 & 1
\end{pmatrix} \begin{pmatrix}
x \\ y \\ 1
\end{pmatrix}
@f]
@f$\theta@f$ is the rotation angle. The offsets @f$xoff@f$ and @f$yoff@f$ are
specified in device dimensions, i.e. a value of 1 equals one device width
or height. Note that rotation applies to the device's origin, rotation
usually requires an offset to move the coordinates back into the original
range.
The most comon matrices are:
- 90 degree clockwise:
@f$
\begin{pmatrix}
0 & -1 & 1 \\
1 & 0 & 0 \\
0 & 0 & 1
\end{pmatrix}
@f$
- 180 degree clockwise:
@f$
\begin{pmatrix}
-1 & 0 & 1 \\
0 & -1 & 1 \\
0 & 0 & 1
\end{pmatrix}
@f$
- 270 degree clockwise:
@f$
\begin{pmatrix}
0 & 1 & 0 \\
-1 & 0 & 1 \\
0 & 0 & 1
\end{pmatrix}
@f$
- reflection along y axis:
@f$
\begin{pmatrix}
-1 & 0 & 1 \\
1 & 0 & 0 \\
0 & 0 & 1
\end{pmatrix}
@f$
See Wikipedia's
<a href="http://en.wikipedia.org/wiki/Transformation_matrix">Transformation
Matrix article</a> for more information on the matrix maths.
See libinput_device_config_calibration_get_default_matrix() for how these
matrices must be supplied to libinput.
@section absolute_axes_nonorm Why x/y coordinates are not normalized
x/y are not given in @ref motion_normalization "normalized coordinates"
([0..1]) for one simple reason: the aspect ratio of virtually all current
devices is something other than 1:1. A normalized axes thus is only useful to
determine that the stylus is e.g. at 78% from the left, 34% from the top of
the device. Without knowing the per-axis resolution, these numbers are
meaningless. Worse, calculation based on previous coordinates is simply wrong:
a movement from 0/0 to 50%/50% is not a 45% degree line.
This could be alleviated by providing resolution and information about the
aspect ratio to the caller. Which shifts processing and likely errors into the
caller for little benefit. Providing the x/y axes in mm from the outset
removes these errors.
*/

View file

@ -0,0 +1,80 @@
/**
@page clickpad_softbuttons Clickpad software button behavior
Clickpad is the name given to touchpads without physical buttons below the
touchpad. Instead, the whole touchpad acts as a button and left or right
button clicks are distinguished by the location and/or number of fingers on
the touchpad. <a href="http://www.synaptics.com/en/clickpad.php">"ClickPad" is
a trademark by Synaptics Inc.</a> but for simplicity we refer to any
touchpad with the above feature as Clickpad, regardless of the manufacturer.
A clickpad is always marked with the <a
href="https://www.kernel.org/doc/Documentation/input/event-codes.txt">INPUT_PROP_BUTTONPAD</a> property.
To perform a right-click on a Clickpad, libinput provides @ref
software_buttons and @ref clickfinger.
In the page below, the term "click" shall refer to a physical button press
and/or release of the touchpad, the term "button event" refers to the events
generated by libinput and passed to the caller in response to a click.
@section software_buttons Software button areas
On most clickpads, this is the default behavior. The bottom of the touchpad
is split in the middle to generate left or right button events on click. The
height of the button area depends on the hardware but is usually around
10mm.
@dot
digraph G {
clickpad [
shape = "record";
label = "{\nMain\nArea\n\n|{LEFT|RIGHT}}";
]
}
@enddot
Left, right and middle button events can be triggered as follows:
- if a finger is in the main area or the left button area, a click generates
left button events.
- if a finger is in the right area, a click generates right button events.
- if there is a finger in both the left and right button area, a click
generates middle button events.
If fingers are down in the main area in addition to fingers in the
left or right button area, those fingers are are ignored.
A release event always releases the buttons logically down, regardless of
the current finger position
The movement of a finger can alter the button area behavior:
- if a finger starts in the main area and moves into the software button
area, the software buttons do not apply to that finger
- a finger in the software button area does not move the pointer
- if a finger moves out out of the button area it will control the pointer
if it's the first finger in the main area
- once a finger has moved out of the button area, it cannot move back in and
trigger a right or middle button event
@section clickfinger Clickfinger behavior
This is the default behavior on Apple touchpads.
Here, a left, right, middle button event is generated when one, two, or
three fingers are held down on the touchpad when a physical click is
generated. The location of the fingers does not matter and there are no
software-defined button areas.
The Xorg synaptics driver uses 30% of the touchpad dimensions as threshold,
libinput does not have this restriction. If two fingers are on the pad
while clicking, that is a two-finger click.
@section special_clickpads Special Clickpads
The Lenovo *40 series laptops have a clickpad that provides two software button sections, one at
the top and one at the bottom. See @ref t440_support "Lenovo *40 series touchpad support"
for details on the top software button.
Some Clickpads, notably some Cypress ones, perform right button detection in
firmware and appear to userspace as if the touchpad had physical buttons.
While physically clickpads, these are not handled by the software and
treated like traditional touchpads.
*/

View file

@ -0,0 +1,39 @@
/**
@page udev_config Static device configuration via udev
libinput supports some static configuration through udev properties.
These propertiesare read when the device is initially added
to libinput's device list, i.e. before the @ref
LIBINPUT_EVENT_DEVICE_ADDED event is generated.
The following udev properties are supported:
<dl>
<dt>LIBINPUT_CALIBRATION_MATRIX</dt>
<dd>Sets the calibration matrix, see
libinput_device_config_calibration_get_default_matrix(). If unset,
defaults to the identity matrix.</dd>
<dt>ID_SEAT</dt>
<dd>Assigns the physical seat for this device. See
libinput_seat_get_physical_name(). Defaults to "seat0".</dd>
<dt>WL_SEAT</dt>
<dd>Assigns the logical seat for this device. See
libinput_seat_get_logical_name()
context. Defaults to "default".</dd>
<dt>MOUSE_DPI</dt>
<dd>HW resolution and sampling frequency of a relative pointer device.
See @ref motion_normalization for details.
</dd>
<dt>MOUSE_WHEEL_CLICK_ANGLE</dt>
<dd>The angle in degrees for each click on a mouse wheel. See
libinput_pointer_get_axis_source() for details.
</dd>
</dl>
Below is an example udev rule to assign "seat1" to a device from vendor
0x012a with the model ID of 0x034b.
@code
ACTION=="add|change", KERNEL=="event[0-9]*", ENV{ID_VENDOR_ID}=="012a", \
ENV{ID_MODEL_ID}=="034b", ENV{ID_SEAT}="seat1"
@endcode
*/

View file

@ -0,0 +1,29 @@
digraph seats_libinput
{
rankdir="BT";
node [
shape="box";
]
ctx1 [label="libinput context 1"; URL="\ref libinput"];
ctx2 [label="libinput context 2"; URL="\ref libinput"];
seat0 [ label="seat phys 0 logical A"];
seat1 [ label="seat phys 0 logical B"];
seat2 [ label="seat phys 1 logical C"];
dev1 [label="device 'Foo'"];
dev2 [label="device 'Bar'"];
dev3 [label="device 'Spam'"];
dev4 [label="device 'Egg'"];
ctx1 -> dev1
ctx1 -> dev2
ctx1 -> dev3
ctx2 -> dev4
dev1 -> seat0
dev2 -> seat0
dev3 -> seat1
dev4 -> seat2
}

51
doc/dot/seats-sketch.gv Normal file
View file

@ -0,0 +1,51 @@
digraph seats
{
rankdir="BT";
node [
shape="box";
]
kernel [label="Kernel"];
event0 [URL="\ref libinput_event"];
event1 [URL="\ref libinput_event"];
event2 [URL="\ref libinput_event"];
event3 [URL="\ref libinput_event"];
pseat0 [label="phys seat0"; URL="\ref libinput_seat_get_physical_name"];
pseat1 [label="phys seat1"; URL="\ref libinput_seat_get_physical_name"];
lseatA [label="logical seat A"; URL="\ref libinput_seat_get_logical_name"];
lseatB [label="logical seat B"; URL="\ref libinput_seat_get_logical_name"];
lseatC [label="logical seat C"; URL="\ref libinput_seat_get_logical_name"];
ctx1 [label="libinput context 1"; URL="\ref libinput"];
ctx2 [label="libinput context 2"; URL="\ref libinput"];
dev1 [label="device 'Foo'"];
dev2 [label="device 'Bar'"];
dev3 [label="device 'Spam'"];
dev4 [label="device 'Egg'"];
kernel -> event0
kernel -> event1
kernel -> event2
kernel -> event3
event0 -> pseat0
event1 -> pseat0
event2 -> pseat0
event3 -> pseat1
pseat0 -> ctx1
pseat1 -> ctx2
ctx1 -> lseatA
ctx1 -> lseatB
ctx2 -> lseatC
lseatA -> dev1
lseatA -> dev2
lseatB -> dev3
lseatC -> dev4
}

View file

@ -842,7 +842,8 @@ EXAMPLE_RECURSIVE = NO
# that contain images that are to be included in the documentation (see the
# \image command).
IMAGE_PATH =
IMAGE_PATH = @top_srcdir@/doc/svg \
@top_srcdir@/doc/dot
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
@ -1421,7 +1422,7 @@ FORMULA_TRANSPARENT = YES
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
USE_MATHJAX = NO
USE_MATHJAX = YES
# When MathJax is enabled you can set the default output format to be used for
# the MathJax output. See the MathJax site (see:
@ -2250,7 +2251,7 @@ DOT_PATH =
# command).
# This tag requires that the tag HAVE_DOT is set to YES.
DOTFILE_DIRS =
DOTFILE_DIRS = @top_srcdir@/doc/dot
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile

View file

@ -0,0 +1,54 @@
/**
@page motion_normalization Normalization of relative motion
Most relative input devices generate input in so-called "mickeys". A
mickey is in device-specific units that depend on the resolution
of the sensor. Most optical mice use sensors with 1000dpi resolution, but
some devices range from 100dpi to well above 8000dpi.
Without a physical reference point, a relative coordinate cannot be
interpreted correctly. A delta of 10 mickeys may be a millimeter of
physical movement or 10 millimeters, depending on the sensor. This
affects pointer acceleration in libinput and interpretation of relative
coordinates in callers.
libinput normalizes all relative input to a physical resolution of
1000dpi, the same delta from two different devices thus represents the
same physical movement of those two devices (within sensor error
margins).
Devices usually do not advertise their resolution and libinput relies on
the udev property <b>MOUSE_DPI</b> for this information. This property is usually
set via the <a
href="http://cgit.freedesktop.org/systemd/systemd/tree/hwdb/70-mouse.hwdb">udev hwdb</a>.
The format of the property for single-resolution mice is:
@code
MOUSE_DPI=resolution@frequency
@endcode
The resolution is in dots per inch, the frequency in Hz.
The format of the property for multi-resolution mice may list multiple
resolutions and frequencies:
@code
MOUSE_DPI=r1@f1 *r2@f2 r3@f3
@endcode
The default frequency must be pre-fixed with an asterisk.
For example, these two properties are valid:
@code
MOUSE_DPI=800@125
MOUSE_DPI=400@125 800@125 *1000@500 5500@500
@endcode
The behavior for a malformed property is undefined.
If the property is unset, libinput assumes the resolution is 1000dpi.
Note that HW does not usually provide information about run-time
resolution changes, libinput will thus not detect when a resolution
changes to the non-default value.
*/

52
doc/palm-detection.dox Normal file
View file

@ -0,0 +1,52 @@
/**
@page palm_detection Palm detection
Palm detection tries to identify accidental touches while typing.
On most laptops typing on the keyboard generates accidental touches on the
touchpad with the palm (usually the area below the thumb). This can lead to
cursor jumps or accidental clicks.
Interference from a palm depends on the size of the touchpad and the position
of the user's hand. Data from touchpads showed that almost all palm events on a
Lenovo T440 happened in the left-most and right-most 5% of the touchpad. The
T440 series has one of the largest touchpads, other touchpads are less
affected by palm touches.
@section palm_exclusion_zones Palm exclusion zones
libinput enables palm detection on the edge of the touchpad. Two exclusion
zones are defined on the left and right edge of the touchpad.
If a touch starts in the exclusion zone, it is considered a palm and the
touch point is ignored. However, for fast cursor movements across the
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.
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,
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.
@image html palm-detection.svg
@section trackpoint-disabling Palm detection during trackpoint use
If a device provides a <a
href="http://en.wikipedia.org/wiki/Pointing_stick">trackpoint</a>, it is
usually located above the touchpad. This increases the likelyhood of
accidental touches whenever the trackpoint is used.
libinput disables the touchpad whenver it detects trackpoint activity for a
certain timeout until after trackpoint activity stops. Touches generated
during this timeout will not move the pointer, and touches started during
this timeout will likewise not move the pointer (allowing for a user to rest
the palm on the touchpad while using the trackstick).
If the touchpad is disabled, the @ref t440_support "top software buttons"
remain enabled.
*/

50
doc/scrolling.dox Normal file
View file

@ -0,0 +1,50 @@
/**
@page scrolling Scrolling
libinput supports three different types of scrolling behavior.
@section twofinger_scrolling Two-finger scrolling
The default on two-finger capable touchpads (almost all modern touchpads are
capable of detecting two fingers). Scrolling is triggered by two fingers
being placed on the surface of the touchpad, then moving those fingers
vertically or horizontally.
@image html twofinger-scrolling.svg "Vertical and horizontal two-finger scrolling"
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.
@section edge_scrolling Edge scrolling
On some touchpads, edge scrolling is available, triggered by moving a single
finger along the right edge (vertical scroll) or bottom edge (horizontal
scroll).
@image html edge-scrolling.svg "Vertical and horizontal edge scrolling"
Due to the layout of the edges, diagonal scrolling is not possible.
Edge scrolling conflicts with @ref clickpad_softbuttons and is
not usually available on clickpads.
@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
<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
button for button scrolling (even when the touchpad is disabled).
*/

73
doc/seats.dox Normal file
View file

@ -0,0 +1,73 @@
/**
@page seats Seats
Each device in libinput is assigned to one seat.
A seat has two identifiers, the physical name and the logical name. The
physical name is summarized as the list of devices a process on the same
physical seat has access to. The logical seat name is the seat name for a
logical group of devices. A compositor may use that to create additonal
seats as independent device sets. Alternatively, a compositor may limit
itself to a single logical seat, leaving a second compositor to manage
devices on the other logical seats.
@section Overview
Below is an illustration of how physical seats and logical seats interact:
@dotfile seats-sketch.gv
The devices "Foo", "Bar" and "Spam" share the same physical seat and are
thus available in the same libinput context. Only "Foo" and "Bar" share the
same logical seat. The device "Egg" is not available in the libinput context
associated with the physical seat 0.
The above graph is for illustration purposes only. In libinput, a struct
@ref libinput_seat comprises both physical seat and logical seat. From a
caller's point-of-view the above device layout is presented as:
@dotfile seats-sketch-libinput.gv
Thus, devices "Foo" and "Bar" both reference the same struct @ref
libinput_seat, all other devices reference their own respective seats.
@section seats_and_features The effect of seat assignment
A logical set is interprested as a group of devices that usually belong to a
single user that interacts with a computer. Thus, the devices are
semantically related. This means for devices within the same logical seat:
- if the same button is pressed on different devices, the button should only
be considered logically pressed once.
- if the same button is released on one device, the button should be
considered logically down if still down on another device.
- if two different buttons or keys are pressed on different devices, the
logical state is that of both buttons/keys down.
- if a button is pressed on one device and another device moves, this should
count as dragging.
- if two touches are down on different devices, the logical state is that of
two touches down.
libinput provides functions to aid with the above:
libinput_event_pointer_get_seat_button_count(),
libinput_event_keyboard_get_seat_key_count(), and
libinput_event_touch_get_seat_slot().
Internally, libinput counts devices within the same logical seat as related.
Cross-device features only activate if all required devices are in the same
logical seat. For example, libinput will only activate the top software
buttons (see @ref t440_support) if both trackstick and touchpad are assigned
to the same logical seat.
@section changing_seats Changing seats
A device may change the logical seat it is assigned to at runtime with
libinput_device_set_seat_logical_name(). The physical seat is immutable and
may not be changed.
Changing the logical seat for a device is equivalent to unplugging the
device and plugging it back in with the new logical seat. No device state
carries over across a logical seat change.
*/

View file

@ -0,0 +1,292 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="393"
height="438.42642"
id="svg2">
<defs
id="defs4">
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lend"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
id="path3710"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lstart"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(0.8,0,0,0.8,10,0)"
id="path3707"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lstart-4"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(0.8,0,0,0.8,10,0)"
id="path3707-7"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lend-2"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
id="path3710-3"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
</defs>
<g
transform="translate(-34.54288,-505.04248)"
id="layer3"
style="display:inline">
<rect
width="58"
height="58"
x="386"
y="65.208893"
transform="translate(-343.95712,527.33359)"
id="rect11748"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="58"
height="58"
x="122.04288"
y="592.54248"
id="rect11778"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="58"
height="58"
x="202.04288"
y="592.54248"
id="rect11780"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="58"
height="58"
x="282.04288"
y="592.54248"
id="rect11782"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="58"
height="58"
x="362.04288"
y="592.54248"
id="rect11784"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="58"
height="58"
x="82.042877"
y="512.54248"
id="rect11786"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="58"
height="58"
x="162.04288"
y="512.54248"
id="rect11788"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="58"
height="58"
x="242.04288"
y="512.54248"
id="rect11790"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="58"
height="58"
x="322.04288"
y="512.54248"
id="rect11792"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<text
x="57.815342"
y="633.93506"
id="text11796"
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="57.815342"
y="633.93506"
id="tspan11798"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">C</tspan></text>
<text
x="137.81534"
y="633.93506"
id="text11800"
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="137.81534"
y="633.93506"
id="tspan11802"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">V</tspan></text>
<text
x="217.81534"
y="633.93506"
id="text11804"
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="217.81534"
y="633.93506"
id="tspan11806"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">B</tspan></text>
<text
x="297.81534"
y="633.93506"
id="text11808"
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="297.81534"
y="633.93506"
id="tspan11810"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">N</tspan></text>
<text
x="377.81534"
y="633.93506"
id="text11812"
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="377.81534"
y="633.93506"
id="tspan11814"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">M</tspan></text>
<text
x="337.81534"
y="553.93506"
id="text11816"
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="337.81534"
y="553.93506"
id="tspan11818"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">J</tspan></text>
<text
x="257.81534"
y="553.93506"
id="text11820"
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="257.81534"
y="553.93506"
id="tspan11822"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">H</tspan></text>
<text
x="177.81534"
y="553.93506"
id="text11824"
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="177.81534"
y="553.93506"
id="tspan11826"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">G</tspan></text>
<text
x="97.815338"
y="553.93506"
id="text11828"
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="97.815338"
y="553.93506"
id="tspan11830"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">F</tspan></text>
<path
d="m 590.15063,48.238251 a 12.628045,12.628045 0 1 1 -25.25609,0 12.628045,12.628045 0 1 1 25.25609,0 z"
transform="matrix(1.5,0,0,1.5,-635.24402,504.05633)"
id="path11832"
style="color:#000000;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:#e50000;stroke-width:2.33333325;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<g
transform="matrix(0.98314313,0.18283763,-0.18283763,0.98314313,38.34468,-177.34601)"
id="g3663-9-5">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path2820-6-6"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path2824-1-1"
style="color:#000000;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7-1-4"
style="opacity:0.92000002;color:#000000;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
</g>
<g
transform="translate(-34.54288,-505.04248)"
id="layer1"
style="display:inline">
<rect
width="135"
height="63"
x="55.166245"
y="782.95221"
id="rect11656"
style="color:#000000;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="135"
height="63"
x="258.97995"
y="782.95221"
id="rect11656-7"
style="color:#000000;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="64.642883"
height="68.303299"
x="192.25995"
y="778.95221"
id="rect11656-1"
style="color:#000000;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.99999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<g
transform="matrix(-0.96540637,0.18283763,0.17953908,0.98314313,431.3343,62.30631)"
id="g3663-9">
<path
d="m 359.78441,828.85231 -28.35583,-65.06155 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 47.45034,95.30837 47.45034,95.30837 l -63.38022,25.11033 z"
id="path2820-6"
style="fill:none;stroke:#000000;stroke-width:1.00914431px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
d="m 360.32021,827.78041 c 0,0 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 49.57778,98.62829 49.57778,98.62829 0,0 -61.86167,24.42554 -61.86167,24.42554 z"
id="path2824-1"
style="color:#000000;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.00201829;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7-1"
style="opacity:0.92000002;color:#000000;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
<path
d="m 232.30267,551.36809 0,46.72376"
id="path11849"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow1Lstart-4);marker-end:url(#Arrow1Lend-2)" />
<path
d="m 255.66455,574.72997 -46.72376,0"
id="path12611"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow1Lstart-4);marker-end:url(#Arrow1Lend-2)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

126
doc/svg/edge-scrolling.svg Normal file
View file

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="865.5542"
height="341.20889"
id="svg2">
<defs
id="defs4">
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lend"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
id="path3710"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lstart"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(0.8,0,0,0.8,10,0)"
id="path3707"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lstart-4"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(0.8,0,0,0.8,10,0)"
id="path3707-7"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lend-2"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
id="path3710-3"
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" />
<path
d="m 21.360531,774.95272 0,-196.97975"
id="path8036"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend)" />
<g
transform="matrix(0.98314313,0.18283763,-0.18283763,0.98314313,-175.46474,-125.7572)"
id="g3663-9-5">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path2820-6-6"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path2824-1-1"
style="color:#000000;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7-1-4"
style="opacity:0.92000002;color:#000000;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
</g>
<g
transform="translate(343.95712,-527.33359)"
id="layer1"
style="display:inline">
<rect
width="386.42856"
height="261.42856"
x="131.56854"
y="530.93359"
id="rect2858"
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" />
<path
d="m 455.56456,762.87232 c 0,0 -235.01231,1.01015 -235.01231,1.01015"
id="path3702"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lstart)" />
<g
transform="matrix(0.98314313,0.18283763,-0.18283763,0.98314313,132.81621,-12.620089)"
id="g3663-9">
<path
d="m 359.78441,828.85231 -28.35583,-65.06155 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 47.45034,95.30837 47.45034,95.30837 l -63.38022,25.11033 z"
id="path2820-6"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
d="m 360.32021,827.78041 c 0,0 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 49.57778,98.62829 49.57778,98.62829 0,0 -61.86167,24.42554 -61.86167,24.42554 z"
id="path2824-1"
style="color:#000000;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7-1"
style="opacity:0.92000002;color:#000000;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7 KiB

122
doc/svg/palm-detection.svg Normal file
View file

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="393.62857"
height="268.62857"
id="svg2">
<defs
id="defs4">
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lend"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
id="path3710"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lstart"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(0.8,0,0,0.8,10,0)"
id="path3707"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lstart-4"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(0.8,0,0,0.8,10,0)"
id="path3707-7"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lend-2"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
id="path3710-3"
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>
<g
transform="translate(343.95712,-527.33359)"
id="layer1"
style="display:inline" />
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="865.5542"
height="327.68497"
id="svg2">
<defs
id="defs4">
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lend"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
id="path3710"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lstart"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(0.8,0,0,0.8,10,0)"
id="path3707"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lstart-4"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(0.8,0,0,0.8,10,0)"
id="path3707-7"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lend-2"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
id="path3710-3"
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">
<g
id="g10793">
<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" />
<path
d="m -142.28418,718.38417 0,-150.51273"
id="path8036"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend)" />
<g
transform="matrix(0.98196551,0.12493315,-0.14261338,1.1209308,-351.0514,-195.9045)"
id="g3663-7">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path2820-9"
style="fill:none;stroke:#000000;stroke-width:0.94553083px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path2824-3"
style="color:#000000;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.00189106;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7-0"
style="opacity:0.92000002;color:#000000;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
<g
transform="matrix(0.98314313,0.18283763,-0.18283763,0.98314313,-400.72876,-98.483075)"
id="g3663-9-5">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path2820-6-6"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path2824-1-1"
style="color:#000000;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7-1-4"
style="opacity:0.92000002;color:#000000;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
</g>
</g>
<g
transform="translate(343.95712,-527.33359)"
id="layer1"
style="display:inline">
<g
transform="translate(42.253088,-4.1631891)"
id="g10781">
<rect
width="386.42856"
height="261.42856"
x="89.315453"
y="535.0968"
id="rect2858"
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" />
<path
d="m 400.17949,657.93904 c 0,0 -235.01231,1.01015 -235.01231,1.01015"
id="path3702"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lstart)" />
<g
transform="matrix(0.98196551,0.12493315,-0.14261338,1.1209308,78.621186,-191.74129)"
id="g3663">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path2820"
style="fill:none;stroke:#000000;stroke-width:0.94553083px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path2824"
style="color:#000000;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.00189106;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7"
style="opacity:0.92000002;color:#000000;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
<g
transform="matrix(0.98314313,0.18283763,-0.18283763,0.98314313,28.943818,-94.319866)"
id="g3663-9">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path2820-6"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path2824-1"
style="color:#000000;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7-1"
style="opacity:0.92000002;color:#000000;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

74
doc/t440-support.dox Normal file
View file

@ -0,0 +1,74 @@
/**
@page t440_support Lenovo *40 series touchpad support
The Lenovo *40 series emulates trackstick buttons on the top part of the
touchpads.
@section t440_support_overview Overview
The Lenovo *40 series introduced a new type of touchpad. Previously, all
laptops had a separate set of physical buttons for the
<a href="http://en.wikipedia.org/wiki/Pointing_stick">trackstick</a>. This
series removed these buttons, relying on a software emulation of the top
section of the touchpad. This is visually marked on the trackpad itself,
approximately like this:
@dot
digraph G {
subgraph cluster_0 {
margin="0";
clickpad [
shape = "record";
color = "none";
label = "{{LLLLLLLLLL|MMMMM|RRRRRRRRR}|\n\n\n\n\n\n\n\n|{LLLLLLLL| |RRRRRRRR}}";
]
}
}
@enddot
This page only covers the top software buttons, the bottom button behavior
is covered in @ref clickpad_softbuttons "Clickpad software buttons".
Clickpads with a top button area are marked with the <a
href="https://www.kernel.org/doc/Documentation/input/event-codes.txt">INPUT_PROP_TOPBUTTONPAD</a>
property.
@section t440_support_btn_size Size of the buttons
The line of the buttons is 5mm from the top edge of the touchpad,
measurements of button presses showed that the size of the buttons needs to
be approximately 10mm high to work reliable (especially when using the
thumb to press the button).
The width of the left and right buttons is approximately 42% of the
touchpad's width, the middle button is centered and should be assigned
approximately 16% of the touchpad width.
@section t440_support_btn_behavior Button behavior
Movement in the top button area does not generate pointer movement. These
buttons are not replacement buttons for the bottom button area but have
their own behavior.
Semantically attached to the trackstick device, libinput re-routes events
from these buttons to appear through the trackstick device. The top button
areas work even if the touchpad is disabled but will be disabled when the
trackstick device is disabled.
If the finger starts inside the top area and moves outside the button area
the finger is treated as dead and must be lifted to generate future buttons.
Likewise, movement into the top button area does not trigger button events, a click
has to start inside this area to take effect.
@section t440_support_identification Kernel support
The firmware on touchpads providing top software buttons is buggy and
announces wrong ranges. <a href="https://lkml.org/lkml/2014/3/7/722">Kernel
patches</a> are required; these fixes are available in kernels
3.14.1, 3.15 and later but each touchpad needs a separate fix.
For a complete list of supported touchpads check <a
href="http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/input/mouse/synaptics.c">the
kernel source</a> (search for "topbuttonpad_pnp_ids").
*/

48
doc/tapping.dox Normal file
View file

@ -0,0 +1,48 @@
/**
@page tapping Tap-to-click behaviour
"Tapping" or "tap-to-click" is the name given to the behavior where a short
finger touch down/up sequence maps into a button click. This is most
commonly used on touchpads, but may be available on other devices.
libinput implements tapping for one, two, and three fingers, where supported
by the hardware, and maps those taps into a left, right, and middle button
click, respectively. Not all devices support three fingers, libinput will
support tapping up to whatever is supported by the hardware. libinput does
not support four-finger taps or any tapping with more than four fingers,
even though some hardware can distinguish between that many fingers.
Tapping is **disabled** by default, see [this
commit](http://cgit.freedesktop.org/wayland/libinput/commit/?id=2219c12c3aa45b80f235e761e87c17fb9ec70eae)
because:
- if you don't know that tapping is a thing (or enabled by default), you get
spurious button events that make the desktop feel buggy.
- if you do know what tapping is and you want it, you usually know where to
enable it, or at least you can search for it.
Tapping can be enabled on a per-device basis. See
libinput_device_config_tap_set_enabled() for details.
@section tapndrag Tap-and-drag
libinput also supports "tap-and-drag" where a tap immediately followed by a
finger down and that finger being held down emulates a button press. Moving
the finger around can thus drag the selected item on the screen. Lifting the
finger and putting it back down immediately (i.e. within the timeout) will
continue the dragging process, so that multiple touchpad-widths of distance
can be covered easily. If two-fingers are supported by the hardware, a
second finger can be used to drag while the first is held in-place.
@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:
- the touch down and touch up must happen within an implementation-defined timeout
- if a finger moves more than an implementation-defined distance while in contact, it's not a tap
- tapping within @ref clickpad_softbuttons "clickpad software buttons" may not trigger an event
- a tap not meeting required pressure thresholds can be ignored as accidental touch
- a tap exceeding certain pressure thresholds can be ignored (see @ref
palm_detection)
- a tap on the edges of the touchpad can usually be ignored (see @ref
palm_detection)
*/

View file

@ -38,6 +38,7 @@ libinput_la_CFLAGS = -I$(top_srcdir)/include \
$(LIBUDEV_CFLAGS) \
$(LIBEVDEV_CFLAGS) \
$(GCC_CFLAGS)
EXTRA_libinput_la_DEPENDENCIES = $(srcdir)/libinput.sym
libinput_util_la_SOURCES = \
libinput-util.c \

View file

@ -69,7 +69,7 @@ tp_edge_scroll_set_state(struct tp_dispatch *tp,
{
libinput_timer_cancel(&t->scroll.timer);
t->scroll.state = state;
t->scroll.edge_state = state;
switch (state) {
case EDGE_SCROLL_TOUCH_STATE_NONE:
@ -207,7 +207,7 @@ tp_edge_scroll_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum scroll_event event)
{
switch (t->scroll.state) {
switch (t->scroll.edge_state) {
case EDGE_SCROLL_TOUCH_STATE_NONE:
tp_edge_scroll_handle_none(tp, t, event);
break;
@ -325,7 +325,10 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
if (t->scroll.direction != -1) {
/* Send stop scroll event */
pointer_notify_axis(device, time,
t->scroll.direction, 0.0);
AS_MASK(t->scroll.direction),
LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
0.0, 0.0,
0, 0);
t->scroll.direction = -1;
}
continue;
@ -347,7 +350,11 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
if (fabs(*delta) < t->scroll.threshold)
continue;
pointer_notify_axis(device, time, axis, *delta);
pointer_notify_axis(device, time,
AS_MASK(axis),
LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
dx, dy,
0, 0);
t->scroll.direction = axis;
tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_POSTED);
@ -365,7 +372,10 @@ tp_edge_scroll_stop_events(struct tp_dispatch *tp, uint64_t time)
tp_for_each_touch(tp, t) {
if (t->scroll.direction != -1) {
pointer_notify_axis(device, time,
t->scroll.direction, 0.0);
AS_MASK(t->scroll.direction),
LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
0.0, 0.0,
0.0, 0.0);
t->scroll.direction = -1;
}
}
@ -374,5 +384,5 @@ tp_edge_scroll_stop_events(struct tp_dispatch *tp, uint64_t time)
int
tp_edge_scroll_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
{
return t->scroll.state == EDGE_SCROLL_TOUCH_STATE_AREA;
return t->scroll.edge_state == EDGE_SCROLL_TOUCH_STATE_AREA;
}

View file

@ -664,8 +664,9 @@ tp_tap_config_set_enabled(struct libinput_device *device,
enum libinput_config_tap_state enabled)
{
struct evdev_dispatch *dispatch = ((struct evdev_device *) device)->dispatch;
struct tp_dispatch *tp = container_of(dispatch, tp, base);
struct tp_dispatch *tp = NULL;
tp = container_of(dispatch, tp, base);
tp_tap_enabled_update(tp, tp->tap.suspended,
(enabled == LIBINPUT_CONFIG_TAP_ENABLED),
libinput_now(device->seat->libinput));

View file

@ -435,7 +435,39 @@ tp_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
tp_filter_motion(tp, &dx, &dy, NULL, NULL, time);
evdev_post_scroll(tp->device, time, dx, dy);
evdev_post_scroll(tp->device,
time,
LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
dx, dy);
tp->scroll.twofinger_state = TWOFINGER_SCROLL_STATE_ACTIVE;
}
static void
tp_twofinger_stop_scroll(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *t, *ptr = NULL;
int nfingers_down = 0;
evdev_stop_scroll(tp->device,
time,
LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
/* If we were scrolling and now there's exactly 1 active finger,
switch back to pointer movement */
if (tp->scroll.twofinger_state == TWOFINGER_SCROLL_STATE_ACTIVE) {
tp_for_each_touch(tp, t) {
if (tp_touch_active(tp, t)) {
nfingers_down++;
if (ptr == NULL)
ptr = t;
}
}
if (nfingers_down == 1)
tp_set_pointer(tp, ptr);
}
tp->scroll.twofinger_state = TWOFINGER_SCROLL_STATE_NONE;
}
static int
@ -458,13 +490,14 @@ tp_twofinger_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
nfingers_down++;
}
if (nfingers_down != 2) {
evdev_stop_scroll(tp->device, time);
return 0;
if (nfingers_down == 2) {
tp_post_twofinger_scroll(tp, time);
return 1;
}
tp_post_twofinger_scroll(tp, time);
return 1;
tp_twofinger_stop_scroll(tp, time);
return 0;
}
static void
@ -504,7 +537,7 @@ tp_stop_scroll_events(struct tp_dispatch *tp, uint64_t time)
case LIBINPUT_CONFIG_SCROLL_NO_SCROLL:
break;
case LIBINPUT_CONFIG_SCROLL_2FG:
evdev_stop_scroll(tp->device, time);
tp_twofinger_stop_scroll(tp, time);
break;
case LIBINPUT_CONFIG_SCROLL_EDGE:
tp_edge_scroll_stop_events(tp, time);
@ -818,7 +851,9 @@ tp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data)
return;
if (!tp->sendevents.trackpoint_active) {
evdev_stop_scroll(tp->device, time);
evdev_stop_scroll(tp->device,
time,
LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
tp_tap_suspend(tp, time);
tp->sendevents.trackpoint_active = true;
}
@ -1275,7 +1310,7 @@ tp_change_to_left_handed(struct evdev_device *device)
{
struct tp_dispatch *tp = (struct tp_dispatch *)device->dispatch;
if (device->buttons.want_left_handed == device->buttons.left_handed)
if (device->left_handed.want_enabled == device->left_handed.enabled)
return;
if (tp->buttons.state & 0x3) /* BTN_LEFT|BTN_RIGHT */
@ -1284,7 +1319,7 @@ tp_change_to_left_handed(struct evdev_device *device)
/* tapping and clickfinger aren't affected by left-handed config,
* so checking physical buttons is enough */
device->buttons.left_handed = device->buttons.want_left_handed;
device->left_handed.enabled = device->left_handed.want_enabled;
}
struct model_lookup_t {

View file

@ -117,6 +117,11 @@ enum tp_edge_scroll_touch_state {
EDGE_SCROLL_TOUCH_STATE_AREA,
};
enum tp_twofinger_scroll_state {
TWOFINGER_SCROLL_STATE_NONE,
TWOFINGER_SCROLL_STATE_ACTIVE,
};
struct tp_motion {
int32_t x;
int32_t y;
@ -165,7 +170,7 @@ struct tp_touch {
} tap;
struct {
enum tp_edge_scroll_touch_state state;
enum tp_edge_scroll_touch_state edge_state;
uint32_t edge;
int direction;
double threshold;
@ -238,6 +243,7 @@ struct tp_dispatch {
enum libinput_config_scroll_method method;
int32_t right_edge;
int32_t bottom_edge;
enum tp_twofinger_scroll_state twofinger_state;
} scroll;
enum touchpad_event queued;

View file

@ -40,7 +40,7 @@
#include "filter.h"
#include "libinput-private.h"
#define DEFAULT_AXIS_STEP_DISTANCE 10
#define DEFAULT_WHEEL_CLICK_ANGLE 15
#define DEFAULT_MIDDLE_BUTTON_SCROLL_TIMEOUT 200
enum evdev_key_type {
@ -119,8 +119,8 @@ evdev_pointer_notify_button(struct evdev_device *device,
pointer_notify_button(&device->base, time, button, state);
if (state == LIBINPUT_BUTTON_STATE_RELEASED &&
device->buttons.change_to_left_handed)
device->buttons.change_to_left_handed(device);
device->left_handed.change_to_enabled)
device->left_handed.change_to_enabled(device);
if (state == LIBINPUT_BUTTON_STATE_RELEASED &&
device->scroll.change_scroll_method)
@ -222,6 +222,7 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time)
hw_is_key_down(device, device->scroll.button)) {
if (device->scroll.button_scroll_active)
evdev_post_scroll(device, time,
LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
dx_unaccel, dy_unaccel);
break;
}
@ -394,7 +395,8 @@ evdev_button_scroll_button(struct evdev_device *device,
} else {
libinput_timer_cancel(&device->scroll.timer);
if (device->scroll.button_scroll_active) {
evdev_stop_scroll(device, time);
evdev_stop_scroll(device, time,
LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
device->scroll.button_scroll_active = false;
} else {
/* If the button is released quickly enough emit the
@ -537,16 +539,24 @@ evdev_process_absolute_motion(struct evdev_device *device,
static void
evdev_notify_axis(struct evdev_device *device,
uint64_t time,
enum libinput_pointer_axis axis,
double value)
uint32_t axes,
enum libinput_pointer_axis_source source,
double x, double y,
double x_discrete, double y_discrete)
{
if (device->scroll.natural_scrolling_enabled)
value *= -1;
if (device->scroll.natural_scrolling_enabled) {
x *= -1;
y *= -1;
x_discrete *= -1;
y_discrete *= -1;
}
pointer_notify_axis(&device->base,
time,
axis,
value);
axes,
source,
x, y,
x_discrete, y_discrete);
}
static inline void
@ -571,16 +581,24 @@ evdev_process_relative(struct evdev_device *device,
evdev_notify_axis(
device,
time,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
-1 * e->value * DEFAULT_AXIS_STEP_DISTANCE);
AS_MASK(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
LIBINPUT_POINTER_AXIS_SOURCE_WHEEL,
0.0,
-1 * e->value * device->scroll.wheel_click_angle,
0.0,
-1 * e->value);
break;
case REL_HWHEEL:
evdev_flush_pending_event(device, time);
evdev_notify_axis(
device,
time,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
e->value * DEFAULT_AXIS_STEP_DISTANCE);
AS_MASK(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
LIBINPUT_POINTER_AXIS_SOURCE_WHEEL,
e->value * device->scroll.wheel_click_angle,
0.0,
e->value,
0.0);
break;
}
}
@ -804,13 +822,13 @@ evdev_left_handed_has(struct libinput_device *device)
static void
evdev_change_to_left_handed(struct evdev_device *device)
{
if (device->buttons.want_left_handed == device->buttons.left_handed)
if (device->left_handed.want_enabled == device->left_handed.enabled)
return;
if (evdev_any_button_down(device))
return;
device->buttons.left_handed = device->buttons.want_left_handed;
device->left_handed.enabled = device->left_handed.want_enabled;
}
static enum libinput_config_status
@ -818,9 +836,9 @@ evdev_left_handed_set(struct libinput_device *device, int left_handed)
{
struct evdev_device *evdev_device = (struct evdev_device *)device;
evdev_device->buttons.want_left_handed = left_handed ? true : false;
evdev_device->left_handed.want_enabled = left_handed ? true : false;
evdev_device->buttons.change_to_left_handed(evdev_device);
evdev_device->left_handed.change_to_enabled(evdev_device);
return LIBINPUT_CONFIG_STATUS_SUCCESS;
}
@ -832,7 +850,7 @@ evdev_left_handed_get(struct libinput_device *device)
/* return the wanted configuration, even if it hasn't taken
* effect yet! */
return evdev_device->buttons.want_left_handed;
return evdev_device->left_handed.want_enabled;
}
static int
@ -845,14 +863,14 @@ int
evdev_init_left_handed(struct evdev_device *device,
void (*change_to_left_handed)(struct evdev_device *))
{
device->buttons.config_left_handed.has = evdev_left_handed_has;
device->buttons.config_left_handed.set = evdev_left_handed_set;
device->buttons.config_left_handed.get = evdev_left_handed_get;
device->buttons.config_left_handed.get_default = evdev_left_handed_get_default;
device->base.config.left_handed = &device->buttons.config_left_handed;
device->buttons.left_handed = false;
device->buttons.want_left_handed = false;
device->buttons.change_to_left_handed = change_to_left_handed;
device->left_handed.config.has = evdev_left_handed_has;
device->left_handed.config.set = evdev_left_handed_set;
device->left_handed.config.get = evdev_left_handed_get;
device->left_handed.config.get_default = evdev_left_handed_get_default;
device->base.config.left_handed = &device->left_handed.config;
device->left_handed.enabled = false;
device->left_handed.want_enabled = false;
device->left_handed.change_to_enabled = change_to_left_handed;
return 0;
}
@ -1046,7 +1064,7 @@ fallback_dispatch_create(struct libinput_device *device)
dispatch->interface = &fallback_interface;
if (evdev_device->buttons.want_left_handed &&
if (evdev_device->left_handed.want_enabled &&
evdev_init_left_handed(evdev_device,
evdev_change_to_left_handed) == -1) {
free(dispatch);
@ -1233,6 +1251,29 @@ evdev_tag_device(struct evdev_device *device)
device->udev_device);
}
static inline int
evdev_read_wheel_click_prop(struct evdev_device *device)
{
struct libinput *libinput = device->base.seat->libinput;
const char *prop;
int angle = DEFAULT_WHEEL_CLICK_ANGLE;
prop = udev_device_get_property_value(device->udev_device,
"MOUSE_WHEEL_CLICK_ANGLE");
if (prop) {
angle = parse_mouse_wheel_click_angle_property(prop);
if (!angle) {
log_error(libinput,
"Mouse wheel click angle '%s' is present but invalid,"
"using %d degrees instead\n",
device->devname,
DEFAULT_WHEEL_CLICK_ANGLE);
angle = DEFAULT_WHEEL_CLICK_ANGLE;
}
}
return angle;
}
static inline int
evdev_read_dpi_prop(struct evdev_device *device)
{
@ -1444,7 +1485,7 @@ evdev_configure_device(struct evdev_device *device)
has_button ? " button" : "");
/* want left-handed config option */
device->buttons.want_left_handed = true;
device->left_handed.want_enabled = true;
/* want natural-scroll config option */
device->scroll.natural_scrolling_enabled = true;
}
@ -1570,6 +1611,8 @@ evdev_device_create(struct libinput_seat *seat,
device->devname = libevdev_get_name(device->evdev);
device->scroll.threshold = 5.0; /* Default may be overridden */
device->scroll.direction = 0;
device->scroll.wheel_click_angle =
evdev_read_wheel_click_prop(device);
device->dpi = evdev_read_dpi_prop(device);
/* at most 5 SYN_DROPPED log-messages per 30s */
ratelimit_init(&device->syn_drop_limit, 30ULL * 1000, 5);
@ -1775,7 +1818,7 @@ evdev_is_scrolling(const struct evdev_device *device,
assert(axis == LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL ||
axis == LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
return (device->scroll.direction & (1 << axis)) != 0;
return (device->scroll.direction & AS_MASK(axis)) != 0;
}
static inline void
@ -1785,12 +1828,13 @@ evdev_start_scrolling(struct evdev_device *device,
assert(axis == LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL ||
axis == LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
device->scroll.direction |= (1 << axis);
device->scroll.direction |= AS_MASK(axis);
}
void
evdev_post_scroll(struct evdev_device *device,
uint64_t time,
enum libinput_pointer_axis_source source,
double dx,
double dy)
{
@ -1835,39 +1879,35 @@ evdev_post_scroll(struct evdev_device *device,
/* We use the trigger to enable, but the delta from this event for
* the actual scroll movement. Otherwise we get a jump once
* scrolling engages */
if (dy != 0.0 &&
evdev_is_scrolling(device,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
evdev_notify_axis(device,
time,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
dy);
}
if (!evdev_is_scrolling(device,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
dy = 0.0;
if (!evdev_is_scrolling(device,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
dx = 0.0;
if (dx != 0.0 &&
evdev_is_scrolling(device,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) {
if (dx != 0.0 || dy != 0.0)
evdev_notify_axis(device,
time,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
dx);
}
device->scroll.direction,
source,
dx, dy,
0.0, 0.0);
}
void
evdev_stop_scroll(struct evdev_device *device, uint64_t time)
evdev_stop_scroll(struct evdev_device *device,
uint64_t time,
enum libinput_pointer_axis_source source)
{
/* terminate scrolling with a zero scroll event */
if (device->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
if (device->scroll.direction != 0)
pointer_notify_axis(&device->base,
time,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
0);
if (device->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
pointer_notify_axis(&device->base,
time,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
0);
device->scroll.direction,
source,
0.0, 0.0,
0.0, 0.0);
device->scroll.buildup_horizontal = 0;
device->scroll.buildup_vertical = 0;

View file

@ -120,6 +120,9 @@ struct evdev_device {
/* set during device init if we want natural scrolling,
* used at runtime to enable/disable the feature */
bool natural_scrolling_enabled;
/* angle per REL_WHEEL click in degrees */
int wheel_click_angle;
} scroll;
enum evdev_event_type pending_event;
@ -142,15 +145,15 @@ struct evdev_device {
uint8_t key_count[KEY_CNT];
struct {
struct libinput_device_config_left_handed config_left_handed;
struct libinput_device_config_left_handed config;
/* left-handed currently enabled */
bool left_handed;
bool enabled;
/* set during device init if we want left_handed config,
* used at runtime to delay the effect until buttons are up */
bool want_left_handed;
bool want_enabled;
/* Checks if buttons are down and commits the setting */
void (*change_to_left_handed)(struct evdev_device *device);
} buttons;
void (*change_to_enabled)(struct evdev_device *device);
} left_handed;
int dpi; /* HW resolution */
struct ratelimit syn_drop_limit; /* ratelimit for SYN_DROPPED logging */
@ -302,12 +305,15 @@ evdev_init_natural_scroll(struct evdev_device *device);
void
evdev_post_scroll(struct evdev_device *device,
uint64_t time,
enum libinput_pointer_axis_source source,
double dx,
double dy);
void
evdev_stop_scroll(struct evdev_device *device, uint64_t time);
evdev_stop_scroll(struct evdev_device *device,
uint64_t time,
enum libinput_pointer_axis_source source);
void
evdev_device_remove(struct evdev_device *device);
@ -330,7 +336,7 @@ static inline uint32_t
evdev_to_left_handed(struct evdev_device *device,
uint32_t button)
{
if (device->buttons.left_handed) {
if (device->left_handed.enabled) {
if (button == BTN_LEFT)
return BTN_RIGHT;
else if (button == BTN_RIGHT)

View file

@ -308,14 +308,6 @@ create_pointer_accelerator_filter(accel_profile_func_t profile)
return &filter->base;
}
static inline double
calc_penumbral_gradient(double x)
{
x *= 2.0;
x -= 1.0;
return 0.5 + (x * sqrt(1.0 - x * x) + asin(x)) / M_PI;
}
double
pointer_accel_profile_linear(struct motion_filter *filter,
void *data,

View file

@ -291,8 +291,12 @@ pointer_notify_button(struct libinput_device *device,
void
pointer_notify_axis(struct libinput_device *device,
uint64_t time,
enum libinput_pointer_axis axis,
double value);
uint32_t axes,
enum libinput_pointer_axis_source source,
double x,
double y,
double x_discrete,
double y_discrete);
void
touch_notify_touch_down(struct libinput_device *device,

View file

@ -131,6 +131,9 @@ ratelimit_test(struct ratelimit *r)
* use a reasonable default instead. If the property contains multiple DPI
* settings but none flagged as default, we return the last because we're
* lazy and that's a silly way to set the property anyway.
*
* @param prop The value of the udev property (without the MOUSE_DPI=)
* @return The default dpi value on success, 0 on error
*/
int
parse_mouse_dpi_property(const char *prop)
@ -168,3 +171,33 @@ parse_mouse_dpi_property(const char *prop)
}
return dpi;
}
/**
* Helper function to parse the MOUSE_WHEEL_CLICK_ANGLE property from udev.
* Property is of the form:
* MOUSE_WHEEL_CLICK_ANGLE=<integer>
* Where the number indicates the degrees travelled for each click.
*
* We skip preceding whitespaces and parse the first number seen. If
* multiple numbers are specified, we ignore those.
*
* @param prop The value of the udev property (without the MOUSE_WHEEL_CLICK_ANGLE=)
* @return The angle of the wheel (may be negative) or 0 on error.
*/
int
parse_mouse_wheel_click_angle_property(const char *prop)
{
int angle = 0,
nread = 0;
while(*prop != 0 && *prop == ' ')
prop++;
sscanf(prop, "%d%n", &angle, &nread);
if (nread == 0 || angle == 0 || abs(angle) > 360)
return 0;
if (prop[nread] != ' ' && prop[nread] != '\0')
return 0;
return angle;
}

View file

@ -79,6 +79,7 @@ int list_empty(const struct list *list);
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
#define ARRAY_FOR_EACH(_arr, _elem) \
for (size_t _i = 0; _i < ARRAY_LENGTH(_arr) && (_elem = &_arr[_i]); _i++)
#define AS_MASK(v) (1 << (v))
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) > (b)) ? (a) : (b))
@ -321,5 +322,6 @@ void ratelimit_init(struct ratelimit *r, uint64_t ival_ms, unsigned int burst);
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);
#endif /* LIBINPUT_UTIL_H */

View file

@ -59,13 +59,15 @@ struct libinput_event_pointer {
uint32_t time;
double x;
double y;
double x_discrete;
double y_discrete;
double dx_unaccel;
double dy_unaccel;
uint32_t button;
uint32_t seat_button_count;
enum libinput_button_state state;
enum libinput_pointer_axis axis;
double value;
enum libinput_pointer_axis_source source;
uint32_t axes;
};
struct libinput_event_touch {
@ -435,16 +437,69 @@ libinput_event_pointer_get_seat_button_count(
return event->seat_button_count;
}
LIBINPUT_EXPORT enum libinput_pointer_axis
libinput_event_pointer_get_axis(struct libinput_event_pointer *event)
LIBINPUT_EXPORT int
libinput_event_pointer_has_axis(struct libinput_event_pointer *event,
enum libinput_pointer_axis axis)
{
return event->axis;
if (event->base.type == LIBINPUT_EVENT_POINTER_AXIS) {
switch (axis) {
case LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL:
case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL:
return !!(event->axes & AS_MASK(axis));
}
}
return 0;
}
LIBINPUT_EXPORT double
libinput_event_pointer_get_axis_value(struct libinput_event_pointer *event)
libinput_event_pointer_get_axis_value(struct libinput_event_pointer *event,
enum libinput_pointer_axis axis)
{
return event->value;
struct libinput *libinput = event->base.device->seat->libinput;
double value = 0;
if (!libinput_event_pointer_has_axis(event, axis)) {
log_bug_client(libinput, "value requested for unset axis\n");
} else {
switch (axis) {
case LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL:
value = event->x;
break;
case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL:
value = event->y;
break;
}
}
return value;
}
LIBINPUT_EXPORT double
libinput_event_pointer_get_axis_value_discrete(struct libinput_event_pointer *event,
enum libinput_pointer_axis axis)
{
struct libinput *libinput = event->base.device->seat->libinput;
double value = 0;
if (!libinput_event_pointer_has_axis(event, axis)) {
log_bug_client(libinput, "value requested for unset axis\n");
} else {
switch (axis) {
case LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL:
value = event->x_discrete;
break;
case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL:
value = event->y_discrete;
break;
}
}
return value;
}
LIBINPUT_EXPORT enum libinput_pointer_axis_source
libinput_event_pointer_get_axis_source(struct libinput_event_pointer *event)
{
return event->source;
}
LIBINPUT_EXPORT uint32_t
@ -1196,8 +1251,10 @@ pointer_notify_button(struct libinput_device *device,
void
pointer_notify_axis(struct libinput_device *device,
uint64_t time,
enum libinput_pointer_axis axis,
double value)
uint32_t axes,
enum libinput_pointer_axis_source source,
double x, double y,
double x_discrete, double y_discrete)
{
struct libinput_event_pointer *axis_event;
@ -1207,8 +1264,12 @@ pointer_notify_axis(struct libinput_device *device,
*axis_event = (struct libinput_event_pointer) {
.time = time,
.axis = axis,
.value = value,
.x = x,
.y = y,
.source = source,
.axes = axes,
.x_discrete = x_discrete,
.y_discrete = y_discrete,
};
post_device_event(device, time,
@ -1499,6 +1560,13 @@ libinput_next_event_type(struct libinput *libinput)
return event->type;
}
LIBINPUT_EXPORT void
libinput_set_user_data(struct libinput *libinput,
void *user_data)
{
libinput->user_data = user_data;
}
LIBINPUT_EXPORT void *
libinput_get_user_data(struct libinput *libinput)
{
@ -1857,7 +1925,7 @@ libinput_device_config_scroll_get_default_natural_scroll_enabled(struct libinput
}
LIBINPUT_EXPORT int
libinput_device_config_buttons_has_left_handed(struct libinput_device *device)
libinput_device_config_left_handed_is_available(struct libinput_device *device)
{
if (!device->config.left_handed)
return 0;
@ -1866,28 +1934,28 @@ libinput_device_config_buttons_has_left_handed(struct libinput_device *device)
}
LIBINPUT_EXPORT enum libinput_config_status
libinput_device_config_buttons_set_left_handed(struct libinput_device *device,
int left_handed)
libinput_device_config_left_handed_set(struct libinput_device *device,
int left_handed)
{
if (!libinput_device_config_buttons_has_left_handed(device))
if (!libinput_device_config_left_handed_is_available(device))
return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
return device->config.left_handed->set(device, left_handed);
}
LIBINPUT_EXPORT int
libinput_device_config_buttons_get_left_handed(struct libinput_device *device)
libinput_device_config_left_handed_get(struct libinput_device *device)
{
if (!libinput_device_config_buttons_has_left_handed(device))
if (!libinput_device_config_left_handed_is_available(device))
return 0;
return device->config.left_handed->get(device);
}
LIBINPUT_EXPORT int
libinput_device_config_buttons_get_default_left_handed(struct libinput_device *device)
libinput_device_config_left_handed_get_default(struct libinput_device *device)
{
if (!libinput_device_config_buttons_has_left_handed(device))
if (!libinput_device_config_left_handed_is_available(device))
return 0;
return device->config.left_handed->get_default(device);

View file

@ -35,154 +35,6 @@ extern "C" {
__attribute__ ((format (printf, _format, _args)))
#define LIBINPUT_ATTRIBUTE_DEPRECATED __attribute__ ((deprecated))
/**
* @page tpbuttons Touchpad button behavior
*
* For touchpad devices without physical buttons, libinput enables an
* emulated right button area through either of two methods.
*
* Software button areas
* =====================
* On most touchpads, the bottom area of the touchpad is split into a left
* and a right-button area. Pressing the touchpad down with a finger in
* those areas will generate clicks as shown in the diagram below:
*
* @code
+------------------------+
| |
| |
| LEFT |
| |
| |
+------------------------+
| LEFT | RIGHT |
+------------------------+
* @endcode
*
* Generally, the touchpad will emulate a right-button click if the finger
* was set down in the right button area and did not leave the
* right button area before clicking, even if another finger was already
* down on the touchpad in another area.
* A middle click is generated by clicking the touchpad when one finger is
* in the bottom left button area, and one finger is in the botton right
* button area.
* The exact behavior of the touchpad is implementation-dependent.
*
* Top software button area
* ========================
* On selected touchpads, the top area of the touchpad is a separate set of
* software buttons split into a left, middle and right button area.
* Pressing the touchpad down with a finger in those areas will generate
* clicks as shown in the diagram below:
*
* @code
+------------------------+
| LEFT | MIDDLE | RIGHT |
+------------------------+
| |
| LEFT |
| |
+------------------------+
| LEFT | RIGHT |
+------------------------+
* @endcode
* This behavior is enabled on the Lenovo *40 series (T440, T540, T240...)
* and the Lenovo Helix, Yoga S1 and Carbon X1 2nd.
*
* Clickfinger
* ===========
* On Apple touchpads, no button areas are provided. Instead, use a
* two-finger click for a right button click, and a three-finger click for a
* middle button click.
*/
/**
* @page udev_config Static device configuration via udev
*
* libinput supports some static configuration through udev properties.
* These propertiesare read when the device is initially added
* to libinput's device list, i.e. before the @ref
* LIBINPUT_EVENT_DEVICE_ADDED event is generated.
*
* The following udev properties are supported:
* <dl>
* <dt>LIBINPUT_CALIBRATION_MATRIX</dt>
* <dd>Sets the calibration matrix, see
* libinput_device_config_calibration_get_default_matrix(). If unset,
* defaults to the identity matrix.</dd>
* <dt>ID_SEAT</dt>
* <dd>Assigns the physical seat for this device. See
* libinput_seat_get_physical_name(). Defaults to "seat0".</dd>
* <dt>WL_SEAT</dt>
* <dd>Assigns the logical seat for this device. See
* libinput_seat_get_logical_name()
* context. Defaults to "default".</dd>
* <dt>MOUSE_DPI</dt>
* <dd>HW resolution and sampling frequency of a relative pointer device.
* See @ref motion_normalization for details.
* </dd>
* </dl>
*
* Below is an example udev rule to assign "seat1" to a device from vendor
* 0x012a with the model ID of 0x034b.
* @code
* ACTION=="add|change", KERNEL=="event[0-9]*", ENV{ID_VENDOR_ID}=="012a", \
* ENV{ID_MODEL_ID}=="034b", ENV{ID_SEAT}="seat1"
* @endcode
*
*/
/**
* @page motion_normalization Normalization of relative motion
*
* Most relative input devices generate input in so-called "mickeys". A
* mickey is in device-specific units that depend on the resolution
* of the sensor. Most optical mice use sensors with 1000dpi resolution, but
* some devices range from 100dpi to well above 8000dpi.
*
* Without a physical reference point, a relative coordinate cannot be
* interpreted correctly. A delta of 10 mickeys may be a millimeter of
* physical movement or 10 millimeters, depending on the sensor. This
* affects pointer acceleration in libinput and interpretation of relative
* coordinates in callers.
*
* libinput normalizes all relative input to a physical resolution of
* 1000dpi, the same delta from two different devices thus represents the
* same physical movement of those two devices (within sensor error
* margins).
*
* Devices usually do not advertise their resolution and libinput relies on
* the udev property MOUSE_DPI for this information.
*
* The format of the property for single-resolution mice is:
* @code
* MOUSE_DPI=resolution@frequency
* @endcode
*
* The resolution is in dots per inch, the frequency in Hz.
* The format of the property for multi-resolution mice may list multiple
* resolutions and frequencies:
* @code
* MOUSE_DPI=r1@f1 *r2@f2 r3@f3
* @endcode
*
* The default frequency must be pre-fixed with an asterisk.
*
* For example, these two properties are valid:
* @code
* MOUSE_DPI=800@125
* MOUSE_DPI=400@125 800@125 *1000@500 5500@500
* @endcode
*
* The behavior for a malformed property is undefined.
*
* If the property is unset, libinput assumes the resolution is 1000dpi.
*
* Note that HW does not usually provide information about run-time
* resolution changes, libinput will thus not detect when a resolution
* changes to the non-default value.
*/
/**
* Log priority for internal logging messages.
*/
@ -256,6 +108,28 @@ enum libinput_pointer_axis {
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL = 1,
};
/**
* @ingroup device
*
* The source for a libinput_pointer_axis event. See
* libinput_event_pointer_get_axis_source() for details.
*/
enum libinput_pointer_axis_source {
/**
* The event is caused by the rotation of a wheel.
*/
LIBINPUT_POINTER_AXIS_SOURCE_WHEEL = 1,
/**
* The event is caused by the movement of one or more fingers on a
* device.
*/
LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
/**
* The event is caused by the motion of some device.
*/
LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
};
/**
* @ingroup device
*
@ -849,7 +723,7 @@ libinput_event_pointer_get_button_state(struct libinput_event_pointer *event);
*
* For the button of a @ref LIBINPUT_EVENT_POINTER_BUTTON event, return the
* total number of buttons pressed on all devices on the associated seat
* after the the event was triggered.
* after the event was triggered.
*
" @note It is an application bug to call this function for events other than
* @ref LIBINPUT_EVENT_POINTER_BUTTON. For other events, this function
@ -864,29 +738,31 @@ libinput_event_pointer_get_seat_button_count(
/**
* @ingroup event_pointer
*
* Return the axis that triggered this event.
* For pointer events that are not of type @ref LIBINPUT_EVENT_POINTER_AXIS,
* this function returns 0.
* Check if the event has a valid value for the given axis.
*
* @note It is an application bug to call this function for events other than
* @ref LIBINPUT_EVENT_POINTER_AXIS.
* If this function returns non-zero for an axis and
* libinput_event_pointer_get_axis_value() returns a value of 0, the event
* is a scroll stop event.
*
* @return the axis triggering this event
* @return non-zero if this event contains a value for this axis
*/
enum libinput_pointer_axis
libinput_event_pointer_get_axis(struct libinput_event_pointer *event);
int
libinput_event_pointer_has_axis(struct libinput_event_pointer *event,
enum libinput_pointer_axis axis);
/**
* @ingroup event_pointer
*
* Return the axis value of the given axis. The interpretation of the value
* is dependent on the axis. For the two scrolling axes
* depends on the axis. For the two scrolling axes
* @ref LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL and
* @ref LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, the value of the event is in
* relative scroll units, with the positive direction being down or right,
* respectively. The dimension of a scroll unit is equal to one unit of
* motion in the respective axis, where applicable (e.g. touchpad two-finger
* scrolling).
* respectively. For the interpretation of the value, see
* libinput_event_pointer_get_axis_source().
*
* If libinput_event_pointer_has_axis() returns 0 for an axis, this function
* returns 0 for that axis.
*
* For pointer events that are not of type @ref LIBINPUT_EVENT_POINTER_AXIS,
* this function returns 0.
@ -895,9 +771,70 @@ libinput_event_pointer_get_axis(struct libinput_event_pointer *event);
* @ref LIBINPUT_EVENT_POINTER_AXIS.
*
* @return the axis value of this event
*
* @see libinput_event_pointer_get_axis_value_discrete
*/
double
libinput_event_pointer_get_axis_value(struct libinput_event_pointer *event);
libinput_event_pointer_get_axis_value(struct libinput_event_pointer *event,
enum libinput_pointer_axis axis);
/**
* @ingroup event_pointer
*
* Return the source for a given axis event. Axis events (scroll events) can
* be caused by a hardware item such as a scroll wheel or emulated from
* other input sources, such as two-finger or edge scrolling on a
* touchpad.
*
* If the source is @ref LIBINPUT_POINTER_AXIS_SOURCE_FINGER, libinput
* guarantees that a scroll sequence is terminated with a scroll value of 0.
* A caller may use this information to decide on whether kinetic scrolling
* should be triggered on this scroll sequence.
* The coordinate system is identical to the cursor movement, i.e. a
* scroll value of 1 represents the equivalent relative motion of 1.
*
* If the source is @ref LIBINPUT_POINTER_AXIS_SOURCE_WHEEL, no terminating
* event is guaranteed (though it may happen).
* Scrolling is in discrete steps, the value is the angle the wheel moved
* in degrees. The default is 15 degrees per wheel click, but some mice may
* have differently grained wheels. It is up to the caller how to interpret
* such different step sizes.
*
* If the source is @ref LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS, no
* terminating event is guaranteed (though it may happen).
* The coordinate system is identical to the cursor movement, i.e. a
* scroll value of 1 represents the equivalent relative motion of 1.
*
* For pointer events that are not of type LIBINPUT_EVENT_POINTER_AXIS,
* this function returns 0.
*
* @note It is an application bug to call this function for events other than
* LIBINPUT_EVENT_POINTER_AXIS.
*
* @return the source for this axis event
*/
enum libinput_pointer_axis_source
libinput_event_pointer_get_axis_source(struct libinput_event_pointer *event);
/**
* @ingroup pointer
*
* Return the axis value in discrete steps for a given axis event. How a
* value translates into a discrete step depends on the source.
*
* If the source is @ref LIBINPUT_POINTER_AXIS_SOURCE_WHEEL, the discrete
* value correspond to the number of physical mouse clicks.
*
* If the source is @ref LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS or @ref
* LIBINPUT_POINTER_AXIS_SOURCE_FINGER, the discrete value is always 0.
*
* @return The discrete value for the given event.
*
* @see libinput_event_pointer_get_axis_value
*/
double
libinput_event_pointer_get_axis_value_discrete(struct libinput_event_pointer *event,
enum libinput_pointer_axis axis);
/**
* @ingroup event_pointer
@ -1472,6 +1409,17 @@ libinput_get_event(struct libinput *libinput);
enum libinput_event_type
libinput_next_event_type(struct libinput *libinput);
/**
* @ingroup base
*
* @param libinput A previously initialized libinput context
* @param user_data Caller-specific data passed to the various callback
* interfaces.
*/
void
libinput_set_user_data(struct libinput *libinput,
void *user_data);
/**
* @ingroup base
*
@ -1609,27 +1557,10 @@ libinput_log_set_handler(struct libinput *libinput,
/**
* @defgroup seat Initialization and manipulation of seats
*
* A seat has two identifiers, the physical name and the logical name. The
* physical name is summarized as the list of devices a process on the same
* physical seat has access to.
*
* The logical seat name is the seat name for a logical group of devices. A
* compositor may use that to create additonal seats as independent device
* sets. Alternatively, a compositor may limit itself to a single logical
* seat, leaving a second compositor to manage devices on the other logical
* seats.
*
* @code
* +---+--------+------------+------------------------+------------+
* | | event0 | | | log seat A |
* | K +--------+ | +------------+
* | e | event1 | phys seat0 | libinput context 1 | |
* | r +--------+ | | log seat B |
* | n | event2 | | | |
* | e +--------+------------+------------------------+------------+
* | l | event3 | phys seat1 | libinput context 2 | log seat C |
* +---+--------+------------+------------------------+------------+
* @endcode
* A seat has two identifiers, the physical name and the logical name. A
* device is always assigned to exactly one seat. It may change to a
* different logical seat but it cannot change physical seats. See @ref
* seats for details.
*/
/**
@ -2371,7 +2302,7 @@ libinput_device_config_accel_is_available(struct libinput_device *device);
* this device. The actual pointer acceleration mechanism is
* implementation-dependent, as is the number of steps available within the
* range. libinput picks the semantically closest acceleration step if the
* requested value does not match a discreet setting.
* requested value does not match a discrete setting.
*
* @param device The device to configure
* @param speed The normalized speed, in a range of [-1, 1]
@ -2493,33 +2424,32 @@ libinput_device_config_scroll_get_default_natural_scroll_enabled(struct libinput
/**
* @ingroup config
*
* Check if a device has a button configuration that supports left-handed
* usage.
* Check if a device has a configuration that supports left-handed usage.
*
* @param device The device to configure
* @return Non-zero if the device can be set to left-handed, or zero
* otherwise
*
* @see libinput_device_config_buttons_set_left_handed
* @see libinput_device_config_buttons_get_left_handed
* @see libinput_device_config_buttons_get_default_left_handed
* @see libinput_device_config_left_handed_set
* @see libinput_device_config_left_handed_get
* @see libinput_device_config_left_handed_get_default
*/
int
libinput_device_config_buttons_has_left_handed(struct libinput_device *device);
libinput_device_config_left_handed_is_available(struct libinput_device *device);
/**
* @ingroup config
*
* Set the left-handed configuration of the device. A device in left-handed
* mode sends a left button event instead of the right button and vice
* versa.
* Set the left-handed configuration of the device. For example, a pointing
* device may reverse it's buttons and send a right button click when the
* left button is pressed, and vice versa.
*
* The exact button behavior is device-dependent. On a mouse and most
* pointing devices, left and right buttons are swapped but the middle
* button is unmodified. On a touchpad, physical buttons (if present) are
* swapped. On a clickpad, the top and bottom software-emulated buttons are
* swapped where present, the main area of the touchpad remains a left
* button. Tapping and clickfinger behavior is not affected by this setting.
* The exact behavior is device-dependent. On a mouse and most pointing
* devices, left and right buttons are swapped but the middle button is
* unmodified. On a touchpad, physical buttons (if present) are swapped. On a
* clickpad, the top and bottom software-emulated buttons are swapped where
* present, the main area of the touchpad remains a left button. Tapping and
* clickfinger behavior is not affected by this setting.
*
* Changing the left-handed configuration of a device may not take effect
* until all buttons have been logically released.
@ -2528,13 +2458,13 @@ libinput_device_config_buttons_has_left_handed(struct libinput_device *device);
* @param left_handed Zero to disable, non-zero to enable left-handed mode
* @return A configuration status code
*
* @see libinput_device_config_buttons_has_left_handed
* @see libinput_device_config_buttons_get_left_handed
* @see libinput_device_config_buttons_get_default_left_handed
* @see libinput_device_config_left_handed_is_available
* @see libinput_device_config_left_handed_get
* @see libinput_device_config_left_handed_get_default
*/
enum libinput_config_status
libinput_device_config_buttons_set_left_handed(struct libinput_device *device,
int left_handed);
libinput_device_config_left_handed_set(struct libinput_device *device,
int left_handed);
/**
* @ingroup config
@ -2545,12 +2475,12 @@ libinput_device_config_buttons_set_left_handed(struct libinput_device *device,
* @return Zero if the device is in right-handed mode, non-zero if the
* device is in left-handed mode
*
* @see libinput_device_config_buttons_has_left_handed
* @see libinput_device_config_buttons_set_left_handed
* @see libinput_device_config_buttons_get_default_left_handed
* @see libinput_device_config_left_handed_is_available
* @see libinput_device_config_left_handed_set
* @see libinput_device_config_left_handed_get_default
*/
int
libinput_device_config_buttons_get_left_handed(struct libinput_device *device);
libinput_device_config_left_handed_get(struct libinput_device *device);
/**
* @ingroup config
@ -2558,15 +2488,15 @@ libinput_device_config_buttons_get_left_handed(struct libinput_device *device);
* Get the default left-handed configuration of the device.
*
* @param device The device to configure
* @return Zero if the device is in right-handed mode by default, or non-zero if the
* device is in left-handed mode by default
* @return Zero if the device is in right-handed mode by default, or non-zero
* if the device is in left-handed mode by default
*
* @see libinput_device_config_buttons_has_left_handed
* @see libinput_device_config_buttons_set_left_handed
* @see libinput_device_config_buttons_get_left_handed
* @see libinput_device_config_left_handed_is_available
* @see libinput_device_config_left_handed_set
* @see libinput_device_config_left_handed_get
*/
int
libinput_device_config_buttons_get_default_left_handed(struct libinput_device *device);
libinput_device_config_left_handed_get_default(struct libinput_device *device);
/**
* @ingroup config

View file

@ -7,10 +7,10 @@ global:
libinput_device_config_accel_get_speed;
libinput_device_config_accel_is_available;
libinput_device_config_accel_set_speed;
libinput_device_config_buttons_get_default_left_handed;
libinput_device_config_buttons_get_left_handed;
libinput_device_config_buttons_has_left_handed;
libinput_device_config_buttons_set_left_handed;
libinput_device_config_left_handed_get_default;
libinput_device_config_left_handed_get;
libinput_device_config_left_handed_is_available;
libinput_device_config_left_handed_set;
libinput_device_config_calibration_get_default_matrix;
libinput_device_config_calibration_get_matrix;
libinput_device_config_calibration_has_matrix;
@ -71,7 +71,9 @@ global:
libinput_event_pointer_get_absolute_y;
libinput_event_pointer_get_absolute_y_transformed;
libinput_event_pointer_get_axis;
libinput_event_pointer_get_axis_source;
libinput_event_pointer_get_axis_value;
libinput_event_pointer_get_axis_value_discrete;
libinput_event_pointer_get_base_event;
libinput_event_pointer_get_button_state;
libinput_event_pointer_get_button;
@ -81,6 +83,7 @@ global:
libinput_event_pointer_get_dy_unaccelerated;
libinput_event_pointer_get_seat_button_count;
libinput_event_pointer_get_time;
libinput_event_pointer_has_axis;
libinput_event_touch_get_base_event;
libinput_event_touch_get_seat_slot;
libinput_event_touch_get_slot;
@ -108,6 +111,7 @@ global:
libinput_seat_ref;
libinput_seat_set_user_data;
libinput_seat_unref;
libinput_set_user_data;
libinput_suspend;
libinput_udev_assign_seat;
libinput_udev_create_context;

View file

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

View file

@ -33,10 +33,6 @@ static void litest_mouse_setup(void)
litest_set_current_device(d);
}
static struct litest_device_interface interface = {
NULL
};
static struct input_id input_id = {
.bustype = 0x3,
.vendor = 0x17ef,
@ -58,7 +54,7 @@ struct litest_test_device litest_mouse_device = {
.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
.shortname = "mouse",
.setup = litest_mouse_setup,
.interface = &interface,
.interface = NULL,
.name = "Lenovo Optical USB Mouse",
.id = &input_id,

View file

@ -33,10 +33,6 @@ static void litest_trackpoint_setup(void)
litest_set_current_device(d);
}
static struct litest_device_interface interface = {
NULL
};
static struct input_id input_id = {
.bustype = 0x11,
.vendor = 0x2,
@ -59,7 +55,7 @@ struct litest_test_device litest_trackpoint_device = {
.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_POINTINGSTICK,
.shortname = "trackpoint",
.setup = litest_trackpoint_setup,
.interface = &interface,
.interface = NULL,
.name = "TPPS/2 IBM TrackPoint",
.id = &input_id,

View file

@ -1092,11 +1092,11 @@ litest_print_event(struct libinput_event *event)
case LIBINPUT_EVENT_POINTER_AXIS:
p = libinput_event_get_pointer_event(event);
fprintf(stderr,
"axis %s value %.2f",
libinput_event_pointer_get_axis(p) ==
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL ?
"vert" : "horiz",
libinput_event_pointer_get_axis_value(p));
"vert %.f horiz %.2f",
libinput_event_pointer_get_axis_value(p,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
libinput_event_pointer_get_axis_value(p,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL));
break;
default:
break;
@ -1307,23 +1307,24 @@ litest_assert_scroll(struct libinput *li,
LIBINPUT_EVENT_POINTER_AXIS);
ptrev = libinput_event_get_pointer_event(event);
ck_assert(ptrev != NULL);
ck_assert_int_eq(libinput_event_pointer_get_axis(ptrev), axis);
if (next_event) {
/* Normal scroll event, check dir */
if (minimum_movement > 0) {
ck_assert_int_ge(
libinput_event_pointer_get_axis_value(ptrev),
libinput_event_pointer_get_axis_value(ptrev,
axis),
minimum_movement);
} else {
ck_assert_int_le(
libinput_event_pointer_get_axis_value(ptrev),
libinput_event_pointer_get_axis_value(ptrev,
axis),
minimum_movement);
}
} else {
/* Last scroll event, must be 0 */
ck_assert_int_eq(
libinput_event_pointer_get_axis_value(ptrev),
libinput_event_pointer_get_axis_value(ptrev, axis),
0);
}
libinput_event_destroy(event);
@ -1332,6 +1333,27 @@ litest_assert_scroll(struct libinput *li,
}
}
void
litest_assert_only_typed_events(struct libinput *li,
enum libinput_event_type type)
{
struct libinput_event *event;
assert(type != LIBINPUT_EVENT_NONE);
libinput_dispatch(li);
event = libinput_get_event(li);
ck_assert_notnull(event);
while (event) {
ck_assert_int_eq(libinput_event_get_type(event),
type);
libinput_event_destroy(event);
libinput_dispatch(li);
event = libinput_get_event(li);
}
}
void
litest_timeout_tap(void)
{

View file

@ -180,6 +180,8 @@ void litest_assert_button_event(struct libinput *li,
void litest_assert_scroll(struct libinput *li,
enum libinput_pointer_axis axis,
int minimum_movement);
void litest_assert_only_typed_events(struct libinput *li,
enum libinput_event_type type);
struct libevdev_uinput * litest_create_uinput_device(const char *name,
struct input_id *id,

View file

@ -569,7 +569,7 @@ END_TEST
struct parser_test {
char *tag;
int expected_dpi;
int expected_value;
};
START_TEST(dpi_parser)
@ -597,13 +597,42 @@ START_TEST(dpi_parser)
{ "", 0 },
{ " ", 0 },
{ "* ", 0 },
{ NULL }
{ NULL, 0 }
};
int i, dpi;
for (i = 0; tests[i].tag != NULL; i++) {
dpi = parse_mouse_dpi_property(tests[i].tag);
ck_assert_int_eq(dpi, tests[i].expected_dpi);
ck_assert_int_eq(dpi, tests[i].expected_value);
}
}
END_TEST
START_TEST(wheel_click_parser)
{
struct parser_test tests[] = {
{ "1", 1 },
{ "10", 10 },
{ "-12", -12 },
{ "360", 360 },
{ "66 ", 66 },
{ " 100 ", 100 },
{ "0", 0 },
{ "-0", 0 },
{ "a", 0 },
{ "10a", 0 },
{ "10-", 0 },
{ "sadfasfd", 0 },
{ "361", 0 },
{ NULL, 0 }
};
int i, angle;
for (i = 0; tests[i].tag != NULL; i++) {
angle = parse_mouse_wheel_click_angle_property(tests[i].tag);
ck_assert_int_eq(angle, tests[i].expected_value);
}
}
END_TEST
@ -621,6 +650,7 @@ int main (int argc, char **argv) {
litest_add_no_device("misc:matrix", matrix_helpers);
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);
}

View file

@ -133,6 +133,21 @@ START_TEST(path_create_destroy)
}
END_TEST
START_TEST(path_set_user_data)
{
struct libinput *li;
int data1, data2;
li = libinput_path_create_context(&simple_interface, &data1);
ck_assert(li != NULL);
ck_assert(libinput_get_user_data(li) == &data1);
libinput_set_user_data(li, &data2);
ck_assert(libinput_get_user_data(li) == &data2);
libinput_unref(li);
}
END_TEST
START_TEST(path_added_seat)
{
struct litest_device *dev = litest_current_device();
@ -867,6 +882,7 @@ main(int argc, char **argv)
litest_add_no_device("path:create", path_create_NULL);
litest_add_no_device("path:create", path_create_invalid);
litest_add_no_device("path:create", path_create_destroy);
litest_add_no_device("path:create", path_set_user_data);
litest_add_no_device("path:suspend", path_suspend);
litest_add_no_device("path:suspend", path_double_suspend);
litest_add_no_device("path:suspend", path_double_resume);

View file

@ -348,14 +348,18 @@ test_wheel_event(struct litest_device *dev, int which, int amount)
struct libinput *li = dev->libinput;
struct libinput_event *event;
struct libinput_event_pointer *ptrev;
enum libinput_pointer_axis axis;
/* the current evdev implementation scales the scroll wheel events
up by a factor 10 */
const int scroll_step = 10;
up by a factor 15 */
const int scroll_step = 15;
int expected = amount * scroll_step;
int discrete = amount;
if (libinput_device_config_scroll_get_natural_scroll_enabled(dev->libinput_device))
if (libinput_device_config_scroll_get_natural_scroll_enabled(dev->libinput_device)) {
expected *= -1;
discrete *= -1;
}
/* mouse scroll wheels are 'upside down' */
if (which == REL_WHEEL)
@ -372,11 +376,17 @@ test_wheel_event(struct litest_device *dev, int which, int amount)
ptrev = libinput_event_get_pointer_event(event);
ck_assert(ptrev != NULL);
ck_assert_int_eq(libinput_event_pointer_get_axis(ptrev),
which == REL_WHEEL ?
axis = (which == REL_WHEEL) ?
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL :
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
ck_assert_int_eq(libinput_event_pointer_get_axis_value(ptrev), expected);
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
ck_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);
libinput_event_destroy(event);
}
@ -567,13 +577,13 @@ START_TEST(pointer_left_handed_defaults)
struct libinput_device *d = dev->libinput_device;
int rc;
rc = libinput_device_config_buttons_has_left_handed(d);
rc = libinput_device_config_left_handed_is_available(d);
ck_assert_int_ne(rc, 0);
rc = libinput_device_config_buttons_get_left_handed(d);
rc = libinput_device_config_left_handed_get(d);
ck_assert_int_eq(rc, 0);
rc = libinput_device_config_buttons_get_default_left_handed(d);
rc = libinput_device_config_left_handed_get_default(d);
ck_assert_int_eq(rc, 0);
}
END_TEST
@ -585,7 +595,7 @@ START_TEST(pointer_left_handed)
struct libinput *li = dev->libinput;
enum libinput_config_status status;
status = libinput_device_config_buttons_set_left_handed(d, 1);
status = libinput_device_config_left_handed_set(d, 1);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
litest_drain_events(li);
@ -635,7 +645,7 @@ START_TEST(pointer_left_handed_during_click)
libinput_dispatch(li);
/* Change while button is down, expect correct release event */
status = libinput_device_config_buttons_set_left_handed(d, 1);
status = libinput_device_config_left_handed_set(d, 1);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
litest_button_click(dev, BTN_LEFT, 0);
@ -660,7 +670,7 @@ START_TEST(pointer_left_handed_during_click_multiple_buttons)
litest_button_click(dev, BTN_LEFT, 1);
libinput_dispatch(li);
status = libinput_device_config_buttons_set_left_handed(d, 1);
status = libinput_device_config_left_handed_set(d, 1);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
/* No left-handed until all buttons were down */

View file

@ -408,16 +408,16 @@ START_TEST(touch_no_left_handed)
enum libinput_config_status status;
int rc;
rc = libinput_device_config_buttons_has_left_handed(d);
rc = libinput_device_config_left_handed_is_available(d);
ck_assert_int_eq(rc, 0);
rc = libinput_device_config_buttons_get_left_handed(d);
rc = libinput_device_config_left_handed_get(d);
ck_assert_int_eq(rc, 0);
rc = libinput_device_config_buttons_get_default_left_handed(d);
rc = libinput_device_config_left_handed_get_default(d);
ck_assert_int_eq(rc, 0);
status = libinput_device_config_buttons_set_left_handed(d, 0);
status = libinput_device_config_left_handed_set(d, 0);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
}
END_TEST

View file

@ -121,7 +121,6 @@ START_TEST(touchpad_1fg_tap_n_drag)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
libinput_device_config_tap_set_enabled(dev->libinput_device,
LIBINPUT_CONFIG_TAP_ENABLED);
@ -140,27 +139,15 @@ START_TEST(touchpad_1fg_tap_n_drag)
LIBINPUT_BUTTON_STATE_PRESSED);
libinput_dispatch(li);
while (libinput_next_event_type(li) == LIBINPUT_EVENT_POINTER_MOTION) {
event = libinput_get_event(li);
libinput_event_destroy(event);
libinput_dispatch(li);
}
ck_assert_int_eq(libinput_next_event_type(li), LIBINPUT_EVENT_NONE);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
/* lift finger, set down again, should continue dragging */
litest_touch_down(dev, 0, 50, 50);
litest_touch_move_to(dev, 0, 50, 50, 80, 80, 5, 40);
litest_touch_up(dev, 0);
libinput_dispatch(li);
while (libinput_next_event_type(li) == LIBINPUT_EVENT_POINTER_MOTION) {
event = libinput_get_event(li);
libinput_event_destroy(event);
libinput_dispatch(li);
}
ck_assert_int_eq(libinput_next_event_type(li), LIBINPUT_EVENT_NONE);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
litest_timeout_tap();
@ -204,29 +191,23 @@ START_TEST(touchpad_2fg_tap_n_drag)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
libinput_device_config_tap_set_enabled(dev->libinput_device,
LIBINPUT_CONFIG_TAP_ENABLED);
litest_drain_events(li);
litest_touch_down(dev, 0, 50, 50);
litest_touch_down(dev, 0, 30, 70);
litest_touch_up(dev, 0);
litest_touch_down(dev, 0, 50, 50);
litest_touch_down(dev, 1, 60, 50);
litest_touch_move_to(dev, 0, 50, 50, 80, 80, 5, 40);
litest_touch_down(dev, 0, 30, 70);
litest_touch_down(dev, 1, 80, 70);
litest_touch_move_to(dev, 0, 30, 70, 30, 30, 5, 40);
libinput_dispatch(li);
litest_assert_button_event(li, BTN_LEFT,
LIBINPUT_BUTTON_STATE_PRESSED);
while (libinput_next_event_type(li) == LIBINPUT_EVENT_POINTER_MOTION) {
event = libinput_get_event(li);
libinput_event_destroy(event);
libinput_dispatch(li);
}
litest_assert_empty_queue(li);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
litest_touch_up(dev, 0);
litest_touch_up(dev, 1);
@ -243,29 +224,23 @@ START_TEST(touchpad_2fg_tap_n_drag_3fg_btntool)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
libinput_device_config_tap_set_enabled(dev->libinput_device,
LIBINPUT_CONFIG_TAP_ENABLED);
litest_drain_events(li);
litest_touch_down(dev, 0, 50, 50);
litest_touch_down(dev, 0, 30, 70);
litest_touch_up(dev, 0);
litest_touch_down(dev, 0, 50, 50);
litest_touch_down(dev, 1, 60, 50);
litest_touch_move_to(dev, 0, 50, 50, 80, 80, 5, 40);
litest_touch_down(dev, 0, 30, 70);
litest_touch_down(dev, 1, 80, 90);
litest_touch_move_to(dev, 0, 30, 70, 30, 30, 5, 40);
libinput_dispatch(li);
litest_assert_button_event(li, BTN_LEFT,
LIBINPUT_BUTTON_STATE_PRESSED);
while (libinput_next_event_type(li) == LIBINPUT_EVENT_POINTER_MOTION) {
event = libinput_get_event(li);
libinput_event_destroy(event);
libinput_dispatch(li);
}
litest_assert_empty_queue(li);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
/* Putting down a third finger should end the drag */
litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 1);
@ -591,7 +566,6 @@ START_TEST(touchpad_1fg_tap_n_drag_click)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
libinput_device_config_tap_set_enabled(dev->libinput_device,
LIBINPUT_CONFIG_TAP_ENABLED);
@ -608,15 +582,7 @@ START_TEST(touchpad_1fg_tap_n_drag_click)
litest_assert_button_event(li, BTN_LEFT,
LIBINPUT_BUTTON_STATE_PRESSED);
libinput_dispatch(li);
ck_assert_int_eq(libinput_next_event_type(li),
LIBINPUT_EVENT_POINTER_MOTION);
while (libinput_next_event_type(li) == LIBINPUT_EVENT_POINTER_MOTION) {
event = libinput_get_event(li);
libinput_event_destroy(event);
libinput_dispatch(li);
}
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
litest_event(dev, EV_KEY, BTN_LEFT, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
@ -873,7 +839,6 @@ START_TEST(clickpad_click_n_drag)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
litest_drain_events(li);
@ -893,14 +858,7 @@ START_TEST(clickpad_click_n_drag)
litest_touch_move_to(dev, 1, 70, 70, 80, 50, 5, 0);
litest_touch_up(dev, 1);
libinput_dispatch(li);
ck_assert_int_eq(libinput_next_event_type(li),
LIBINPUT_EVENT_POINTER_MOTION);
do {
event = libinput_get_event(li);
libinput_event_destroy(event);
libinput_dispatch(li);
} while (libinput_next_event_type(li) == LIBINPUT_EVENT_POINTER_MOTION);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
litest_event(dev, EV_KEY, BTN_LEFT, 0);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
@ -924,7 +882,7 @@ START_TEST(clickpad_softbutton_left)
litest_assert_button_event(li,
BTN_LEFT,
LIBINPUT_BUTTON_STATE_PRESSED);
LIBINPUT_BUTTON_STATE_PRESSED);
litest_event(dev, EV_KEY, BTN_LEFT, 0);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
@ -932,7 +890,7 @@ START_TEST(clickpad_softbutton_left)
litest_assert_button_event(li,
BTN_LEFT,
LIBINPUT_BUTTON_STATE_RELEASED);
LIBINPUT_BUTTON_STATE_RELEASED);
libinput_dispatch(li);
@ -1481,18 +1439,19 @@ START_TEST(touchpad_2fg_scroll_slow_distance)
/* last event is value 0, tested elsewhere */
while (libinput_next_event_type(li) != LIBINPUT_EVENT_NONE) {
double axisval;
ck_assert_int_eq(libinput_event_get_type(event),
LIBINPUT_EVENT_POINTER_AXIS);
ptrev = libinput_event_get_pointer_event(event);
ck_assert_int_eq(libinput_event_pointer_get_axis(ptrev),
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
ck_assert(libinput_event_pointer_get_axis_value(ptrev) > 0.0);
axisval = libinput_event_pointer_get_axis_value(ptrev,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
ck_assert(axisval > 0.0);
/* this is to verify we test the right thing, if the value
is greater than scroll.threshold we triggered the wrong
condition */
ck_assert(libinput_event_pointer_get_axis_value(ptrev) < 5.0);
ck_assert(axisval < 5.0);
libinput_event_destroy(event);
event = libinput_get_event(li);
@ -1503,6 +1462,67 @@ START_TEST(touchpad_2fg_scroll_slow_distance)
}
END_TEST
START_TEST(touchpad_2fg_scroll_source)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
struct libinput_event_pointer *ptrev;
litest_drain_events(li);
test_2fg_scroll(dev, 0, 20, 0);
litest_wait_for_event_of_type(li, LIBINPUT_EVENT_POINTER_AXIS, -1);
while ((event = libinput_get_event(li))) {
ck_assert_int_eq(libinput_event_get_type(event),
LIBINPUT_EVENT_POINTER_AXIS);
ptrev = libinput_event_get_pointer_event(event);
ck_assert_int_eq(libinput_event_pointer_get_axis_source(ptrev),
LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
libinput_event_destroy(event);
}
}
END_TEST
START_TEST(touchpad_2fg_scroll_return_to_motion)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
litest_drain_events(li);
/* start with motion */
litest_touch_down(dev, 0, 70, 70);
litest_touch_move_to(dev, 0, 70, 70, 47, 50, 10, 0);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
/* 2fg scroll */
litest_touch_down(dev, 1, 53, 50);
litest_touch_move_to(dev, 0, 47, 50, 47, 70, 5, 0);
litest_touch_move_to(dev, 1, 53, 50, 53, 70, 5, 0);
litest_touch_up(dev, 1);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS);
litest_touch_move_to(dev, 0, 47, 70, 47, 50, 10, 0);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
/* back to 2fg scroll, lifting the other finger */
litest_touch_down(dev, 1, 50, 50);
litest_touch_move_to(dev, 0, 47, 50, 47, 70, 5, 0);
litest_touch_move_to(dev, 1, 53, 50, 53, 70, 5, 0);
litest_touch_up(dev, 0);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS);
/* move with second finger */
litest_touch_move_to(dev, 1, 53, 70, 53, 50, 10, 0);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
litest_touch_up(dev, 1);
litest_assert_empty_queue(li);
}
END_TEST
START_TEST(touchpad_scroll_natural_defaults)
{
struct litest_device *dev = litest_current_device();
@ -1549,6 +1569,154 @@ START_TEST(touchpad_scroll_natural)
}
END_TEST
START_TEST(touchpad_edge_scroll)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
litest_drain_events(li);
litest_touch_down(dev, 0, 99, 20);
litest_touch_move_to(dev, 0, 99, 20, 99, 80, 10, 0);
litest_touch_up(dev, 0);
libinput_dispatch(li);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 10);
litest_assert_empty_queue(li);
litest_touch_down(dev, 0, 99, 80);
litest_touch_move_to(dev, 0, 99, 80, 99, 20, 10, 0);
litest_touch_up(dev, 0);
libinput_dispatch(li);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, -10);
litest_assert_empty_queue(li);
litest_touch_down(dev, 0, 20, 99);
litest_touch_move_to(dev, 0, 20, 99, 70, 99, 10, 0);
litest_touch_up(dev, 0);
libinput_dispatch(li);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, 10);
litest_assert_empty_queue(li);
litest_touch_down(dev, 0, 70, 99);
litest_touch_move_to(dev, 0, 70, 99, 20, 99, 10, 0);
litest_touch_up(dev, 0);
libinput_dispatch(li);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, -10);
litest_assert_empty_queue(li);
}
END_TEST
START_TEST(touchpad_edge_scroll_slow_distance)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
struct libinput_event_pointer *ptrev;
litest_drain_events(li);
litest_touch_down(dev, 0, 99, 20);
litest_touch_move_to(dev, 0, 99, 20, 99, 80, 60, 10);
litest_touch_up(dev, 0);
libinput_dispatch(li);
event = libinput_get_event(li);
ck_assert_notnull(event);
while (libinput_next_event_type(li) != LIBINPUT_EVENT_NONE) {
double axisval;
ck_assert_int_eq(libinput_event_get_type(event),
LIBINPUT_EVENT_POINTER_AXIS);
ptrev = libinput_event_get_pointer_event(event);
axisval = libinput_event_pointer_get_axis_value(ptrev,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
ck_assert(axisval > 0.0);
/* this is to verify we test the right thing, if the value
is greater than scroll.threshold we triggered the wrong
condition */
ck_assert(axisval < 5.0);
libinput_event_destroy(event);
event = libinput_get_event(li);
}
litest_assert_empty_queue(li);
libinput_event_destroy(event);
}
END_TEST
START_TEST(touchpad_edge_scroll_no_motion)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
litest_drain_events(li);
litest_touch_down(dev, 0, 99, 20);
litest_touch_move_to(dev, 0, 99, 20, 99, 60, 10, 0);
/* moving outside -> no motion event */
litest_touch_move_to(dev, 0, 99, 60, 20, 80, 10, 0);
/* moving down outside edge once scrolling had started -> scroll */
litest_touch_move_to(dev, 0, 20, 80, 40, 99, 10, 0);
litest_touch_up(dev, 0);
libinput_dispatch(li);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 5);
litest_assert_empty_queue(li);
}
END_TEST
START_TEST(touchpad_edge_scroll_no_edge_after_motion)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
litest_drain_events(li);
/* moving into the edge zone must not trigger scroll events */
litest_touch_down(dev, 0, 20, 20);
litest_touch_move_to(dev, 0, 20, 20, 99, 20, 10, 0);
litest_touch_move_to(dev, 0, 99, 20, 99, 80, 10, 0);
litest_touch_up(dev, 0);
libinput_dispatch(li);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
litest_assert_empty_queue(li);
}
END_TEST
START_TEST(touchpad_edge_scroll_source)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
struct libinput_event_pointer *ptrev;
litest_drain_events(li);
litest_touch_down(dev, 0, 99, 20);
litest_touch_move_to(dev, 0, 99, 20, 99, 80, 10, 0);
litest_touch_up(dev, 0);
litest_wait_for_event_of_type(li, LIBINPUT_EVENT_POINTER_AXIS, -1);
while ((event = libinput_get_event(li))) {
ck_assert_int_eq(libinput_event_get_type(event),
LIBINPUT_EVENT_POINTER_AXIS);
ptrev = libinput_event_get_pointer_event(event);
ck_assert_int_eq(libinput_event_pointer_get_axis_source(ptrev),
LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
libinput_event_destroy(event);
}
}
END_TEST
START_TEST(touchpad_tap_is_available)
{
struct litest_device *dev = litest_current_device();
@ -1697,8 +1865,6 @@ START_TEST(touchpad_palm_detect_palm_becomes_pointer)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *ev;
enum libinput_event_type type;
if (!touchpad_has_palm_detect_size(dev))
return;
@ -1711,15 +1877,7 @@ START_TEST(touchpad_palm_detect_palm_becomes_pointer)
libinput_dispatch(li);
ev = libinput_get_event(li);
ck_assert_notnull(ev);
do {
type = libinput_event_get_type(ev);
ck_assert_int_eq(type, LIBINPUT_EVENT_POINTER_MOTION);
libinput_event_destroy(ev);
ev = libinput_get_event(li);
} while (ev);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
litest_assert_empty_queue(li);
}
@ -1729,8 +1887,6 @@ START_TEST(touchpad_palm_detect_no_palm_moving_into_edges)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *ev;
enum libinput_event_type type;
if (!touchpad_has_palm_detect_size(dev))
return;
@ -1746,16 +1902,7 @@ START_TEST(touchpad_palm_detect_no_palm_moving_into_edges)
litest_touch_move_to(dev, 0, 99, 50, 99, 90, 5, 0);
libinput_dispatch(li);
type = libinput_next_event_type(li);
do {
ck_assert_int_eq(type, LIBINPUT_EVENT_POINTER_MOTION);
ev = libinput_get_event(li);
libinput_event_destroy(ev);
type = libinput_next_event_type(li);
libinput_dispatch(li);
} while (type != LIBINPUT_EVENT_NONE);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
litest_touch_up(dev, 0);
libinput_dispatch(li);
@ -1770,7 +1917,7 @@ START_TEST(touchpad_left_handed)
struct libinput *li = dev->libinput;
enum libinput_config_status status;
status = libinput_device_config_buttons_set_left_handed(d, 1);
status = libinput_device_config_left_handed_set(d, 1);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
litest_drain_events(li);
@ -1815,7 +1962,7 @@ START_TEST(touchpad_left_handed_clickpad)
struct libinput *li = dev->libinput;
enum libinput_config_status status;
status = libinput_device_config_buttons_set_left_handed(d, 1);
status = libinput_device_config_left_handed_set(d, 1);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
litest_drain_events(li);
@ -1866,7 +2013,7 @@ START_TEST(touchpad_left_handed_clickfinger)
struct libinput *li = dev->libinput;
enum libinput_config_status status;
status = libinput_device_config_buttons_set_left_handed(d, 1);
status = libinput_device_config_left_handed_set(d, 1);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
litest_drain_events(li);
@ -1909,7 +2056,7 @@ START_TEST(touchpad_left_handed_tapping)
libinput_device_config_tap_set_enabled(dev->libinput_device,
LIBINPUT_CONFIG_TAP_ENABLED);
status = libinput_device_config_buttons_set_left_handed(d, 1);
status = libinput_device_config_left_handed_set(d, 1);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
litest_drain_events(li);
@ -1940,7 +2087,7 @@ START_TEST(touchpad_left_handed_tapping_2fg)
libinput_device_config_tap_set_enabled(dev->libinput_device,
LIBINPUT_CONFIG_TAP_ENABLED);
status = libinput_device_config_buttons_set_left_handed(d, 1);
status = libinput_device_config_left_handed_set(d, 1);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
litest_drain_events(li);
@ -1976,7 +2123,7 @@ START_TEST(touchpad_left_handed_delayed)
litest_button_click(dev, BTN_LEFT, 1);
libinput_dispatch(li);
status = libinput_device_config_buttons_set_left_handed(d, 1);
status = libinput_device_config_left_handed_set(d, 1);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
litest_button_click(dev, BTN_LEFT, 0);
@ -1993,7 +2140,7 @@ START_TEST(touchpad_left_handed_delayed)
litest_button_click(dev, BTN_LEFT, 1);
libinput_dispatch(li);
status = libinput_device_config_buttons_set_left_handed(d, 0);
status = libinput_device_config_left_handed_set(d, 0);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
litest_button_click(dev, BTN_RIGHT, 0);
@ -2026,7 +2173,7 @@ START_TEST(touchpad_left_handed_clickpad_delayed)
litest_button_click(dev, BTN_LEFT, 1);
libinput_dispatch(li);
status = libinput_device_config_buttons_set_left_handed(d, 1);
status = libinput_device_config_left_handed_set(d, 1);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
litest_button_click(dev, BTN_LEFT, 0);
@ -2045,7 +2192,7 @@ START_TEST(touchpad_left_handed_clickpad_delayed)
litest_button_click(dev, BTN_LEFT, 1);
libinput_dispatch(li);
status = libinput_device_config_buttons_set_left_handed(d, 0);
status = libinput_device_config_left_handed_set(d, 0);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
litest_button_click(dev, BTN_LEFT, 0);
@ -2122,9 +2269,16 @@ int main(int argc, char **argv) {
litest_add("touchpad:scroll", touchpad_2fg_scroll, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:scroll", touchpad_2fg_scroll_slow_distance, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:scroll", touchpad_2fg_scroll_return_to_motion, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:scroll", touchpad_2fg_scroll_source, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:scroll", touchpad_scroll_natural_defaults, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:scroll", touchpad_scroll_natural_enable_config, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:scroll", touchpad_scroll_natural, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:scroll", touchpad_edge_scroll, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY);
litest_add("touchpad:scroll", touchpad_edge_scroll_no_motion, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY);
litest_add("touchpad:scroll", touchpad_edge_scroll_no_edge_after_motion, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY);
litest_add("touchpad:scroll", touchpad_edge_scroll_slow_distance, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY);
litest_add("touchpad:scroll", touchpad_edge_scroll_source, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY);
litest_add("touchpad:palm", touchpad_palm_detect_at_edge, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:palm", touchpad_palm_detect_at_bottom_corners, LITEST_TOUCHPAD, LITEST_CLICKPAD);

View file

@ -107,11 +107,35 @@ START_TEST(trackpoint_middlebutton_noscroll)
}
END_TEST
START_TEST(trackpoint_scroll_source)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
struct libinput_event_pointer *ptrev;
litest_drain_events(li);
litest_button_scroll(dev, BTN_MIDDLE, 0, 6);
litest_wait_for_event_of_type(li, LIBINPUT_EVENT_POINTER_AXIS, -1);
while ((event = libinput_get_event(li))) {
ptrev = libinput_event_get_pointer_event(event);
ck_assert_int_eq(libinput_event_pointer_get_axis_source(ptrev),
LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
libinput_event_destroy(event);
}
}
END_TEST
int main(int argc, char **argv) {
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

@ -129,6 +129,26 @@ START_TEST(udev_create_empty_seat)
}
END_TEST
START_TEST(udev_set_user_data)
{
struct libinput *li;
struct udev *udev;
int data1, data2;
udev = udev_new();
ck_assert(udev != NULL);
li = libinput_udev_create_context(&simple_interface, &data1, udev);
ck_assert(li != NULL);
ck_assert(libinput_get_user_data(li) == &data1);
libinput_set_user_data(li, &data2);
ck_assert(libinput_get_user_data(li) == &data2);
libinput_unref(li);
udev_unref(udev);
}
END_TEST
/**
* This test only works if there's at least one device in the system that is
* assigned the default seat. Should cover the 99% case.
@ -488,6 +508,7 @@ main(int argc, char **argv)
litest_add_no_device("udev:create", udev_create_NULL);
litest_add_no_device("udev:create", udev_create_seat0);
litest_add_no_device("udev:create", udev_create_empty_seat);
litest_add_no_device("udev:create", udev_set_user_data);
litest_add_no_device("udev:seat", udev_added_seat_default);
litest_add_no_device("udev:seat", udev_change_seat);

View file

@ -1,10 +1,16 @@
noinst_PROGRAMS = event-debug
noinst_LTLIBRARIES = libshared.la
AM_CPPFLAGS = -I$(top_srcdir)/include \
-I$(top_srcdir)/src
libshared_la_SOURCES = \
shared.c \
shared.h
event_debug_SOURCES = event-debug.c
event_debug_LDADD = ../src/libinput.la $(LIBUDEV_LIBS)
event_debug_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS)
event_debug_LDFLAGS = -no-install
event_debug_CFLAGS = $(LIBUDEV_CFLAGS)
@ -12,7 +18,7 @@ if BUILD_EVENTGUI
noinst_PROGRAMS += event-gui
event_gui_SOURCES = event-gui.c
event_gui_LDADD = ../src/libinput.la $(CAIRO_LIBS) $(GTK_LIBS) $(LIBUDEV_LIBS)
event_gui_LDADD = ../src/libinput.la libshared.la $(CAIRO_LIBS) $(GTK_LIBS) $(LIBUDEV_LIBS)
event_gui_CFLAGS = $(CAIRO_CFLAGS) $(GTK_CFLAGS) $(LIBUDEV_CFLAGS)
event_gui_LDFLAGS = -no-install
endif

View file

@ -23,7 +23,6 @@
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <poll.h>
#include <stdio.h>
#include <signal.h>
@ -33,85 +32,16 @@
#include <libudev.h>
#include "linux/input.h"
#include <sys/ioctl.h>
#include <sys/signalfd.h>
#include <libinput.h>
static enum {
MODE_UDEV,
MODE_DEVICE,
} mode = MODE_UDEV;
static const char *device;
static const char *seat = "seat0";
static struct udev *udev;
#include "shared.h"
uint32_t start_time;
static const uint32_t screen_width = 100;
static const uint32_t screen_height = 100;
static int verbose = 0;
static void
usage(void)
{
printf("Usage: %s [--verbose] [--udev [<seat>]|--device /dev/input/event0]\n"
"--verbose ....... Print debugging output.\n"
"--udev <seat>.... Use udev device discovery (default).\n"
" Specifying a seat ID is optional.\n"
"--device /path/to/device .... open the given device only\n",
program_invocation_short_name);
}
static int
parse_args(int argc, char **argv)
{
while (1) {
int c;
int option_index = 0;
static struct option opts[] = {
{ "device", 1, 0, 'd' },
{ "udev", 0, 0, 'u' },
{ "help", 0, 0, 'h' },
{ "verbose", 0, 0, 'v'},
{ 0, 0, 0, 0}
};
c = getopt_long(argc, argv, "h", opts, &option_index);
if (c == -1)
break;
switch(c) {
case 'h': /* --help */
usage();
exit(0);
case 'd': /* --device */
mode = MODE_DEVICE;
if (!optarg) {
usage();
return 1;
}
device = optarg;
break;
case 'u': /* --udev */
mode = MODE_UDEV;
if (optarg)
seat = optarg;
break;
case 'v': /* --verbose */
verbose = 1;
break;
default:
usage();
return 1;
}
}
if (optind < argc) {
usage();
return 1;
}
return 0;
}
struct tools_options options;
static unsigned int stop = 0;
static int
open_restricted(const char *path, int flags, void *user_data)
@ -131,70 +61,6 @@ static const struct libinput_interface interface = {
.close_restricted = close_restricted,
};
static void
log_handler(struct libinput *li,
enum libinput_log_priority priority,
const char *format,
va_list args)
{
vprintf(format, args);
}
static int
open_udev(struct libinput **li)
{
udev = udev_new();
if (!udev) {
fprintf(stderr, "Failed to initialize udev\n");
return 1;
}
*li = libinput_udev_create_context(&interface, NULL, udev);
if (!*li) {
fprintf(stderr, "Failed to initialize context from udev\n");
return 1;
}
if (verbose) {
libinput_log_set_handler(*li, log_handler);
libinput_log_set_priority(*li, LIBINPUT_LOG_PRIORITY_DEBUG);
}
if (libinput_udev_assign_seat(*li, seat)) {
fprintf(stderr, "Failed to set seat\n");
libinput_unref(*li);
return 1;
}
return 0;
}
static int
open_device(struct libinput **li, const char *path)
{
struct libinput_device *device;
*li = libinput_path_create_context(&interface, NULL);
if (!*li) {
fprintf(stderr, "Failed to initialize context from %s\n", path);
return 1;
}
if (verbose) {
libinput_log_set_handler(*li, log_handler);
libinput_log_set_priority(*li, LIBINPUT_LOG_PRIORITY_DEBUG);
}
device = libinput_path_add_device(*li, path);
if (!device) {
fprintf(stderr, "Failed to initialized device %s\n", path);
libinput_unref(*li);
return 1;
}
return 0;
}
static void
print_event_header(struct libinput_event *ev)
{
@ -271,17 +137,28 @@ print_device_notify(struct libinput_event *ev)
double w, h;
uint32_t scroll_methods;
printf("%-30s %s %s",
printf("%-33s %5s %7s",
libinput_device_get_name(dev),
libinput_seat_get_physical_name(seat),
libinput_seat_get_logical_name(seat));
printf(" cap:");
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_KEYBOARD))
printf("k");
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_POINTER))
printf("p");
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_TOUCH))
printf("t");
if (libinput_device_get_size(dev, &w, &h) == 0)
printf("\tsize %.2f/%.2fmm", w, h);
if (libinput_device_config_tap_get_finger_count((dev)))
printf(" tap");
if (libinput_device_config_buttons_has_left_handed((dev)))
if (libinput_device_config_left_handed_is_available((dev)))
printf(" left");
if (libinput_device_config_scroll_has_natural_scroll((dev)))
printf(" scroll-nat");
@ -375,24 +252,18 @@ static void
print_pointer_axis_event(struct libinput_event *ev)
{
struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
enum libinput_pointer_axis axis = libinput_event_pointer_get_axis(p);
const char *ax;
double val;
switch (axis) {
case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL:
ax = "vscroll";
break;
case LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL:
ax = "hscroll";
break;
default:
abort();
}
double v = 0, h = 0;
if (libinput_event_pointer_has_axis(p,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
v = libinput_event_pointer_get_axis_value(p,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
if (libinput_event_pointer_has_axis(p,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
h = libinput_event_pointer_get_axis_value(p,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
print_event_time(libinput_event_pointer_get_time(p));
val = libinput_event_pointer_get_axis_value(p);
printf("%s %.2f\n", ax, val);
printf("vert %.2f horiz %.2f\n", v, h);
}
static const char*
@ -532,6 +403,8 @@ handle_and_print_events(struct libinput *li)
case LIBINPUT_EVENT_DEVICE_ADDED:
case LIBINPUT_EVENT_DEVICE_REMOVED:
print_device_notify(ev);
tools_device_apply_config(libinput_event_get_device(ev),
&options);
break;
case LIBINPUT_EVENT_KEYBOARD_KEY:
print_key_event(ev);
@ -584,27 +457,30 @@ handle_and_print_events(struct libinput *li)
return rc;
}
static void
sighandler(int signal, siginfo_t *siginfo, void *userdata)
{
stop = 1;
}
static void
mainloop(struct libinput *li)
{
struct pollfd fds[2];
sigset_t mask;
struct pollfd fds;
struct sigaction act;
fds[0].fd = libinput_get_fd(li);
fds[0].events = POLLIN;
fds[0].revents = 0;
fds.fd = libinput_get_fd(li);
fds.events = POLLIN;
fds.revents = 0;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
memset(&act, 0, sizeof(act));
act.sa_sigaction = sighandler;
act.sa_flags = SA_SIGINFO;
fds[1].fd = signalfd(-1, &mask, SFD_NONBLOCK);
fds[1].events = POLLIN;
fds[1].revents = 0;
if (fds[1].fd == -1 ||
sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
if (sigaction(SIGINT, &act, NULL) == -1) {
fprintf(stderr, "Failed to set up signal handling (%s)\n",
strerror(errno));
return;
}
/* Handle already-pending device added events */
@ -612,14 +488,8 @@ mainloop(struct libinput *li)
fprintf(stderr, "Expected device added events on startup but got none. "
"Maybe you don't have the right permissions?\n");
while (poll(fds, 2, -1) > -1) {
if (fds[1].revents)
break;
while (!stop && poll(&fds, 1, -1) > -1)
handle_and_print_events(li);
}
close(fds[1].fd);
}
int
@ -628,17 +498,14 @@ main(int argc, char **argv)
struct libinput *li;
struct timespec tp;
if (parse_args(argc, argv))
tools_init_options(&options);
if (tools_parse_args(argc, argv, &options))
return 1;
if (mode == MODE_UDEV) {
if (open_udev(&li))
return 1;
} else if (mode == MODE_DEVICE) {
if (open_device(&li, device))
return 1;
} else
abort();
li = tools_open_backend(&options, NULL, &interface);
if (!li)
return 1;
clock_gettime(CLOCK_MONOTONIC, &tp);
start_time = tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
@ -646,8 +513,6 @@ main(int argc, char **argv)
mainloop(li);
libinput_unref(li);
if (udev)
udev_unref(udev);
return 0;
}

View file

@ -27,7 +27,6 @@
#include <cairo.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
@ -40,8 +39,12 @@
#include <libinput.h>
#include <libinput-util.h>
#include "shared.h"
#define clip(val_, min_, max_) min((max_), max((min_), (val_)))
struct tools_options options;
struct touch {
int active;
int x, y;
@ -95,12 +98,6 @@ msg(const char *fmt, ...)
va_end(args);
}
static void
usage(void)
{
printf("%s [path/to/device]\n", program_invocation_short_name);
}
static gboolean
draw(GtkWidget *widget, cairo_t *cr, gpointer data)
{
@ -361,20 +358,20 @@ static void
handle_event_axis(struct libinput_event *ev, struct window *w)
{
struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
enum libinput_pointer_axis axis = libinput_event_pointer_get_axis(p);
double v = libinput_event_pointer_get_axis_value(p);
double v, h;
switch (axis) {
case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL:
v = libinput_event_pointer_get_axis_value(p,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
h = libinput_event_pointer_get_axis_value(p,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
if (v != 0.0) {
w->vy += (int)v;
w->vy = clip(w->vy, 0, w->height);
break;
case LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL:
}
if (h != 0.0) {
w->hx += (int)v;
w->hx = clip(w->hx, 0, w->width);
break;
default:
abort();
}
}
@ -495,35 +492,6 @@ sockets_init(struct libinput *li)
g_io_add_watch(c, G_IO_IN, handle_event_libinput, li);
}
static int
parse_opts(int argc, char *argv[])
{
while (1) {
static struct option long_options[] = {
{ "help", no_argument, 0, 'h' },
};
int option_index = 0;
int c;
c = getopt_long(argc, argv, "h", long_options,
&option_index);
if (c == -1)
break;
switch(c) {
case 'h':
usage();
return 0;
default:
usage();
return 1;
}
}
return 0;
}
static int
open_restricted(const char *path, int flags, void *user_data)
{
@ -551,16 +519,18 @@ main(int argc, char *argv[])
gtk_init(&argc, &argv);
if (parse_opts(argc, argv) != 0)
tools_init_options(&options);
if (tools_parse_args(argc, argv, &options) != 0)
return 1;
udev = udev_new();
if (!udev)
error("Failed to initialize udev\n");
li = libinput_udev_create_context(&interface, &w, udev);
if (!li || libinput_udev_assign_seat(li, "seat0") != 0)
error("Failed to initialize context from udev\n");
li = tools_open_backend(&options, &w, &interface);
if (!li)
return 1;
window_init(&w);
sockets_init(li);

266
tools/shared.c Normal file
View file

@ -0,0 +1,266 @@
/*
* 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.
*/
#define _GNU_SOURCE
#include <config.h>
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libudev.h>
#include "shared.h"
enum options {
OPT_DEVICE,
OPT_UDEV,
OPT_HELP,
OPT_VERBOSE,
OPT_TAP_ENABLE,
OPT_TAP_DISABLE,
OPT_NATURAL_SCROLL_ENABLE,
OPT_NATURAL_SCROLL_DISABLE,
OPT_LEFT_HANDED_ENABLE,
OPT_LEFT_HANDED_DISABLE,
};
static void
log_handler(struct libinput *li,
enum libinput_log_priority priority,
const char *format,
va_list args)
{
vprintf(format, args);
}
void
tools_usage()
{
printf("Usage: %s [options] [--udev [<seat>]|--device /dev/input/event0]\n"
"--udev <seat>.... Use udev device discovery (default).\n"
" Specifying a seat ID is optional.\n"
"--device /path/to/device .... open the given device only\n"
"\n"
"Features:\n"
"--enable-tap\n"
"--disable-tap.... enable/disable tapping\n"
"--enable-natural-scrolling\n"
"--disable-natural-scrolling.... enable/disable natural scrolling\n"
"--enable-left-handed\n"
"--disable-left-handed.... enable/disable left-handed button configuration\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"
"\n"
"Other options:\n"
"--verbose ....... Print debugging output.\n"
"--help .......... Print this help.\n",
program_invocation_short_name);
}
void
tools_init_options(struct tools_options *options)
{
memset(options, 0, sizeof(*options));
options->tapping = -1;
options->natural_scroll = -1;
options->left_handed = -1;
options->backend = BACKEND_UDEV;
options->seat = "seat0";
}
int
tools_parse_args(int argc, char **argv, struct tools_options *options)
{
while (1) {
int c;
int option_index = 0;
static struct option opts[] = {
{ "device", 1, 0, OPT_DEVICE },
{ "udev", 0, 0, OPT_UDEV },
{ "help", 0, 0, OPT_HELP },
{ "verbose", 0, 0, OPT_VERBOSE },
{ "enable-tap", 0, 0, OPT_TAP_ENABLE },
{ "disable-tap", 0, 0, OPT_TAP_DISABLE },
{ "enable-natural-scrolling", 0, 0, OPT_NATURAL_SCROLL_ENABLE },
{ "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 },
{ 0, 0, 0, 0}
};
c = getopt_long(argc, argv, "h", opts, &option_index);
if (c == -1)
break;
switch(c) {
case 'h': /* --help */
case OPT_HELP:
tools_usage();
exit(0);
case OPT_DEVICE: /* --device */
options->backend = BACKEND_DEVICE;
if (!optarg) {
tools_usage();
return 1;
}
options->device = optarg;
break;
case OPT_UDEV: /* --udev */
options->backend = BACKEND_UDEV;
if (optarg)
options->seat = optarg;
break;
case OPT_VERBOSE: /* --verbose */
options->verbose = 1;
break;
case OPT_TAP_ENABLE:
options->tapping = 1;
break;
case OPT_TAP_DISABLE:
options->tapping = 0;
break;
case OPT_NATURAL_SCROLL_ENABLE:
options->natural_scroll = 1;
break;
case OPT_NATURAL_SCROLL_DISABLE:
options->natural_scroll = 0;
break;
case OPT_LEFT_HANDED_ENABLE:
options->left_handed = 1;
break;
case OPT_LEFT_HANDED_DISABLE:
options->left_handed = 0;
break;
default:
tools_usage();
return 1;
}
}
if (optind < argc) {
tools_usage();
return 1;
}
return 0;
}
static struct libinput *
open_udev(const struct libinput_interface *interface,
void *userdata,
const char *seat,
int verbose)
{
struct libinput *li;
struct udev *udev = udev_new();
if (!udev) {
fprintf(stderr, "Failed to initialize udev\n");
return NULL;
}
li = libinput_udev_create_context(interface, userdata, udev);
if (!li) {
fprintf(stderr, "Failed to initialize context from udev\n");
goto out;
}
if (verbose) {
libinput_log_set_handler(li, log_handler);
libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG);
}
if (libinput_udev_assign_seat(li, seat)) {
fprintf(stderr, "Failed to set seat\n");
libinput_unref(li);
li = NULL;
goto out;
}
out:
udev_unref(udev);
return li;
}
static struct libinput *
open_device(const struct libinput_interface *interface,
void *userdata,
const char *path,
int verbose)
{
struct libinput_device *device;
struct libinput *li;
li = libinput_path_create_context(interface, userdata);
if (!li) {
fprintf(stderr, "Failed to initialize context from %s\n", path);
return NULL;
}
if (verbose) {
libinput_log_set_handler(li, log_handler);
libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG);
}
device = libinput_path_add_device(li, path);
if (!device) {
fprintf(stderr, "Failed to initialized device %s\n", path);
libinput_unref(li);
li = NULL;
}
return li;
}
struct libinput *
tools_open_backend(struct tools_options *options,
void *userdata,
const struct libinput_interface *interface)
{
struct libinput *li = NULL;
if (options->backend == BACKEND_UDEV) {
li = open_udev(interface, userdata, options->seat, options->verbose);
} else if (options->backend == BACKEND_DEVICE) {
li = open_device(interface, userdata, options->device, options->verbose);
} else
abort();
return li;
}
void
tools_device_apply_config(struct libinput_device *device,
struct tools_options *options)
{
if (options->tapping != -1)
libinput_device_config_tap_set_enabled(device, options->tapping);
if (options->natural_scroll != -1)
libinput_device_config_scroll_set_natural_scroll_enabled(device,
options->natural_scroll);
if (options->left_handed != -1)
libinput_device_config_left_handed_set(device, options->left_handed);
}

53
tools/shared.h Normal file
View file

@ -0,0 +1,53 @@
/*
* 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.
*/
#ifndef _SHARED_H_
#define _SHARED_H_
#include <libinput.h>
enum tools_backend {
BACKEND_DEVICE,
BACKEND_UDEV
};
struct tools_options {
enum tools_backend backend;
const char *device; /* if backend is BACKEND_DEVICE */
const char *seat; /* if backend is BACKEND_UDEV */
int verbose;
int tapping;
int natural_scroll;
int left_handed;
};
void tools_init_options(struct tools_options *options);
int tools_parse_args(int argc, char **argv, struct tools_options *options);
struct libinput* tools_open_backend(struct tools_options *options,
void *userdata,
const struct libinput_interface *interface);
void tools_device_apply_config(struct libinput_device *device,
struct tools_options *options);
void tools_usage();
#endif