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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + C + V + B + N + M + J + H + G + F + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + A + B + + + + 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. - *
- *
- * - * 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 diff --git a/src/libinput.sym b/src/libinput.sym index 5c47e535..8f4672d6 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -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; diff --git a/test/build-pedantic.c b/test/build-pedantic.c index 1dfb67df..920fc4ab 100644 --- a/test/build-pedantic.c +++ b/test/build-pedantic.c @@ -3,6 +3,6 @@ /* This is a build-test only */ int -main(int argc, char **argv) { +main(void) { return 0; } diff --git a/test/litest-mouse.c b/test/litest-mouse.c index cf0a7a05..3f68521f 100644 --- a/test/litest-mouse.c +++ b/test/litest-mouse.c @@ -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, diff --git a/test/litest-trackpoint.c b/test/litest-trackpoint.c index 061835fc..09f8b704 100644 --- a/test/litest-trackpoint.c +++ b/test/litest-trackpoint.c @@ -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, diff --git a/test/litest.c b/test/litest.c index 6e476f36..c436771f 100644 --- a/test/litest.c +++ b/test/litest.c @@ -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) { diff --git a/test/litest.h b/test/litest.h index 95e27c17..a34c4407 100644 --- a/test/litest.h +++ b/test/litest.h @@ -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, diff --git a/test/misc.c b/test/misc.c index 480f1594..8036eaf2 100644 --- a/test/misc.c +++ b/test/misc.c @@ -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); } diff --git a/test/path.c b/test/path.c index 3a2bf2f8..243edd79 100644 --- a/test/path.c +++ b/test/path.c @@ -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); diff --git a/test/pointer.c b/test/pointer.c index dfed6b7d..45e0d57e 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -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 */ diff --git a/test/touch.c b/test/touch.c index 5c9b72a8..29890a41 100644 --- a/test/touch.c +++ b/test/touch.c @@ -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 diff --git a/test/touchpad.c b/test/touchpad.c index 934674c1..c5edecc8 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -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); diff --git a/test/trackpoint.c b/test/trackpoint.c index 1d1a10a0..2708bad2 100644 --- a/test/trackpoint.c +++ b/test/trackpoint.c @@ -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); } diff --git a/test/udev.c b/test/udev.c index f5d2c883..c351bed5 100644 --- a/test/udev.c +++ b/test/udev.c @@ -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); diff --git a/tools/Makefile.am b/tools/Makefile.am index aadd874a..cebcd729 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -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 diff --git a/tools/event-debug.c b/tools/event-debug.c index 6dca8001..ad64f4be 100644 --- a/tools/event-debug.c +++ b/tools/event-debug.c @@ -23,7 +23,6 @@ #define _GNU_SOURCE #include #include -#include #include #include #include @@ -33,85 +32,16 @@ #include #include "linux/input.h" #include -#include #include -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 []|--device /dev/input/event0]\n" - "--verbose ....... Print debugging output.\n" - "--udev .... 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; } diff --git a/tools/event-gui.c b/tools/event-gui.c index 4d531f0e..05169b1d 100644 --- a/tools/event-gui.c +++ b/tools/event-gui.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -40,8 +39,12 @@ #include #include +#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); diff --git a/tools/shared.c b/tools/shared.c new file mode 100644 index 00000000..b4f41d04 --- /dev/null +++ b/tools/shared.c @@ -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 + +#include +#include +#include +#include +#include +#include + +#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 []|--device /dev/input/event0]\n" + "--udev .... 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); +} diff --git a/tools/shared.h b/tools/shared.h new file mode 100644 index 00000000..4388cea6 --- /dev/null +++ b/tools/shared.h @@ -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 + +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