diff --git a/configure.ac b/configure.ac
index 33e380ce..1532e819 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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}
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 8f05bd2c..f66b47f1 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -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
diff --git a/doc/absolute-axes.dox b/doc/absolute-axes.dox
new file mode 100644
index 00000000..7d205f62
--- /dev/null
+++ b/doc/absolute-axes.dox
@@ -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
+Transformation
+Matrix article 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.
+
+*/
diff --git a/doc/clickpad-softbuttons.dox b/doc/clickpad-softbuttons.dox
new file mode 100644
index 00000000..2653c3b7
--- /dev/null
+++ b/doc/clickpad-softbuttons.dox
@@ -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. "ClickPad" is
+a trademark by Synaptics Inc. 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 INPUT_PROP_BUTTONPAD 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.
+
+*/
diff --git a/doc/device-configuration-via-udev.dox b/doc/device-configuration-via-udev.dox
new file mode 100644
index 00000000..bee36598
--- /dev/null
+++ b/doc/device-configuration-via-udev.dox
@@ -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:
+
+
LIBINPUT_CALIBRATION_MATRIX
+
Sets the calibration matrix, see
+libinput_device_config_calibration_get_default_matrix(). If unset,
+defaults to the identity matrix.
+
ID_SEAT
+
Assigns the physical seat for this device. See
+libinput_seat_get_physical_name(). Defaults to "seat0".
+
WL_SEAT
+
Assigns the logical seat for this device. See
+libinput_seat_get_logical_name()
+context. Defaults to "default".
+
MOUSE_DPI
+
HW resolution and sampling frequency of a relative pointer device.
+See @ref motion_normalization for details.
+
+
MOUSE_WHEEL_CLICK_ANGLE
+
The angle in degrees for each click on a mouse wheel. See
+libinput_pointer_get_axis_source() for details.
+
+
+
+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
+
+*/
diff --git a/doc/dot/seats-sketch-libinput.gv b/doc/dot/seats-sketch-libinput.gv
new file mode 100644
index 00000000..b0216563
--- /dev/null
+++ b/doc/dot/seats-sketch-libinput.gv
@@ -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
+}
diff --git a/doc/dot/seats-sketch.gv b/doc/dot/seats-sketch.gv
new file mode 100644
index 00000000..3647cef8
--- /dev/null
+++ b/doc/dot/seats-sketch.gv
@@ -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
+}
diff --git a/doc/libinput.doxygen.in b/doc/libinput.doxygen.in
index 9800f808..155880a2 100644
--- a/doc/libinput.doxygen.in
+++ b/doc/libinput.doxygen.in
@@ -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
diff --git a/doc/normalization-of-relative-motion.dox b/doc/normalization-of-relative-motion.dox
new file mode 100644
index 00000000..aaa1735f
--- /dev/null
+++ b/doc/normalization-of-relative-motion.dox
@@ -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 MOUSE_DPI for this information. This property is usually
+set via the udev hwdb.
+
+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.
+
+*/
+
diff --git a/doc/palm-detection.dox b/doc/palm-detection.dox
new file mode 100644
index 00000000..4e839e62
--- /dev/null
+++ b/doc/palm-detection.dox
@@ -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 trackpoint, 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.
+
+
+*/
diff --git a/doc/scrolling.dox b/doc/scrolling.dox
new file mode 100644
index 00000000..3ded909d
--- /dev/null
+++ b/doc/scrolling.dox
@@ -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
+pointing stick 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).
+
+*/
diff --git a/doc/seats.dox b/doc/seats.dox
new file mode 100644
index 00000000..5eec5ded
--- /dev/null
+++ b/doc/seats.dox
@@ -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.
+
+
+*/
diff --git a/doc/svg/button-scrolling.svg b/doc/svg/button-scrolling.svg
new file mode 100644
index 00000000..476c9c7d
--- /dev/null
+++ b/doc/svg/button-scrolling.svg
@@ -0,0 +1,292 @@
+
+
+
+
diff --git a/doc/svg/edge-scrolling.svg b/doc/svg/edge-scrolling.svg
new file mode 100644
index 00000000..cba72c50
--- /dev/null
+++ b/doc/svg/edge-scrolling.svg
@@ -0,0 +1,126 @@
+
+
+
+
diff --git a/doc/svg/palm-detection.svg b/doc/svg/palm-detection.svg
new file mode 100644
index 00000000..9fb6077d
--- /dev/null
+++ b/doc/svg/palm-detection.svg
@@ -0,0 +1,122 @@
+
+
+
+
diff --git a/doc/svg/twofinger-scrolling.svg b/doc/svg/twofinger-scrolling.svg
new file mode 100644
index 00000000..e182a7fe
--- /dev/null
+++ b/doc/svg/twofinger-scrolling.svg
@@ -0,0 +1,165 @@
+
+
+
+
diff --git a/doc/t440-support.dox b/doc/t440-support.dox
new file mode 100644
index 00000000..277dbe8e
--- /dev/null
+++ b/doc/t440-support.dox
@@ -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
+trackstick. 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 INPUT_PROP_TOPBUTTONPAD
+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. Kernel
+patches 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 the
+kernel source (search for "topbuttonpad_pnp_ids").
+
+*/
diff --git a/doc/tapping.dox b/doc/tapping.dox
new file mode 100644
index 00000000..f603b7d5
--- /dev/null
+++ b/doc/tapping.dox
@@ -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)
+
+*/
diff --git a/src/Makefile.am b/src/Makefile.am
index 87116ede..2442794a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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 \
diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c
index 616080fa..26a86141 100644
--- a/src/evdev-mt-touchpad-edge-scroll.c
+++ b/src/evdev-mt-touchpad-edge-scroll.c
@@ -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;
}
diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c
index c34b2034..0e37c5ee 100644
--- a/src/evdev-mt-touchpad-tap.c
+++ b/src/evdev-mt-touchpad-tap.c
@@ -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));
diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index 964900d2..34b107e2 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -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 {
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index 14502882..5807f08c 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -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;
diff --git a/src/evdev.c b/src/evdev.c
index a0e1466a..939d266c 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -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;
diff --git a/src/evdev.h b/src/evdev.h
index 41368466..b6f31acf 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -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)
diff --git a/src/filter.c b/src/filter.c
index afb87ef2..89390d12 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -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,
diff --git a/src/libinput-private.h b/src/libinput-private.h
index 49232f9e..d7f28e34 100644
--- a/src/libinput-private.h
+++ b/src/libinput-private.h
@@ -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,
diff --git a/src/libinput-util.c b/src/libinput-util.c
index 923e1162..49e297af 100644
--- a/src/libinput-util.c
+++ b/src/libinput-util.c
@@ -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=
+ * 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;
+}
diff --git a/src/libinput-util.h b/src/libinput-util.h
index fd803975..ca26b09f 100644
--- a/src/libinput-util.h
+++ b/src/libinput-util.h
@@ -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 */
diff --git a/src/libinput.c b/src/libinput.c
index 527c8894..2469c864 100644
--- a/src/libinput.c
+++ b/src/libinput.c
@@ -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);
diff --git a/src/libinput.h b/src/libinput.h
index 77d02076..649484d6 100644
--- a/src/libinput.h
+++ b/src/libinput.h
@@ -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:
- *
- *
LIBINPUT_CALIBRATION_MATRIX
- *
Sets the calibration matrix, see
- * libinput_device_config_calibration_get_default_matrix(). If unset,
- * defaults to the identity matrix.
- *
ID_SEAT
- *
Assigns the physical seat for this device. See
- * libinput_seat_get_physical_name(). Defaults to "seat0".
- *
WL_SEAT
- *
Assigns the logical seat for this device. See
- * libinput_seat_get_logical_name()
- * context. Defaults to "default".
- *
MOUSE_DPI
- *
HW resolution and sampling frequency of a relative pointer device.
- * See @ref motion_normalization for details.
- *