diff --git a/CODING_STYLE b/CODING_STYLE new file mode 100644 index 00000000..1a64ffe6 --- /dev/null +++ b/CODING_STYLE @@ -0,0 +1,79 @@ +- Indentation in tabs, 8 characters wide, spaces after the tabs where + vertical alignment is required (see below) + +- Max line width 80ch, do not break up printed strings though + +- Break up long lines at logical groupings, one line for each logical group + + int a = somelongname() + + someotherlongname(); + + if (a < 0 && + (b > 20 & d < 10) && + d != 0.0) + + + somelongfunctioncall(arg1, + arg2, + arg3); + +- Function declarations: return type on separate line, {} on separate line, + arguments broken up as above. + + static inline int + foobar(int a, int b) + { + + } + + void + somenamethatiswaytoolong(int a, + int b, + int c) + { + } + +- /* comments only */, no // comments + +- variable_name, not VariableName or variableName. same for functions. + +- no typedefs of structs, enums, unions + +- if it generates a compiler warning, it needs to be fixed +- if it generates a static checker warning, it needs to be fixed or + commented + +- declare variables at the top, try to keep them as local as possible. + Exception: if the same variable is re-used in multiple blocks, declare it + at the top. + + int a; + int c; + + if (foo) { + int b; + + c = get_value(); + usevalue(c); + } + + if (bar) { + c = get_value(); + useit(c); + } + +- public functions MUST be doxygen-commented, use doxygen's @foo rather than + \foo notation + +- include "config.h" comes first, followed by system headers, followed by + external library headers, followed by internal headers. + sort alphabetically where it makes sense (specifically system headers) + + #include "config.h" + + #include + #include + + #include + + #include "libinput-private.h" diff --git a/COPYING b/COPYING index efc1a949..93ac3bca 100644 --- a/COPYING +++ b/COPYING @@ -4,7 +4,7 @@ Copyright © 2010-2012 Intel Corporation Copyright © 2010-2011 Benjamin Franzke Copyright © 2011-2012 Collabora, Ltd. Copyright © 2013-2014 Jonas Ådahl -Copyright © 2013-2014 Red Hat, Inc. +Copyright © 2013-2015 Red Hat, Inc. Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that @@ -23,3 +23,11 @@ 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. + +libinput ships a copy of the GPL-licensed Linux kernel's linux/input.h +header file. [1] This does not make libinput GPL. +This copy is provided to provide consistent behavior regardless which kernel +version libinput is compiled against. The header is used during compilation +only, libinput does not link against GPL libraries. + +[1] http://cgit.freedesktop.org/wayland/libinput/tree/include/linux/input.h diff --git a/README.txt b/README.txt index c5dc61e5..07e8fa31 100644 --- a/README.txt +++ b/README.txt @@ -9,22 +9,78 @@ applications that need to directly deal with input devices. It provides device detection, device handling, input device event processing and abstraction so minimize the amount of custom input code the user of libinput need to provide the common set of functionality that users expect. - Input event processing includes scaling touch coordinates, generating pointer events from touchpads, pointer acceleration, etc. -libinput originates from weston, the Wayland reference compositor. +libinput originates from +[weston](http://cgit.freedesktop.org/wayland/weston/), the Wayland reference +compositor. + +Architecture +------------ + +libinput is not used directly by applications, rather it is used by the +xf86-input-libinput X.Org driver or wayland compositors. The typical +software stack for a system running Wayland is: + +@dotfile libinput-stack-wayland.gv + +Where the Wayland compositor may be Weston, mutter, KWin, etc. Note that +Wayland encourages the use of toolkits, so the Wayland client (your +application) does not usually talk directly to the compositor but rather +employs a toolkit (e.g. GTK) to do so. + +The simplified software stack for a system running X.Org is: + +@dotfile libinput-stack-xorg.gv + +Again, on a modern system the application does not usually talk directly to +the X server using Xlib but rather employs a toolkit to do so. + +Source code +----------- The source code of libinput can be found at: http://cgit.freedesktop.org/wayland/libinput -For more information, visit: +For a list of current and past releases visit: http://www.freedesktop.org/wiki/Software/libinput/ +Reporting Bugs +-------------- + Bugs can be filed in the libinput component of Wayland: https://bugs.freedesktop.org/enter_bug.cgi?product=Wayland&component=libinput -Online API documentation: +Where possible, please provide an +[evemu](http://www.freedesktop.org/wiki/Evemu/) recording of the input +device and/or the event sequence in question. + +Documentation +------------- + +Developer API documentation: http://wayland.freedesktop.org/libinput/doc/latest/modules.html +High-level documentation about libinput's features: +http://wayland.freedesktop.org/libinput/doc/latest/pages.html + +License +------- + +libinput is licensed under the MIT license. + +> 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. + +See the [COPYING](http://cgit.freedesktop.org/wayland/libinput/tree/COPYING) +file for the full license information. + */ diff --git a/configure.ac b/configure.ac index d321e87b..0265042c 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], [15]) +m4_define([libinput_minor_version], [17]) m4_define([libinput_micro_version], [0]) m4_define([libinput_version], [libinput_major_version.libinput_minor_version.libinput_micro_version]) @@ -31,7 +31,7 @@ AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) # b) If interfaces have been changed or added, but binary compatibility has # been preserved, change to C+1:0:A+1 # c) If the interface is the same as the previous version, change to C:R+1:A -LIBINPUT_LT_VERSION=12:0:2 +LIBINPUT_LT_VERSION=12:2:2 AC_SUBST(LIBINPUT_LT_VERSION) AM_SILENT_RULES([yes]) diff --git a/doc/Makefile.am b/doc/Makefile.am index 48e68b8a..113666cc 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -13,6 +13,7 @@ header_files = \ $(srcdir)/absolute-axes.dox \ $(srcdir)/clickpad-softbuttons.dox \ $(srcdir)/device-configuration-via-udev.dox \ + $(srcdir)/faqs.dox \ $(srcdir)/normalization-of-relative-motion.dox \ $(srcdir)/palm-detection.dox \ $(srcdir)/scrolling.dox \ @@ -25,9 +26,16 @@ header_files = \ diagram_files = \ $(srcdir)/dot/seats-sketch.gv \ $(srcdir)/dot/seats-sketch-libinput.gv \ + $(srcdir)/dot/libinput-stack-wayland.gv \ + $(srcdir)/dot/libinput-stack-xorg.gv \ + $(srcdir)/dot/libinput-stack-gnome.gv \ + $(srcdir)/svg/software-buttons.svg \ + $(srcdir)/svg/clickfinger.svg \ $(srcdir)/svg/button-scrolling.svg \ $(srcdir)/svg/edge-scrolling.svg \ $(srcdir)/svg/palm-detection.svg \ + $(srcdir)/svg/tap-n-drag.svg \ + $(srcdir)/svg/top-software-buttons.svg \ $(srcdir)/svg/twofinger-scrolling.svg html/index.html: libinput.doxygen $(header_files) $(diagram_files) diff --git a/doc/clickpad-softbuttons.dox b/doc/clickpad-softbuttons.dox index 8d919369..a4f2e444 100644 --- a/doc/clickpad-softbuttons.dox +++ b/doc/clickpad-softbuttons.dox @@ -24,15 +24,6 @@ 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. @@ -40,6 +31,8 @@ Left, right and middle button events can be triggered as follows: - if there is a finger in both the left and right button area, a click generates middle button events. +@image html software-buttons.svg "Left, right and middle-button click with software button areas" + 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 @@ -69,9 +62,18 @@ 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. +@image html clickfinger.svg "One, two and three-finger click with Clickfinger behavior" + +On some touchpads, libinput imposes a limit on how the fingers may be placed +on the touchpad. In the most common use-case this allows for a user to +trigger a click with the thumb while leaving the pointer-moving finger on +the touchpad. + +@image html clickfinger-distance.svg "Illustration of the distance detection algorithm" + +In the illustration above the red area marks the proximity area around the +first finger. Since the thumb is outside of that area libinput considers the +click a single-finger click rather than a two-finger click. Clickfinger configuration can be enabled through the libinput_device_config_click_set_method() call. If clickfingers are diff --git a/doc/dot/libinput-stack-gnome.gv b/doc/dot/libinput-stack-gnome.gv new file mode 100644 index 00000000..4e0ccf43 --- /dev/null +++ b/doc/dot/libinput-stack-gnome.gv @@ -0,0 +1,30 @@ +digraph stack +{ + compound=true; + rankdir="LR"; + node [ + shape="box"; + ] + + gcc -> gsettings + + xf86libinput -> libinput + + subgraph cluster0 { + label="X.Org"; + xf86libinput [label="xf86-input-libinput"]; + xserver [label="X Server"]; + xserver -> xf86libinput; + } + + gcc [label="gnome-control-center"]; + + subgraph cluster3 { + gsettings + } + + gsd [label="gnome-settings-daemon"]; + + gsd -> gsettings + gsd -> xserver +} diff --git a/doc/dot/libinput-stack-wayland.gv b/doc/dot/libinput-stack-wayland.gv new file mode 100644 index 00000000..20b334ea --- /dev/null +++ b/doc/dot/libinput-stack-wayland.gv @@ -0,0 +1,17 @@ +digraph stack +{ + rankdir="LR"; + node [ + shape="box"; + ] + + kernel [label="Kernel"]; + + libinput; + compositor [label="Wayland Compositor"]; + client [label="Wayland Client"]; + + kernel -> libinput + libinput -> compositor + compositor -> client +} diff --git a/doc/dot/libinput-stack-xorg.gv b/doc/dot/libinput-stack-xorg.gv new file mode 100644 index 00000000..e50f2418 --- /dev/null +++ b/doc/dot/libinput-stack-xorg.gv @@ -0,0 +1,19 @@ +digraph stack +{ + rankdir="LR"; + node [ + shape="box"; + ] + + kernel [label="Kernel"]; + + libinput; + xf86libinput [label="xf86-input-libinput"]; + xserver [label="X Server"]; + client [label="X11 client"]; + + kernel -> libinput + libinput -> xf86libinput + xf86libinput -> xserver + xserver -> client +} diff --git a/doc/faqs.dox b/doc/faqs.dox new file mode 100644 index 00000000..92a42285 --- /dev/null +++ b/doc/faqs.dox @@ -0,0 +1,51 @@ +/** +@page faq FAQs - Frequently Asked Questions + +Frequently asked questions about libinput. + +@section faq_kinetic_scrolling Kinetic scrolling does not work + +The X.Org synaptics driver implemented kinetic scrolling in the driver. It +measures the scroll speed and once the finger leaves the touchpad the driver +keeps sending scroll events for a predetermined time. This effectively +provides for kinetic scrolling without client support but triggers an +unfixable [bug](https://bugs.freedesktop.org/show_bug.cgi?id=38909): the +client cannot know that the events are from a kinetic scroll source. Scroll +events in X are always sent to the current cursor position, a movement of the +cursor after lifting the finger will send the kinetic scroll events to the +new client, something the user does not usually expect. A key event during +the kinetic scroll procedure causes side-effects such as triggering zoom. + +libinput does not implement kinetic scrolling for touchpads. Instead it +provides the libinput_event_pointer_get_axis_source() function that enables +callers to implement kinetic scrolling on a per-widget basis, see @ref +scroll_sources. + +@section faq_gpl Is libinput GPL-licensed? + +No, libinput is MIT licensed. The Linux kernel header file linux/input.h in +libinput's tree is provded to ensure the same behavior regardless of which +kernel version libinput is built on. It does not make libinput GPL-licensed. + +@section faq_config_options Where is the configuration stored? + +libinput does not store configuration options, it is up to the caller to +manage these and decide which configuration option to apply to each device. +This must be done at startup, after a resume and whenever a new device is +detected. + +In a GNOME X.Org stack a user would usually toggle an option in +the gnome-control-center which adjusts a gsettings entry. That change is +picked up by gnome-settings-daemon and applied to the device by adjusting +input device properties that the xf86-input-libinput driver provides. +The input device property changes map to the respective libinput +configuration options. + +@dotfile libinput-stack-gnome.gv + +This has an effect on the availability of configuration options: if an +option is not exposed by the intermediary, it cannot be configured by the +client. Also some configuration options that are provided by the +intermediary may not be libinput-specific configuration options. + +*/ diff --git a/doc/palm-detection.dox b/doc/palm-detection.dox index a03f9c14..d787455e 100644 --- a/doc/palm-detection.dox +++ b/doc/palm-detection.dox @@ -74,8 +74,9 @@ Notable behaviors of libinput's disable-while-typing feature: - Some keys do not trigger the timeout, specifically some modifier keys (Ctrl, Alt, Shift, and Fn). Actions such as Ctrl + click thus stay responsive. -- Touches started while the touchpad is disabled do not control the cursor, - it is thus possible to rest the palm on the touchpad while typing. +- Touches started while typing do not control the cursor even after typing + has stopped, it is thus possible to rest the palm on the touchpad while + typing. - Physical buttons work even while the touchpad is disabled. This includes software-emulated buttons. diff --git a/doc/scrolling.dox b/doc/scrolling.dox index b5a01cf8..94aa8158 100644 --- a/doc/scrolling.dox +++ b/doc/scrolling.dox @@ -63,4 +63,23 @@ the motion events. Cross-device scrolling is not supported but for one exception: libinput's @ref t440_support enables the use of the middle button for button scrolling (even when the touchpad is disabled). +@section scroll_sources Scroll sources + +libinput provides a pointer axis *source* for each scroll event. The +source can be obtained with the libinput_event_pointer_get_axis_source() +function and is one of **wheel**, **finger**, or **continuous**. The source +information lets a caller decide when to implement kinetic scrolling. +Usually, a caller will process events of source wheel as they come in. +For events of source finger a caller should calculate the velocity of the +scroll motion and upon finger release start a kinetic scrolling motion (i.e. +continue executing a scroll according to some friction factor). +libinput expects the caller to be in charge of widget handling, the source +information is thus enough to provide kinetic scrolling on a per-widget +basis. A caller should cancel kinetic scrolling when the pointer leaves the +current widget or when a key is pressed. + +See the libinput_event_pointer_get_axis_source() for details on the +behavior of each scroll source. + +See also http://who-t.blogspot.com.au/2015/03/libinput-scroll-sources.html */ diff --git a/doc/svg/clickfinger-distance.svg b/doc/svg/clickfinger-distance.svg new file mode 100644 index 00000000..ac659cfe --- /dev/null +++ b/doc/svg/clickfinger-distance.svg @@ -0,0 +1,106 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/doc/svg/clickfinger.svg b/doc/svg/clickfinger.svg new file mode 100644 index 00000000..b92dcb44 --- /dev/null +++ b/doc/svg/clickfinger.svg @@ -0,0 +1,207 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/svg/software-buttons.svg b/doc/svg/software-buttons.svg new file mode 100644 index 00000000..903535c8 --- /dev/null +++ b/doc/svg/software-buttons.svg @@ -0,0 +1,175 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/svg/tap-n-drag.svg b/doc/svg/tap-n-drag.svg new file mode 100644 index 00000000..5c84beb7 --- /dev/null +++ b/doc/svg/tap-n-drag.svg @@ -0,0 +1,810 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + a) Tap + b) Hold down + c) Move + d) Reset finger, move (optional) + f) Tap to end (optional) + e) Release + diff --git a/doc/svg/top-software-buttons.svg b/doc/svg/top-software-buttons.svg new file mode 100644 index 00000000..ab311240 --- /dev/null +++ b/doc/svg/top-software-buttons.svg @@ -0,0 +1,213 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/t440-support.dox b/doc/t440-support.dox index 277dbe8e..652a6d0b 100644 --- a/doc/t440-support.dox +++ b/doc/t440-support.dox @@ -11,21 +11,10 @@ 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: +and clicks can be triggered by pressing the touchpad down with a finger in +the respective area: -@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 +@image html top-software-buttons.svg "Left, right and middle-button click with top software button areas" This page only covers the top software buttons, the bottom button behavior is covered in @ref clickpad_softbuttons "Clickpad software buttons". @@ -36,36 +25,77 @@ property. @section t440_support_btn_size Size of the buttons +The size of the buttons matches the visual markings on this touchpad. +The width of the left and right buttons is approximately 42% of the +touchpad's width, the middle button is centered and assigned 16% of the +touchpad width. + 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. +their own behavior. Semantically attached to the trackstick device, libinput +re-routes events from these buttons to appear through the trackstick device. -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. +@dot +digraph top_button_routing +{ + rankdir="LR"; + node [shape="box";] + + trackstick [label="trackstick kernel device"]; + touchpad [label="touchpad kernel device"]; + + subgraph cluster0 { + bgcolor = floralwhite + label = "libinput" + + libinput_ts [label="trackstick libinput_device" + style=filled + fillcolor=white]; + libinput_tp [label="touchpad libinput_device" + style=filled + fillcolor=white]; + + libinput_tp -> libinput_ts [constraint=false + color="red4"]; + } + + trackstick -> libinput_ts [arrowhead="none"] + touchpad -> libinput_tp [color="red4"] + + events_tp [label="other touchpad events"]; + events_topbutton [label="top sofware button events"]; + + libinput_tp -> events_tp [arrowhead="none"] + libinput_ts -> events_topbutton [color="red4"] +} +@enddot + + +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. +The firmware on the first generation of 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. + +The October 2014 refresh of these laptops do not have this firmware bug +anymore and should work without per-device patches, though +this kernel commit is required. For a complete list of supported touchpads check the diff --git a/doc/tapping.dox b/doc/tapping.dox index 7eb81e62..1337fc53 100644 --- a/doc/tapping.dox +++ b/doc/tapping.dox @@ -27,15 +27,22 @@ libinput_device_config_tap_set_enabled() for details. 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. +the finger around can thus drag the selected item on the screen. -An alternative method to end a drag process is to tap immediately after -lifting the finger. The full sequence is thus: tap, finger down, drag, -finger up, tap. +@image html tap-n-drag.svg "Tap-and-drag process" + +The above diagram explains the process, a tap (a) followed by a finger held +down (b) starts the drag process and logically holds the left mouse button +down. A movement of the finger (c) will drag the selected item until the +finger is relased (e). If needed, the finger's position can be reset by +lifting and quickly setting it down again on the touchpad (d). This will be +interpreted as continuing move and is especially useful on small touchpads +or with slow pointer acceleration. +The release of the mouse buttons after the finger release (e) is triggered +by a timeout. To release the button immediately, simply tap again (f). + +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 diff --git a/src/evdev-middle-button.c b/src/evdev-middle-button.c index 328cf6c3..c9894865 100644 --- a/src/evdev-middle-button.c +++ b/src/evdev-middle-button.c @@ -1,5 +1,5 @@ /* - * Copyright © 2014 Red Hat, Inc. + * Copyright © 2014-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -20,6 +20,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include #include "evdev.h" diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 43e983b2..5786ea8b 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -1,5 +1,5 @@ /* - * Copyright © 2014 Red Hat, Inc. + * Copyright © 2014-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -20,6 +20,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include #include #include @@ -392,7 +394,7 @@ tp_button_handle_event(struct tp_dispatch *tp, enum button_event event, uint64_t time) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); enum button_state current = t->button.state; switch(t->button.state) { @@ -478,7 +480,7 @@ tp_process_button(struct tp_dispatch *tp, const struct input_event *e, uint64_t time) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); uint32_t mask = 1 << (e->code - BTN_LEFT); /* Ignore other buttons on clickpads */ @@ -680,7 +682,7 @@ int tp_init_buttons(struct tp_dispatch *tp, struct evdev_device *device) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); struct tp_touch *t; int width, height; double diagonal; @@ -731,7 +733,7 @@ tp_init_buttons(struct tp_dispatch *tp, tp_for_each_touch(tp, t) { t->button.state = BUTTON_STATE_NONE; libinput_timer_init(&t->button.timer, - tp->device->base.seat->libinput, + tp_libinput_context(tp), tp_button_handle_timeout, t); } @@ -782,6 +784,104 @@ tp_post_physical_buttons(struct tp_dispatch *tp, uint64_t time) return 0; } +static inline int +tp_check_clickfinger_distance(struct tp_dispatch *tp, + struct tp_touch *t1, + struct tp_touch *t2) +{ + double x, y; + + if (!t1 || !t2) + return 0; + + x = abs(t1->point.x - t2->point.x); + y = abs(t1->point.y - t2->point.y); + + /* no resolution, so let's assume they're close enough together */ + if (tp->device->abs.fake_resolution) { + int w, h; + + /* Use a maximum of 30% of the touchpad width or height if + * we dont' have resolution. */ + w = tp->device->abs.absinfo_x->maximum - + tp->device->abs.absinfo_x->minimum; + h = tp->device->abs.absinfo_y->maximum - + tp->device->abs.absinfo_y->minimum; + + return (x < w * 0.3 && y < h * 0.3) ? 1 : 0; + } else { + /* maximum spread is 40mm horiz, 20mm vert. Anything wider than that + * is probably a gesture. The y spread is small so we ignore clicks + * with thumbs at the bottom of the touchpad while the pointer + * moving finger is still on the pad */ + + x /= tp->device->abs.absinfo_x->resolution; + y /= tp->device->abs.absinfo_y->resolution; + + return (x < 40 && y < 20) ? 1 : 0; + } + +} + +static uint32_t +tp_clickfinger_set_button(struct tp_dispatch *tp) +{ + uint32_t button; + unsigned int nfingers = tp->nfingers_down; + struct tp_touch *t; + struct tp_touch *first = NULL, + *second = NULL, + *third = NULL; + uint32_t close_touches = 0; + + if (nfingers < 2 || nfingers > 3) + goto out; + + /* two or three fingers down on the touchpad. Check for distance + * between the fingers. */ + tp_for_each_touch(tp, t) { + if (t->state != TOUCH_BEGIN && t->state != TOUCH_UPDATE) + continue; + + if (!first) + first = t; + else if (!second) + second = t; + else if (!third) { + third = t; + break; + } + } + + if (!first || !second) { + nfingers = 1; + goto out; + } + + close_touches |= tp_check_clickfinger_distance(tp, first, second) << 0; + close_touches |= tp_check_clickfinger_distance(tp, second, third) << 1; + close_touches |= tp_check_clickfinger_distance(tp, first, third) << 2; + + switch(__builtin_popcount(close_touches)) { + case 0: nfingers = 1; break; + case 1: nfingers = 2; break; + default: nfingers = 3; break; + } + +out: + switch (nfingers) { + case 0: + case 1: button = BTN_LEFT; break; + case 2: button = BTN_RIGHT; break; + case 3: button = BTN_MIDDLE; break; + default: + button = 0; + break; + } + + return button; +} + static int tp_notify_clickpadbutton(struct tp_dispatch *tp, uint64_t time, @@ -816,14 +916,7 @@ tp_notify_clickpadbutton(struct tp_dispatch *tp, */ if (tp->buttons.click_method == LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER && state == LIBINPUT_BUTTON_STATE_PRESSED) { - switch (tp->nfingers_down) { - case 0: - case 1: button = BTN_LEFT; break; - case 2: button = BTN_RIGHT; break; - case 3: button = BTN_MIDDLE; break; - default: - button = 0; - } + button = tp_clickfinger_set_button(tp); tp->buttons.active = button; if (!button) diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c index 369fdedc..2302d2c0 100644 --- a/src/evdev-mt-touchpad-edge-scroll.c +++ b/src/evdev-mt-touchpad-edge-scroll.c @@ -1,5 +1,5 @@ /* - * Copyright © 2014 Red Hat, Inc. + * Copyright © 2014-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -20,6 +20,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include #include #include @@ -121,7 +123,7 @@ tp_edge_scroll_handle_none(struct tp_dispatch *tp, struct tp_touch *t, enum scroll_event event) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); switch (event) { case SCROLL_EVENT_TOUCH: @@ -149,7 +151,7 @@ tp_edge_scroll_handle_edge_new(struct tp_dispatch *tp, struct tp_touch *t, enum scroll_event event) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); switch (event) { case SCROLL_EVENT_TOUCH: @@ -178,7 +180,7 @@ tp_edge_scroll_handle_edge(struct tp_dispatch *tp, struct tp_touch *t, enum scroll_event event) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); switch (event) { case SCROLL_EVENT_TOUCH: @@ -209,7 +211,7 @@ tp_edge_scroll_handle_area(struct tp_dispatch *tp, struct tp_touch *t, enum scroll_event event) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); switch (event) { case SCROLL_EVENT_TOUCH: @@ -232,7 +234,7 @@ tp_edge_scroll_handle_event(struct tp_dispatch *tp, struct tp_touch *t, enum scroll_event event) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); enum tp_edge_scroll_touch_state current = t->scroll.edge_state; switch (current) { @@ -301,7 +303,7 @@ tp_edge_scroll_init(struct tp_dispatch *tp, struct evdev_device *device) tp_for_each_touch(tp, t) { t->scroll.direction = -1; libinput_timer_init(&t->scroll.timer, - device->base.seat->libinput, + tp_libinput_context(tp), tp_edge_scroll_handle_timeout, t); } @@ -361,6 +363,9 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time) if (!t->dirty) continue; + if (t->palm.state != PALM_NONE) + continue; + switch (t->scroll.edge) { case EDGE_NONE: if (t->scroll.direction != -1) { @@ -438,6 +443,10 @@ tp_edge_scroll_stop_events(struct tp_dispatch *tp, uint64_t time) &zero, &zero_discrete); t->scroll.direction = -1; + /* reset touch to area state, avoids loading the + * state machine with special case handling */ + t->scroll.edge = EDGE_NONE; + 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 fb8c9e43..b51f083a 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -1,5 +1,5 @@ /* - * Copyright © 2013 Red Hat, Inc. + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without @@ -37,7 +37,7 @@ #define CASE_RETURN_STRING(a) case a: return #a #define DEFAULT_TAP_TIMEOUT_PERIOD 180 -#define DEFAULT_DRAG_TIMEOUT_PERIOD 500 +#define DEFAULT_DRAG_TIMEOUT_PERIOD 300 #define DEFAULT_TAP_MOVE_THRESHOLD TP_MM_TO_DPI_NORMALIZED(3) enum tap_event { @@ -147,7 +147,7 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp, struct tp_touch *t, enum tap_event event, uint64_t time) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); switch (event) { case TAP_EVENT_TOUCH: @@ -223,7 +223,7 @@ tp_tap_tapped_handle_event(struct tp_dispatch *tp, struct tp_touch *t, enum tap_event event, uint64_t time) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); switch (event) { case TAP_EVENT_MOTION: @@ -483,7 +483,7 @@ tp_tap_multitap_handle_event(struct tp_dispatch *tp, struct tp_touch *t, enum tap_event event, uint64_t time) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); switch (event) { case TAP_EVENT_RELEASE: @@ -576,7 +576,7 @@ tp_tap_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) { - struct libinput *libinput = tp->device->base.seat->libinput; + struct libinput *libinput = tp_libinput_context(tp); enum tp_tap_state current; current = tp->tap.state; @@ -857,7 +857,7 @@ tp_init_tap(struct tp_dispatch *tp) tp->tap.enabled = tp_tap_default(tp->device); libinput_timer_init(&tp->tap.timer, - tp->device->base.seat->libinput, + tp_libinput_context(tp), tp_tap_handle_timeout, tp); return 0; diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 79cc0519..e7abe945 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1,5 +1,5 @@ /* - * Copyright © 2014 Red Hat, Inc. + * Copyright © 2014-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -237,6 +237,7 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) t->state = TOUCH_END; t->pinned.is_pinned = false; t->millis = time; + t->palm.time = 0; assert(tp->nfingers_down >= 1); tp->nfingers_down--; tp->queued |= TOUCHPAD_EVENT_MOTION; @@ -310,6 +311,7 @@ tp_process_absolute(struct tp_dispatch *tp, tp_new_touch(tp, t, time); else tp_end_sequence(tp, t, time); + break; } } @@ -475,12 +477,45 @@ tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t) /* We're inside the left/right palm edge and in the northern half of * the touchpad - this tap is a palm */ - if (t->point.y < tp->palm.vert_center) + if (t->point.y < tp->palm.vert_center) { + log_debug(tp_libinput_context(tp), + "palm: palm-tap detected\n"); return true; + } return false; } +static int +tp_palm_detect_dwt(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) +{ + if (tp->dwt.keyboard_active && + t->state == TOUCH_BEGIN) { + t->palm.state = PALM_TYPING; + t->palm.time = time; + t->palm.first = t->point; + return 1; + } else if (!tp->dwt.keyboard_active && + t->state == TOUCH_UPDATE && + t->palm.state == PALM_TYPING) + { + /* If a touch has started before the first or after the last + key press, release it on timeout. Benefit: a palm rested + while typing on the touchpad will be ignored, but a touch + started once we stop typing will be able to control the + pointer (alas not tap, etc.). + */ + if (t->palm.time == 0 || + t->palm.time > tp->dwt.keyboard_last_press_time) { + t->palm.state = PALM_NONE; + log_debug(tp_libinput_context(tp), + "palm: touch released, timeout after typing\n"); + } + } + + return 0; +} + static void tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) { @@ -489,13 +524,8 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) struct device_float_coords delta; int dirs; - if (tp->sendevents.keyboard_active && - t->state == TOUCH_BEGIN) { - t->palm.state = PALM_TYPING; - t->palm.time = time; - t->palm.first = t->point; - return; - } + if (tp_palm_detect_dwt(tp, t, time)) + goto out; /* If labelled a touch as palm, we unlabel as palm when we move out of the palm edge zone within the timeout, provided @@ -509,6 +539,8 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) tp_normalize_delta(tp, delta)); if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS)) { t->palm.state = PALM_NONE; + log_debug(tp_libinput_context(tp), + "palm: touch released, out of edge zone\n"); } } return; @@ -530,6 +562,11 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) t->palm.state = PALM_EDGE; t->palm.time = time; t->palm.first = t->point; + +out: + log_debug(tp_libinput_context(tp), + "palm: palm detected (%s)\n", + t->palm.state == PALM_EDGE ? "edge" : "typing"); } static void @@ -717,7 +754,7 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time) if (filter_motion || tp->sendevents.trackpoint_active || - tp->sendevents.keyboard_active) { + tp->dwt.keyboard_active) { tp_edge_scroll_stop_events(tp, time); tp_gesture_stop(tp, time); return; @@ -767,15 +804,15 @@ static void tp_remove_sendevents(struct tp_dispatch *tp) { libinput_timer_cancel(&tp->sendevents.trackpoint_timer); - libinput_timer_cancel(&tp->sendevents.keyboard_timer); + libinput_timer_cancel(&tp->dwt.keyboard_timer); if (tp->buttons.trackpoint) libinput_device_remove_event_listener( &tp->sendevents.trackpoint_listener); - if (tp->sendevents.keyboard) + if (tp->dwt.keyboard) libinput_device_remove_event_listener( - &tp->sendevents.keyboard_listener); + &tp->dwt.keyboard_listener); } static void @@ -810,7 +847,7 @@ tp_release_fake_touches(struct tp_dispatch *tp) static void tp_clear_state(struct tp_dispatch *tp) { - uint64_t now = libinput_now(tp->device->base.seat->libinput); + uint64_t now = libinput_now(tp_libinput_context(tp)); struct tp_touch *t; /* Unroll the touchpad state. @@ -912,7 +949,41 @@ tp_keyboard_timeout(uint64_t now, void *data) struct tp_dispatch *tp = data; tp_tap_resume(tp, now); - tp->sendevents.keyboard_active = false; + + tp->dwt.keyboard_active = false; + + log_debug(tp_libinput_context(tp), "palm: keyboard timeout\n"); +} + +static inline bool +tp_key_ignore_for_dwt(unsigned int keycode) +{ + switch (keycode) { + /* Ignore modifiers to be responsive to ctrl-click, alt-tab, etc. */ + case KEY_LEFTCTRL: + case KEY_RIGHTCTRL: + case KEY_LEFTALT: + case KEY_RIGHTALT: + case KEY_LEFTSHIFT: + case KEY_RIGHTSHIFT: + case KEY_FN: + case KEY_CAPSLOCK: + case KEY_TAB: + case KEY_COMPOSE: + case KEY_RIGHTMETA: + case KEY_LEFTMETA: + return true; + default: + break; + } + + /* Ignore keys not part of the "typewriter set", i.e. F-keys, + * multimedia keys, numpad, etc. + */ + if (keycode >= KEY_F1) + return true; + + return false; } static void @@ -934,42 +1005,61 @@ tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data) /* modifier keys don't trigger disable-while-typing so things like * ctrl+zoom or ctrl+click are possible */ - switch (libinput_event_keyboard_get_key(kbdev)) { - case KEY_LEFTCTRL: - case KEY_RIGHTCTRL: - case KEY_LEFTALT: - case KEY_RIGHTALT: - case KEY_LEFTSHIFT: - case KEY_RIGHTSHIFT: - case KEY_FN: - return; - default: - break; - } + if (tp_key_ignore_for_dwt(libinput_event_keyboard_get_key(kbdev))) + return; - if (!tp->sendevents.keyboard_active) { + if (!tp->dwt.keyboard_active) { tp_edge_scroll_stop_events(tp, time); tp_gesture_stop(tp, time); tp_tap_suspend(tp, time); - tp->sendevents.keyboard_active = true; + tp->dwt.keyboard_active = true; timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1; } else { timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2; } - libinput_timer_set(&tp->sendevents.keyboard_timer, + tp->dwt.keyboard_last_press_time = time; + libinput_timer_set(&tp->dwt.keyboard_timer, time + timeout); } +static bool +tp_want_dwt(struct evdev_device *touchpad, + struct evdev_device *keyboard) +{ + unsigned int bus_tp = libevdev_get_id_bustype(touchpad->evdev), + bus_kbd = libevdev_get_id_bustype(keyboard->evdev); + + if (bus_tp == BUS_BLUETOOTH || bus_kbd == BUS_BLUETOOTH) + return false; + + /* evemu will set the right bus type */ + if (bus_tp == BUS_VIRTUAL || bus_kbd == BUS_VIRTUAL) + return false; + + /* If the touchpad is on serio, the keyboard is too, so ignore any + other devices */ + if (bus_tp == BUS_I8042 && bus_kbd != bus_tp) + return false; + + /* Wacom makes touchpads, but not internal ones */ + if (libevdev_get_id_vendor(touchpad->evdev) == VENDOR_ID_WACOM) + return false; + + /* everything else we don't really know, so we have to assume + they go together */ + + return true; +} + static void tp_interface_device_added(struct evdev_device *device, struct evdev_device *added_device) { struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch; unsigned int bus_tp = libevdev_get_id_bustype(device->evdev), - bus_trp = libevdev_get_id_bustype(added_device->evdev), - bus_kbd = libevdev_get_id_bustype(added_device->evdev); - bool tp_is_internal, trp_is_internal, kbd_is_internal; + bus_trp = libevdev_get_id_bustype(added_device->evdev); + bool tp_is_internal, trp_is_internal; tp_is_internal = bus_tp != BUS_USB && bus_tp != BUS_BLUETOOTH; trp_is_internal = bus_trp != BUS_USB && bus_trp != BUS_BLUETOOTH; @@ -985,16 +1075,19 @@ tp_interface_device_added(struct evdev_device *device, tp_trackpoint_event, tp); } - /* FIXME: detect external keyboard better */ - kbd_is_internal = bus_tp != BUS_BLUETOOTH && - bus_kbd == bus_tp; - if (tp_is_internal && kbd_is_internal && - tp->sendevents.keyboard == NULL) { + if (added_device->tags & EVDEV_TAG_KEYBOARD && + tp->dwt.keyboard == NULL && + tp_want_dwt(device, added_device)) { + log_debug(tp_libinput_context(tp), + "palm: dwt activated with %s<->%s\n", + device->devname, + added_device->devname); + libinput_device_add_event_listener(&added_device->base, - &tp->sendevents.keyboard_listener, + &tp->dwt.keyboard_listener, tp_keyboard_event, tp); - tp->sendevents.keyboard = added_device; - tp->sendevents.keyboard_active = false; + tp->dwt.keyboard = added_device; + tp->dwt.keyboard_active = false; } if (tp->sendevents.current_mode != @@ -1023,10 +1116,10 @@ tp_interface_device_removed(struct evdev_device *device, tp->buttons.trackpoint = NULL; } - if (removed_device == tp->sendevents.keyboard) { + if (removed_device == tp->dwt.keyboard) { libinput_device_remove_event_listener( - &tp->sendevents.keyboard_listener); - tp->sendevents.keyboard = NULL; + &tp->dwt.keyboard_listener); + tp->dwt.keyboard = NULL; } if (tp->sendevents.current_mode != @@ -1044,9 +1137,9 @@ tp_interface_device_removed(struct evdev_device *device, tp_resume(tp, device); } -static void -tp_interface_tag_device(struct evdev_device *device, - struct udev_device *udev_device) +void +evdev_tag_touchpad(struct evdev_device *device, + struct udev_device *udev_device) { int bustype; @@ -1076,7 +1169,6 @@ static struct evdev_dispatch_interface tp_interface = { tp_interface_device_removed, tp_interface_device_removed, /* device_suspended, treat as remove */ tp_interface_device_added, /* device_resumed, treat as add */ - tp_interface_tag_device, NULL, /* post_added */ }; @@ -1182,7 +1274,7 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal) * and y resolution, so that a circle on the * touchpad does not turn into an elipse on the screen. */ - if (res_x > 1 && res_y > 1) { + if (!tp->device->abs.fake_resolution) { tp->accel.x_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_x; tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y; } else { @@ -1231,7 +1323,7 @@ tp_scroll_config_scroll_method_set_method(struct libinput_device *device, { struct evdev_device *evdev = (struct evdev_device*)device; struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; - uint64_t time = libinput_now(device->seat->libinput); + uint64_t time = libinput_now(tp_libinput_context(tp)); if (method == tp->scroll.method) return LIBINPUT_CONFIG_STATUS_SUCCESS; @@ -1297,6 +1389,7 @@ tp_init_palmdetect(struct tp_dispatch *tp, struct evdev_device *device) { int width, height; + unsigned int vendor_id; tp->palm.right_edge = INT_MAX; tp->palm.left_edge = INT_MIN; @@ -1307,8 +1400,13 @@ tp_init_palmdetect(struct tp_dispatch *tp, height = abs(device->abs.absinfo_y->maximum - device->abs.absinfo_y->minimum); - /* Apple touchpads are always big enough to warrant palm detection */ - if (evdev_device_get_id_vendor(device) != VENDOR_ID_APPLE) { + vendor_id = evdev_device_get_id_vendor(device); + + /* Wacom doesn't have internal touchpads, + * Apple touchpads are always big enough to warrant palm detection */ + if (vendor_id == VENDOR_ID_WACOM) { + return 0; + } else if (vendor_id != VENDOR_ID_APPLE) { /* We don't know how big the touchpad is */ if (device->abs.absinfo_x->resolution == 1) return 0; @@ -1332,15 +1430,39 @@ tp_init_sendevents(struct tp_dispatch *tp, struct evdev_device *device) { libinput_timer_init(&tp->sendevents.trackpoint_timer, - tp->device->base.seat->libinput, + tp_libinput_context(tp), tp_trackpoint_timeout, tp); - libinput_timer_init(&tp->sendevents.keyboard_timer, - tp->device->base.seat->libinput, + libinput_timer_init(&tp->dwt.keyboard_timer, + tp_libinput_context(tp), tp_keyboard_timeout, tp); return 0; } +static int +tp_sanity_check(struct tp_dispatch *tp, + struct evdev_device *device) +{ + struct libevdev *evdev = device->evdev; + struct libinput *libinput = tp_libinput_context(tp); + + if (!libevdev_has_event_code(evdev, EV_ABS, ABS_X)) + goto error; + + if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOUCH)) + goto error; + + if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_FINGER)) + goto error; + + return 0; + +error: + log_bug_kernel(libinput, + "device %s failed touchpad sanity checks\n"); + return -1; +} + static int tp_init(struct tp_dispatch *tp, struct evdev_device *device) @@ -1351,6 +1473,9 @@ tp_init(struct tp_dispatch *tp, tp->base.interface = &tp_interface; tp->device = device; + if (tp_sanity_check(tp, device) != 0) + return -1; + if (tp_init_slots(tp, device) != 0) return -1; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 3d51a398..fef5cb32 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -1,5 +1,5 @@ /* - * Copyright © 2014 Red Hat, Inc. + * Copyright © 2014-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -32,8 +32,6 @@ #define TOUCHPAD_HISTORY_LENGTH 4 #define TOUCHPAD_MIN_SAMPLES 4 -#define VENDOR_ID_APPLE 0x5ac - /* Convert mm to a distance normalized to DEFAULT_MOUSE_DPI */ #define TP_MM_TO_DPI_NORMALIZED(mm) (DEFAULT_MOUSE_DPI/25.4 * mm) @@ -282,17 +280,27 @@ struct tp_dispatch { bool trackpoint_active; struct libinput_event_listener trackpoint_listener; struct libinput_timer trackpoint_timer; + } sendevents; + struct { bool keyboard_active; struct libinput_event_listener keyboard_listener; struct libinput_timer keyboard_timer; struct evdev_device *keyboard; - } sendevents; + + uint64_t keyboard_last_press_time; + } dwt; }; #define tp_for_each_touch(_tp, _t) \ for (unsigned int _i = 0; _i < (_tp)->ntouches && (_t = &(_tp)->touches[_i]); _i++) +static inline struct libinput* +tp_libinput_context(struct tp_dispatch *tp) +{ + return tp->device->base.seat->libinput; +} + static inline struct normalized_coords tp_normalize_delta(struct tp_dispatch *tp, struct device_float_coords delta) { diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index 301fe246..a0ce5091 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -1029,7 +1029,6 @@ static struct evdev_dispatch_interface tablet_interface = { NULL, /* device_removed */ NULL, /* device_suspended */ NULL, /* device_resumed */ - NULL, /* tag_device */ tablet_check_initial_proximity, }; diff --git a/src/evdev.c b/src/evdev.c index 3d8cace9..3e66df36 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1,6 +1,7 @@ /* * Copyright © 2010 Intel Corporation * Copyright © 2013 Jonas Ådahl + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -59,6 +60,7 @@ enum evdev_device_udev_tags { EVDEV_UDEV_TAG_JOYSTICK = (1 << 6), EVDEV_UDEV_TAG_ACCELEROMETER = (1 << 7), EVDEV_UDEV_TAG_BUTTONSET = (1 << 8), + EVDEV_UDEV_TAG_POINTINGSTICK = (1 << 9), }; struct evdev_udev_tag_match { @@ -77,6 +79,7 @@ static const struct evdev_udev_tag_match evdev_udev_tag_matches[] = { {"ID_INPUT_TABLET_PAD", EVDEV_UDEV_TAG_BUTTONSET}, {"ID_INPUT_JOYSTICK", EVDEV_UDEV_TAG_JOYSTICK}, {"ID_INPUT_ACCELEROMETER", EVDEV_UDEV_TAG_ACCELEROMETER}, + {"ID_INPUT_POINTINGSTICK", EVDEV_UDEV_TAG_POINTINGSTICK}, /* sentinel value */ { 0 }, @@ -439,6 +442,7 @@ evdev_button_scroll_button(struct evdev_device *device, if (is_press) { libinput_timer_set(&device->scroll.timer, time + DEFAULT_MIDDLE_BUTTON_SCROLL_TIMEOUT); + device->scroll.button_down_time = time; } else { libinput_timer_cancel(&device->scroll.timer); if (device->scroll.button_scroll_active) { @@ -448,7 +452,8 @@ evdev_button_scroll_button(struct evdev_device *device, } else { /* If the button is released quickly enough emit the * button press/release events. */ - evdev_pointer_notify_physical_button(device, time, + evdev_pointer_notify_physical_button(device, + device->scroll.button_down_time, device->scroll.button, LIBINPUT_BUTTON_STATE_PRESSED); evdev_pointer_notify_physical_button(device, time, @@ -719,20 +724,40 @@ evdev_tag_external_mouse(struct evdev_device *device, int bustype; bustype = libevdev_get_id_bustype(device->evdev); - if (bustype == BUS_USB || bustype == BUS_BLUETOOTH) { - if (device->seat_caps & EVDEV_DEVICE_POINTER) - device->tags |= EVDEV_TAG_EXTERNAL_MOUSE; - } + if (bustype == BUS_USB || bustype == BUS_BLUETOOTH) + device->tags |= EVDEV_TAG_EXTERNAL_MOUSE; } static void evdev_tag_trackpoint(struct evdev_device *device, struct udev_device *udev_device) { - if (libevdev_has_property(device->evdev, INPUT_PROP_POINTING_STICK)) + if (libevdev_has_property(device->evdev, + INPUT_PROP_POINTING_STICK) || + udev_device_get_property_value(udev_device, + "ID_INPUT_POINTINGSTICK")) device->tags |= EVDEV_TAG_TRACKPOINT; } +static void +evdev_tag_keyboard(struct evdev_device *device, + struct udev_device *udev_device) +{ + int code; + + if (!libevdev_has_event_type(device->evdev, EV_KEY)) + return; + + for (code = KEY_Q; code <= KEY_P; code++) { + if (!libevdev_has_event_code(device->evdev, + EV_KEY, + code)) + return; + } + + device->tags |= EVDEV_TAG_KEYBOARD; +} + static void fallback_process(struct evdev_dispatch *dispatch, struct evdev_device *device, @@ -825,14 +850,6 @@ fallback_destroy(struct evdev_dispatch *dispatch) free(dispatch); } -static void -fallback_tag_device(struct evdev_device *device, - struct udev_device *udev_device) -{ - evdev_tag_external_mouse(device, udev_device); - evdev_tag_trackpoint(device, udev_device); -} - static int evdev_calibration_has_matrix(struct libinput_device *libinput_device) { @@ -883,7 +900,6 @@ struct evdev_dispatch_interface fallback_interface = { NULL, /* device_removed */ NULL, /* device_suspended */ NULL, /* device_resumed */ - fallback_tag_device, NULL, /* post_added */ }; @@ -1045,7 +1061,7 @@ evdev_scroll_get_default_method(struct libinput_device *device) { struct evdev_device *evdev = (struct evdev_device *)device; - if (libevdev_has_property(evdev->evdev, INPUT_PROP_POINTING_STICK)) + if (evdev->tags & EVDEV_TAG_TRACKPOINT) return LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; /* Mice without a scroll wheel but with middle button have on-button @@ -1085,14 +1101,7 @@ evdev_scroll_get_default_button(struct libinput_device *device) { struct evdev_device *evdev = (struct evdev_device *)device; - if (libevdev_has_property(evdev->evdev, INPUT_PROP_POINTING_STICK)) - return BTN_MIDDLE; - - /* A device that defaults to button scrolling defaults - to BTN_MIDDLE */ - if (evdev_scroll_get_default_method(device) == - LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN && - libevdev_has_event_code(evdev->evdev, EV_KEY, BTN_MIDDLE)) + if( libevdev_has_event_code(evdev->evdev, EV_KEY, BTN_MIDDLE)) return BTN_MIDDLE; return 0; @@ -1412,14 +1421,6 @@ evdev_need_mtdev(struct evdev_device *device) !libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT)); } -static void -evdev_tag_device(struct evdev_device *device) -{ - if (device->dispatch->interface->tag_device) - device->dispatch->interface->tag_device(device, - device->udev_device); -} - static inline int evdev_read_wheel_click_prop(struct evdev_device *device) { @@ -1480,7 +1481,7 @@ evdev_read_dpi_prop(struct evdev_device *device) * POINTINGSTICK_CONST_ACCEL value to compensate for sensitivity * differences between models, we translate this to a fake dpi. */ - if (libevdev_has_property(device->evdev, INPUT_PROP_POINTING_STICK)) + if (device->tags & EVDEV_TAG_TRACKPOINT) return evdev_get_trackpoint_dpi(device); mouse_dpi = udev_device_get_property_value(device->udev_device, @@ -1820,13 +1821,14 @@ evdev_configure_device(struct evdev_device *device) } log_info(libinput, - "input device '%s', %s is tagged by udev as:%s%s%s%s%s%s%s%s\n", + "input device '%s', %s is tagged by udev as:%s%s%s%s%s%s%s%s%s\n", device->devname, devnode, udev_tags & EVDEV_UDEV_TAG_KEYBOARD ? " Keyboard" : "", udev_tags & EVDEV_UDEV_TAG_MOUSE ? " Mouse" : "", udev_tags & EVDEV_UDEV_TAG_TOUCHPAD ? " Touchpad" : "", udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN ? " Touchscreen" : "", udev_tags & EVDEV_UDEV_TAG_TABLET ? " Tablet" : "", + udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK ? " Pointingstick" : "", udev_tags & EVDEV_UDEV_TAG_JOYSTICK ? " Joystick" : "", udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER ? " Accelerometer" : "", udev_tags & EVDEV_UDEV_TAG_BUTTONSET ? " Buttonset" : ""); @@ -1897,10 +1899,13 @@ evdev_configure_device(struct evdev_device *device) log_info(libinput, "input device '%s', %s is a touchpad\n", device->devname, devnode); + + evdev_tag_touchpad(device, device->udev_device); return device->dispatch == NULL ? -1 : 0; } - if (udev_tags & EVDEV_UDEV_TAG_MOUSE) { + if (udev_tags & EVDEV_UDEV_TAG_MOUSE || + udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK) { if (libevdev_has_event_code(evdev, EV_REL, REL_X) && libevdev_has_event_code(evdev, EV_REL, REL_Y) && evdev_device_init_pointer_acceleration( @@ -1920,6 +1925,9 @@ evdev_configure_device(struct evdev_device *device) device->scroll.natural_scrolling_enabled = true; /* want button scrolling config option */ device->scroll.want_button = 1; + + evdev_tag_external_mouse(device, device->udev_device); + evdev_tag_trackpoint(device, device->udev_device); } if (udev_tags & EVDEV_UDEV_TAG_KEYBOARD) { @@ -1934,6 +1942,8 @@ evdev_configure_device(struct evdev_device *device) device->scroll.natural_scrolling_enabled = true; device->seat_caps |= EVDEV_DEVICE_POINTER; } + + evdev_tag_keyboard(device, device->udev_device); } if (udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN) { @@ -2089,7 +2099,6 @@ evdev_device_create(struct libinput_seat *seat, device->scroll.direction = 0; device->scroll.wheel_click_angle = evdev_read_wheel_click_prop(device); - device->dpi = evdev_read_dpi_prop(device); device->model = evdev_read_model(device); /* at most 5 SYN_DROPPED log-messages per 30s */ ratelimit_init(&device->syn_drop_limit, 30ULL * 1000, 5); @@ -2101,6 +2110,8 @@ evdev_device_create(struct libinput_seat *seat, if (evdev_configure_device(device) == -1) goto err; + device->dpi = evdev_read_dpi_prop(device); + if (device->seat_caps == 0) { unhandled_device = 1; goto err; @@ -2122,7 +2133,6 @@ evdev_device_create(struct libinput_seat *seat, list_insert(seat->devices_list.prev, &device->base.link); - evdev_tag_device(device); evdev_notify_added_device(device); return device; diff --git a/src/evdev.h b/src/evdev.h index 19454ec8..17ae0d95 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -1,6 +1,7 @@ /* * Copyright © 2011, 2012 Intel Corporation * Copyright © 2013 Jonas Ådahl + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -69,6 +70,7 @@ enum evdev_device_tags { EVDEV_TAG_INTERNAL_TOUCHPAD = (1 << 1), EVDEV_TAG_TRACKPOINT = (1 << 2), EVDEV_TAG_TOUCHPAD_TRACKPOINT = (1 << 3), + EVDEV_TAG_KEYBOARD = (1 << 4), }; enum evdev_middlebutton_state { @@ -149,6 +151,8 @@ struct evdev_device { /* Currently enabled method, button */ enum libinput_config_scroll_method method; uint32_t button; + uint64_t button_down_time; + /* set during device init, used at runtime to delay changes * until all buttons are up */ enum libinput_config_scroll_method want_method; @@ -254,10 +258,6 @@ struct evdev_dispatch_interface { void (*device_resumed)(struct evdev_device *device, struct evdev_device *resumed_device); - /* Tag device with one of EVDEV_TAG */ - void (*tag_device)(struct evdev_device *device, - struct udev_device *udev_device); - /* Called immediately after the LIBINPUT_EVENT_DEVICE_ADDED event * was sent */ void (*post_added)(struct evdev_device *device, @@ -298,6 +298,10 @@ evdev_mt_touchpad_create(struct evdev_device *device); struct evdev_dispatch * evdev_tablet_create(struct evdev_device *device); +void +evdev_tag_touchpad(struct evdev_device *device, + struct udev_device *udev_device); + void evdev_device_led_update(struct evdev_device *device, enum libinput_led leds); diff --git a/src/filter.c b/src/filter.c index 626cb0aa..c54d866c 100644 --- a/src/filter.c +++ b/src/filter.c @@ -1,6 +1,7 @@ /* * Copyright © 2006-2009 Simon Thum * Copyright © 2012 Jonas Ådahl + * Copyright © 2014-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -77,7 +78,7 @@ filter_get_speed(struct motion_filter *filter) */ #define MAX_VELOCITY_DIFF 1.0 /* units/ms */ -#define MOTION_TIMEOUT 300 /* (ms) */ +#define MOTION_TIMEOUT 1000 /* (ms) */ #define NUM_POINTER_TRACKERS 16 struct pointer_tracker { @@ -143,6 +144,24 @@ calculate_tracker_velocity(struct pointer_tracker *tracker, uint64_t time) return normalized_length(tracker->delta) / tdelta; /* units/ms */ } +static inline double +calculate_velocity_after_timeout(struct pointer_tracker *tracker) +{ + /* First movement after timeout needs special handling. + * + * When we trigger the timeout, the last event is too far in the + * past to use it for velocity calculation across multiple tracker + * values. + * + * Use the motion timeout itself to calculate the speed rather than + * the last tracker time. This errs on the side of being too fast + * for really slow movements but provides much more useful initial + * movement in normal use-cases (pause, move, pause, move) + */ + return calculate_tracker_velocity(tracker, + tracker->time + MOTION_TIMEOUT); +} + static double calculate_velocity(struct pointer_accelerator *accel, uint64_t time) { @@ -162,15 +181,23 @@ calculate_velocity(struct pointer_accelerator *accel, uint64_t time) /* Stop if too far away in time */ if (time - tracker->time > MOTION_TIMEOUT || - tracker->time > time) + tracker->time > time) { + if (offset == 1) + result = calculate_velocity_after_timeout(tracker); break; + } + + velocity = calculate_tracker_velocity(tracker, time); /* Stop if direction changed */ dir &= tracker->dir; - if (dir == 0) + if (dir == 0) { + /* First movement after dirchange - velocity is that + * of the last movement */ + if (offset == 1) + result = velocity; break; - - velocity = calculate_tracker_velocity(tracker, time); + } if (initial_velocity == 0.0) { result = initial_velocity = velocity; @@ -196,17 +223,20 @@ acceleration_profile(struct pointer_accelerator *accel, static double calculate_acceleration(struct pointer_accelerator *accel, - void *data, double velocity, uint64_t time) + void *data, + double velocity, + double last_velocity, + uint64_t time) { double factor; /* Use Simpson's rule to calculate the avarage acceleration between * the previous motion and the most recent. */ factor = acceleration_profile(accel, data, velocity, time); - factor += acceleration_profile(accel, data, accel->last_velocity, time); + factor += acceleration_profile(accel, data, last_velocity, time); factor += 4.0 * acceleration_profile(accel, data, - (accel->last_velocity + velocity) / 2, + (last_velocity + velocity) / 2, time); factor = factor / 6.0; @@ -227,7 +257,11 @@ accelerator_filter(struct motion_filter *filter, feed_trackers(accel, unaccelerated, time); velocity = calculate_velocity(accel, time); - accel_value = calculate_acceleration(accel, data, velocity, time); + accel_value = calculate_acceleration(accel, + data, + velocity, + accel->last_velocity, + time); accelerated.x = accel_value * unaccelerated->x; accelerated.y = accel_value * unaccelerated->y; diff --git a/src/filter.h b/src/filter.h index a0538601..16896a4c 100644 --- a/src/filter.h +++ b/src/filter.h @@ -1,5 +1,6 @@ /* * Copyright © 2012 Jonas Ådahl + * Copyright © 2014-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/libinput-private.h b/src/libinput-private.h index ae20f807..d825eb66 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -1,5 +1,6 @@ /* * Copyright © 2013 Jonas Ådahl + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/libinput-util.c b/src/libinput-util.c index 4857435b..ceea054b 100644 --- a/src/libinput-util.c +++ b/src/libinput-util.c @@ -1,6 +1,7 @@ /* * Copyright © 2008-2011 Kristian Høgsberg * Copyright © 2011 Intel Corporation + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that diff --git a/src/libinput-util.h b/src/libinput-util.h index 04623cca..5db3e3fa 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -1,5 +1,6 @@ /* * Copyright © 2008 Kristian Høgsberg + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -25,11 +26,16 @@ #include #include +#include +#include #include #include #include "libinput.h" +#define VENDOR_ID_APPLE 0x5ac +#define VENDOR_ID_WACOM 0x56a + void set_logging_enabled(int enabled); @@ -250,6 +256,38 @@ matrix_to_farray6(const struct matrix *m, float out[6]) out[5] = m->val[1][2]; } +/** + * Simple wrapper for asprintf that ensures the passed in-pointer is set + * to NULL upon error. + * The standard asprintf() call does not guarantee the passed in pointer + * will be NULL'ed upon failure, whereas this wrapper does. + * + * @param strp pointer to set to newly allocated string. + * This pointer should be passed to free() to release when done. + * @param fmt the format string to use for printing. + * @return The number of bytes printed (excluding the null byte terminator) + * upon success or -1 upon failure. In the case of failure the pointer is set + * to NULL. + */ +static inline int +xasprintf(char **strp, const char *fmt, ...) + LIBINPUT_ATTRIBUTE_PRINTF(2, 3); + +static inline int +xasprintf(char **strp, const char *fmt, ...) +{ + int rc = 0; + va_list args; + + va_start(args, fmt); + rc = vasprintf(strp, fmt, args); + va_end(args); + if ((rc == -1) && strp) + *strp = NULL; + + return rc; +} + enum ratelimit_state { RATELIMIT_EXCEEDED, RATELIMIT_THRESHOLD, diff --git a/src/libinput.c b/src/libinput.c index fd607d1f..7f430861 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1,5 +1,6 @@ /* * Copyright © 2013 Jonas Ådahl + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/libinput.h b/src/libinput.h index b08e9d62..60431668 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -1,5 +1,6 @@ /* * Copyright © 2013 Jonas Ådahl + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/path.c b/src/path.c index ab5587bf..5ec8bf51 100644 --- a/src/path.c +++ b/src/path.c @@ -1,5 +1,5 @@ /* - * Copyright © 2013 Red Hat, Inc. + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/timer.c b/src/timer.c index d1d3c108..1e507df1 100644 --- a/src/timer.c +++ b/src/timer.c @@ -1,5 +1,5 @@ /* - * Copyright © 2014 Red Hat, Inc. + * Copyright © 2014-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -20,6 +20,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include #include #include diff --git a/src/udev-seat.c b/src/udev-seat.c index 8dc0c236..588e1b40 100644 --- a/src/udev-seat.c +++ b/src/udev-seat.c @@ -1,5 +1,6 @@ /* * Copyright © 2013 Intel Corporation + * Copyright © 2013-2015 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/test/device.c b/test/device.c index 193c3ffb..d1f4f215 100644 --- a/test/device.c +++ b/test/device.c @@ -69,7 +69,7 @@ START_TEST(device_sendevents_config_touchpad) expected = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; /* The wacom devices in the test suite are external */ - if (libevdev_get_id_vendor(dev->evdev) != 0x56a) /* wacom */ + if (libevdev_get_id_vendor(dev->evdev) != VENDOR_ID_WACOM) expected |= LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; diff --git a/test/litest.c b/test/litest.c index fbbdaeb2..8c82cc76 100644 --- a/test/litest.c +++ b/test/litest.c @@ -84,8 +84,10 @@ litest_backtrace_get_lineno(const char *executable, char *s; unsigned int i; - if (!cwd[0]) - getcwd(cwd, sizeof(cwd)); + if (!cwd[0]) { + if (getcwd(cwd, sizeof(cwd)) == NULL) + cwd[0] = 0; /* contents otherwise undefined. */ + } sprintf (buffer, ADDR2LINE " -C -e %s -i %lx", @@ -383,7 +385,17 @@ static struct list all_tests; static void litest_reload_udev_rules(void) { - system("udevadm control --reload-rules"); + int ret = system("udevadm control --reload-rules"); + if (ret == -1) { + litest_abort_msg("Failed to execute: udevadm"); + } else if (WIFEXITED(ret)) { + if (WEXITSTATUS(ret)) + litest_abort_msg("udevadm failed with %d", + WEXITSTATUS(ret)); + } else if (WIFSIGNALED(ret)) { + litest_abort_msg("udevadm terminated with signal %d", + WTERMSIG(ret)); + } } static int @@ -648,6 +660,7 @@ _litest_add_ranged_for_device(const char *name, { struct suite *s; struct litest_test_device **dev = devices; + bool device_filtered = false; assert(type < LITEST_NO_DEVICE); @@ -662,8 +675,10 @@ _litest_add_ranged_for_device(const char *name, s = get_suite(name); for (; *dev; dev++) { if (filter_device && - fnmatch(filter_device, (*dev)->shortname, 0) != 0) + fnmatch(filter_device, (*dev)->shortname, 0) != 0) { + device_filtered = true; continue; + } if ((*dev)->type == type) { litest_add_tcase_for_device(s, @@ -675,7 +690,9 @@ _litest_add_ranged_for_device(const char *name, } } - litest_abort_msg("Invalid test device type"); + /* only abort if no filter was set, that's a bug */ + if (!device_filtered) + litest_abort_msg("Invalid test device type"); } static int @@ -879,7 +896,7 @@ litest_init_udev_rules(struct litest_test_device *dev) ck_abort_msg("Failed to create udev rules directory (%s)\n", strerror(errno)); - rc = asprintf(&path, + rc = xasprintf(&path, "%s/%s%s.rules", UDEV_RULES_D, UDEV_RULE_PREFIX, @@ -985,6 +1002,32 @@ litest_restore_log_handler(struct libinput *libinput) libinput_log_set_handler(libinput, litest_log_handler); } +static inline void +litest_wait_for_udev(int fd) +{ + struct udev *udev; + struct udev_device *device; + struct stat st; + int loop_count = 0; + + litest_assert_int_ge(fstat(fd, &st), 0); + + udev = udev_new(); + device = udev_device_new_from_devnum(udev, 'c', st.st_rdev); + litest_assert_ptr_notnull(device); + while (device && !udev_device_get_property_value(device, "ID_INPUT")) { + loop_count++; + litest_assert_int_lt(loop_count, 300); + + udev_device_unref(device); + msleep(2); + device = udev_device_new_from_devnum(udev, 'c', st.st_rdev); + } + + udev_device_unref(device); + udev_unref(udev); +} + struct litest_device * litest_add_device_with_overrides(struct libinput *libinput, enum litest_device_type which, @@ -1012,6 +1055,8 @@ litest_add_device_with_overrides(struct libinput *libinput, rc = libevdev_new_from_fd(fd, &d->evdev); litest_assert_int_eq(rc, 0); + litest_wait_for_udev(fd); + d->libinput = libinput; d->libinput_device = libinput_path_add_device(d->libinput, path); litest_assert(d->libinput_device != NULL); @@ -1097,6 +1142,11 @@ litest_delete_device(struct litest_device *d) free(d->private); memset(d,0, sizeof(*d)); free(d); + + /* zzz so udev can catch up with things, so we don't accidentally open + * an old device in the next test and then get all upset when things blow + * up */ + msleep(10); } void @@ -2115,6 +2165,18 @@ litest_timeout_middlebutton(void) msleep(70); } +void +litest_timeout_dwt_short(void) +{ + msleep(220); +} + +void +litest_timeout_dwt_long(void) +{ + msleep(520); +} + void litest_push_event_frame(struct litest_device *dev) { diff --git a/test/litest.h b/test/litest.h index 9efacde8..e23388c2 100644 --- a/test/litest.h +++ b/test/litest.h @@ -416,6 +416,8 @@ void litest_timeout_buttonscroll(void); void litest_timeout_edgescroll(void); void litest_timeout_finger_switch(void); void litest_timeout_middlebutton(void); +void litest_timeout_dwt_short(void); +void litest_timeout_dwt_long(void); void litest_push_event_frame(struct litest_device *dev); void litest_pop_event_frame(struct litest_device *dev); diff --git a/test/touch.c b/test/touch.c index 2c07e090..be2bea92 100644 --- a/test/touch.c +++ b/test/touch.c @@ -586,7 +586,8 @@ START_TEST(touch_initial_state) { struct litest_device *dev; struct libinput *libinput1, *libinput2; - struct libinput_event *ev1, *ev2; + struct libinput_event *ev1 = NULL; + struct libinput_event *ev2 = NULL; struct libinput_event_touch *t1, *t2; struct libinput_device *device1, *device2; int axis = _i; /* looped test */ diff --git a/test/touchpad.c b/test/touchpad.c index c8ecb322..692698ce 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -1808,6 +1808,51 @@ START_TEST(touchpad_2fg_clickfinger) } END_TEST +START_TEST(touchpad_2fg_clickfinger_distance) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + libinput_device_config_click_set_method(dev->libinput_device, + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + litest_drain_events(li); + + litest_touch_down(dev, 0, 90, 50); + litest_touch_down(dev, 1, 10, 50); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + litest_touch_up(dev, 1); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); + + litest_touch_down(dev, 0, 50, 5); + litest_touch_down(dev, 1, 50, 95); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_touch_up(dev, 0); + litest_touch_up(dev, 1); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); +} +END_TEST + START_TEST(touchpad_clickfinger_to_area_method) { struct litest_device *dev = litest_current_device(); @@ -2636,7 +2681,7 @@ START_TEST(clickpad_topsoftbuttons_clickfinger) litest_assert_empty_queue(li); litest_touch_down(dev, 0, 90, 5); - litest_touch_down(dev, 1, 10, 5); + litest_touch_down(dev, 1, 80, 5); litest_event(dev, EV_KEY, BTN_LEFT, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); litest_event(dev, EV_KEY, BTN_LEFT, 0); @@ -2959,7 +3004,7 @@ START_TEST(touchpad_edge_scroll) litest_touch_up(dev, 0); libinput_dispatch(li); - litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 10); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 4); litest_assert_empty_queue(li); litest_touch_down(dev, 0, 99, 80); @@ -2967,7 +3012,7 @@ START_TEST(touchpad_edge_scroll) litest_touch_up(dev, 0); libinput_dispatch(li); - litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, -10); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, -4); litest_assert_empty_queue(li); litest_touch_down(dev, 0, 20, 99); @@ -2975,7 +3020,7 @@ START_TEST(touchpad_edge_scroll) litest_touch_up(dev, 0); libinput_dispatch(li); - litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, 10); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, 4); litest_assert_empty_queue(li); litest_touch_down(dev, 0, 70, 99); @@ -2983,7 +3028,7 @@ START_TEST(touchpad_edge_scroll) litest_touch_up(dev, 0); libinput_dispatch(li); - litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, -10); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, -4); litest_assert_empty_queue(li); } END_TEST @@ -3065,7 +3110,7 @@ START_TEST(touchpad_edge_scroll_no_motion) litest_touch_up(dev, 0); libinput_dispatch(li); - litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 5); + litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 4); litest_assert_empty_queue(li); } END_TEST @@ -3198,9 +3243,13 @@ static int touchpad_has_palm_detect_size(struct litest_device *dev) { double width, height; + unsigned int vendor; int rc; - if (libinput_device_get_id_vendor(dev->libinput_device) == 0x5ac) /* Apple */ + vendor = libinput_device_get_id_vendor(dev->libinput_device); + if (vendor == VENDOR_ID_WACOM) + return 0; + if (vendor == VENDOR_ID_APPLE) return 1; rc = libinput_device_get_size(dev->libinput_device, &width, &height); @@ -4590,6 +4639,434 @@ START_TEST(touchpad_initial_state) } END_TEST +static inline bool +has_disable_while_typing(struct litest_device *device) +{ + if (libevdev_get_id_vendor(device->evdev) == VENDOR_ID_WACOM) + return false; + + return true; +} + +START_TEST(touchpad_dwt) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_DISABLED); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + libinput_dispatch(li); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1); + litest_touch_up(touchpad, 0); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + /* within timeout - no events */ + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1); + litest_touch_up(touchpad, 0); + litest_assert_empty_queue(li); + + litest_timeout_dwt_short(); + libinput_dispatch(li); + + /* after timeout - motion events*/ + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1); + litest_touch_up(touchpad, 0); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_enable_touch) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_DISABLED); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + libinput_dispatch(li); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + /* finger down after last key event, but + we're still within timeout - no events */ + msleep(10); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1); + litest_assert_empty_queue(li); + + litest_timeout_dwt_short(); + libinput_dispatch(li); + + /* same touch after timeout - motion events*/ + litest_touch_move_to(touchpad, 0, 70, 50, 50, 50, 10, 1); + litest_touch_up(touchpad, 0); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_touch_hold) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_DISABLED); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + msleep(1); /* make sure touch starts after key press */ + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + /* touch still down - no events */ + litest_keyboard_key(keyboard, KEY_A, false); + libinput_dispatch(li); + litest_touch_move_to(touchpad, 0, 70, 50, 30, 50, 5, 1); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + /* touch still down - no events */ + litest_timeout_dwt_short(); + libinput_dispatch(li); + litest_touch_move_to(touchpad, 0, 30, 50, 50, 50, 5, 1); + litest_touch_up(touchpad, 0); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_key_hold) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_DISABLED); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + libinput_dispatch(li); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1); + litest_touch_up(touchpad, 0); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + litest_keyboard_key(keyboard, KEY_A, false); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_type) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + int i; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_DISABLED); + litest_drain_events(li); + + for (i = 0; i < 5; i++) { + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + libinput_dispatch(li); + } + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1); + litest_touch_up(touchpad, 0); + litest_assert_empty_queue(li); + + litest_timeout_dwt_long(); + libinput_dispatch(li); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1); + litest_touch_up(touchpad, 0); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_type_short_timeout) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + int i; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_DISABLED); + litest_drain_events(li); + + for (i = 0; i < 5; i++) { + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + libinput_dispatch(li); + } + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1); + litest_touch_up(touchpad, 0); + litest_assert_empty_queue(li); + + litest_timeout_dwt_short(); + libinput_dispatch(li); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1); + litest_touch_up(touchpad, 0); + litest_assert_empty_queue(li); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_tap) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + libinput_dispatch(li); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_up(touchpad, 0); + + litest_keyboard_key(keyboard, KEY_A, false); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_timeout_dwt_short(); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_up(touchpad, 0); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_tap_drag) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + libinput_dispatch(li); + msleep(1); /* make sure touch starts after key press */ + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_up(touchpad, 0); + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1); + + litest_keyboard_key(keyboard, KEY_A, false); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_timeout_dwt_short(); + libinput_dispatch(li); + litest_touch_move_to(touchpad, 0, 70, 50, 50, 50, 5, 1); + litest_touch_up(touchpad, 0); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_click) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + + if (!has_disable_while_typing(touchpad)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + libinput_device_config_tap_set_enabled(touchpad->libinput_device, + LIBINPUT_CONFIG_TAP_DISABLED); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_touch_down(touchpad, 0, 50, 50); + litest_button_click(touchpad, BTN_LEFT, true); + litest_button_click(touchpad, BTN_LEFT, false); + libinput_dispatch(li); + litest_touch_up(touchpad, 0); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); + + litest_keyboard_key(keyboard, KEY_A, false); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_edge_scroll) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + + if (!has_disable_while_typing(touchpad)) + return; + + enable_edge_scroll(touchpad); + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_touch_down(touchpad, 0, 99, 20); + libinput_dispatch(li); + litest_timeout_edgescroll(); + libinput_dispatch(li); + litest_assert_empty_queue(li); + + /* edge scroll timeout is 300ms atm, make sure we don't accidentally + exit the DWT timeout */ + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + libinput_dispatch(li); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_touch_move_to(touchpad, 0, 99, 20, 99, 80, 60, 10); + libinput_dispatch(li); + litest_assert_empty_queue(li); + + litest_touch_move_to(touchpad, 0, 99, 80, 99, 20, 60, 10); + litest_touch_up(touchpad, 0); + libinput_dispatch(li); + litest_assert_empty_queue(li); + + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(touchpad_dwt_edge_scroll_interrupt) +{ + struct litest_device *touchpad = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = touchpad->libinput; + struct libinput_event_pointer *stop_event; + + if (!has_disable_while_typing(touchpad)) + return; + + enable_edge_scroll(touchpad); + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + litest_drain_events(li); + + litest_touch_down(touchpad, 0, 99, 20); + libinput_dispatch(li); + litest_timeout_edgescroll(); + litest_touch_move_to(touchpad, 0, 99, 20, 99, 30, 10, 10); + libinput_dispatch(li); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS); + + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_A, false); + + /* scroll stop event */ + litest_wait_for_event(li); + stop_event = litest_is_axis_event(libinput_get_event(li), + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, + LIBINPUT_POINTER_AXIS_SOURCE_FINGER); + libinput_event_destroy(libinput_event_pointer_get_base_event(stop_event)); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_timeout_dwt_long(); + + /* Known bad behavior: a touch starting to edge-scroll before dwt + * kicks in will stop to scroll but be recognized as normal + * pointer-moving touch once the timeout expires. We'll fix that + * when we need to. + */ + litest_touch_move_to(touchpad, 0, 99, 30, 99, 80, 10, 5); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_delete_device(keyboard); +} +END_TEST + void litest_setup_tests(void) { @@ -4652,6 +5129,7 @@ litest_setup_tests(void) litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger_no_touch, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger_distance, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", touchpad_clickfinger_to_area_method, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", touchpad_clickfinger_to_area_method_while_down, LITEST_CLICKPAD, LITEST_ANY); @@ -4738,4 +5216,16 @@ litest_setup_tests(void) litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_no_trackpoint, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS); litest_add_ranged("touchpad:state", touchpad_initial_state, LITEST_TOUCHPAD, LITEST_ANY, &axis_range); + + litest_add("touchpad:dwt", touchpad_dwt, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_enable_touch, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_touch_hold, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_key_hold, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_type, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_type_short_timeout, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_tap, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_tap_drag, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_click, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:dwt", touchpad_dwt_edge_scroll, LITEST_TOUCHPAD, LITEST_CLICKPAD); + litest_add("touchpad:dwt", touchpad_dwt_edge_scroll_interrupt, LITEST_TOUCHPAD, LITEST_CLICKPAD); } diff --git a/test/trackpoint.c b/test/trackpoint.c index 9fcce6f7..0a6f6b0f 100644 --- a/test/trackpoint.c +++ b/test/trackpoint.c @@ -35,15 +35,34 @@ START_TEST(trackpoint_middlebutton) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + uint64_t ptime, rtime; litest_drain_events(li); /* A quick middle button click should get reported normally */ litest_button_click(dev, BTN_MIDDLE, 1); + msleep(2); litest_button_click(dev, BTN_MIDDLE, 0); - litest_assert_button_event(li, BTN_MIDDLE, 1); - litest_assert_button_event(li, BTN_MIDDLE, 0); + litest_wait_for_event(li); + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + ptime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + rtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + + ck_assert_int_lt(ptime, rtime); litest_assert_empty_queue(li); } diff --git a/tools/Makefile.am b/tools/Makefile.am index b24c560d..68e60cbb 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -13,9 +13,9 @@ libshared_la_CFLAGS = $(LIBEVDEV_CFLAGS) libshared_la_LIBADD = $(LIBEVDEV_LIBS) event_debug_SOURCES = event-debug.c -event_debug_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS) +event_debug_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS) $(LIBEVDEV_LIBS) event_debug_LDFLAGS = -no-install -event_debug_CFLAGS = $(LIBUDEV_CFLAGS) +event_debug_CFLAGS = $(LIBUDEV_CFLAGS) $(LIBEVDEV_CFLAGS) ptraccel_debug_SOURCES = ptraccel-debug.c ptraccel_debug_LDADD = ../src/libfilter.la diff --git a/tools/event-debug.c b/tools/event-debug.c index e8e49cd7..36c7cd32 100644 --- a/tools/event-debug.c +++ b/tools/event-debug.c @@ -34,6 +34,7 @@ #include #include +#include #include "shared.h" @@ -205,11 +206,17 @@ print_key_event(struct libinput_event *ev) { struct libinput_event_keyboard *k = libinput_event_get_keyboard_event(ev); enum libinput_key_state state; + uint32_t key; + const char *keyname; print_event_time(libinput_event_keyboard_get_time(k)); state = libinput_event_keyboard_get_key_state(k); - printf("%d %s\n", - libinput_event_keyboard_get_key(k), + + key = libinput_event_keyboard_get_key(k); + keyname = libevdev_event_code_get_name(EV_KEY, key); + printf("%s (%d) %s\n", + keyname ? keyname : "???", + key, state == LIBINPUT_KEY_STATE_PRESSED ? "pressed" : "released"); } diff --git a/tools/libinput-list-devices.c b/tools/libinput-list-devices.c index 66251731..68ddb612 100644 --- a/tools/libinput-list-devices.c +++ b/tools/libinput-list-devices.c @@ -120,17 +120,17 @@ calibration_default(struct libinput_device *device) float calibration[6]; if (!libinput_device_config_calibration_has_matrix(device)) { - asprintf(&str, "n/a"); + xasprintf(&str, "n/a"); return str; } if (libinput_device_config_calibration_get_default_matrix(device, calibration) == 0) { - asprintf(&str, "identity matrix"); + xasprintf(&str, "identity matrix"); return str; } - asprintf(&str, + xasprintf(&str, "%.2f %.2f %.2f %.2f %.2f %.2f", calibration[0], calibration[1], @@ -150,13 +150,13 @@ scroll_defaults(struct libinput_device *device) scroll_methods = libinput_device_config_scroll_get_methods(device); if (scroll_methods == LIBINPUT_CONFIG_SCROLL_NO_SCROLL) { - asprintf(&str, "none"); + xasprintf(&str, "none"); return str; } method = libinput_device_config_scroll_get_default_method(device); - asprintf(&str, + xasprintf(&str, "%s%s%s%s%s%s", (method == LIBINPUT_CONFIG_SCROLL_2FG) ? "*" : "", (scroll_methods & LIBINPUT_CONFIG_SCROLL_2FG) ? "two-finger " : "", @@ -176,12 +176,12 @@ click_defaults(struct libinput_device *device) click_methods = libinput_device_config_click_get_methods(device); if (click_methods == LIBINPUT_CONFIG_CLICK_METHOD_NONE) { - asprintf(&str, "none"); + xasprintf(&str, "none"); return str; } method = libinput_device_config_click_get_default_method(device); - asprintf(&str, + xasprintf(&str, "%s%s%s%s", (method == LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "*" : "", (click_methods & LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "button-areas " : "",