...
 
Commits (31)
......@@ -41,6 +41,12 @@ libinput_jobs:
name: Build - No docs
environment:
MESON_PARAMS: -Ddocumentation=false
build_dist: &build_dist
run:
<<: *build_and_test_default
name: Build - ninja dist
environment:
NINJA_ARGS: dist
ninja_scan_build: &ninja_scan_build
run:
<<: *build_and_test_default
......@@ -88,6 +94,7 @@ fedora_build_all: &fedora_build_all
- *build_no_debug_gui
- *build_no_tests
- *build_no_docs
- *build_dist
ubuntu_install: &ubuntu_install
run:
......@@ -112,6 +119,7 @@ ubuntu_build_all: &ubuntu_build_all
- *build_no_debug_gui
- *build_no_tests
- *build_no_docs
- *build_dist
scan_build_run: &scan_build_run
<<: *default_settings
......
This diff is collapsed.
# Source for the button debouncing wave diagram
# Paste into http://wavedrom.com/editor.html
{signal: [
{name:'current mode', wave: '3............', data: ['normal button press and release']},
{name:'physical button', wave: '01......0....'},
{name:'application ', wave: '01......0....'},
{},
['bounce mode',
{name:'current mode', wave: '4............', data: ['debounced button press']},
{name:'physical button', wave: '0101...0.....'},
{name: 'timeouts', wave: '01...0.1...0.'},
{name:'application ', wave: '01.....0.....'},
{},
{name:'current mode', wave: '4............', data: ['debounced button release']},
{name:'physical button', wave: '1...010......'},
{name: 'timeouts', wave: '0...1...0....'},
{name:'application ', wave: '1...0........'},
{},
{name:'current mode', wave: '5............', data: ['delayed button press']},
{name:'physical button', wave: '1...01.......'},
{name: 'timeouts', wave: '0...1...0....'},
{name:'application ', wave: '1...0...1....'},
{},
{name:'current mode', wave: '5............', data: ['delayed button release']},
{name:'physical button', wave: '0...10.......'},
{name: 'timeouts', wave: '0...1...0....'},
{name:'application ', wave: '0...1...0....'},
],
{},
['spurious mode',
{name:'current mode', wave: '3............', data: ['first spurious button release ']},
{name:'physical button', wave: '1.......01...'},
{name:'application ', wave: '1.......01...'},
{},
{name:'current mode', wave: '3............', data: ['later spurious button release ']},
{name:'physical button', wave: '1....01......'},
{name: 'timeouts', wave: '0....1..0....'},
{name:'application ', wave: '1............'},
{},
{name:'current mode', wave: '3............', data: ['delayed release in spurious mode ']},
{name:'physical button', wave: '1....0.......'},
{name: 'timeouts', wave: '0....1..0....'},
{name:'application ', wave: '1.......0....'}
],
],
head:{
text:'Button Debouncing Scenarios',
},
}
......@@ -9,12 +9,25 @@ though the user only pressed or clicked the button once. This effect can be
counteracted by "debouncing" the buttons, usually by ignoring erroneous
events.
libinput has a built-in debouncing for hardware defects. This feature is
available for all button-base devices but not active by default. When
libinput detects a faulty button on a device, debouncing is enabled and a
warning is printed to the log. Subsequent button events are handled
correctly in that bouncing button events are ignored, a user should thus see
the expected behavior.
libinput provides two methods of debouncing buttons, referred to as the
"bounce" and "spurious" methods:
- In the "bounce" method, libinput monitors hardware bouncing on button
state changes, i.e. when a user clicks or releases a button. For example,
if a user presses a button but the hardware generates a
press-release-press sequence in quick succession, libinput ignores the
release and second press event. This method is always enabled.
- in the "spurious" method, libinput detects spurious releases of a button
while the button is physically held down by the user. These releases are
immediately followed by a press event. libinput monitors for these events
and ignores the release and press event. This method is disabled by
default and enables once libinput detects the first faulty event sequence.
The "bounce" method guarantees that all press events are delivered
immediately and most release events are delivered immediately. The
"spurious" method requires that release events are delayed, libinput thus
does not enable this method unless a faulty event sequence is detected. A
message is printed to the log when spurious deboucing was detected.
Note that libinput's debouncing intended to correct hardware damage or
substandard hardware. Debouncing is also used as an accessibility feature
......@@ -23,4 +36,12 @@ physical key presses, usually caused by involuntary muscle movement, must be
filtered to only one key press. This feature must be implemented higher in
the stack, libinput is limited to hardware debouncing.
Below is an illustration of the button debouncing modes to show the relation
of the physical button state and the application state. Where applicable, an
extra line is added to show the timeouts used by libinput that
affect the button state handling. The waveform's high and low states
correspond to the buttons 'pressed' and 'released' states, respectively.
@image html button-debouncing-wave-diagram.svg "Diagram illustrating button debouncing"
*/
......@@ -125,6 +125,20 @@ Changes performed by xinput do not persist across device hotplugs. xinput is
considered a debugging and testing tool only and should not be used for
permanent configurations.
@section faq_configuration Can you add a configuration option for $FEATURE?
No. At least that's going to be the initial answer. Read <a
href="http://who-t.blogspot.com/2016/04/why-libinput-doesnt-have-lot-of-config.html">Why
libinput doesn't have a lot of configuration options</a> first.
Configuration options for most features are a signal that we are incapable
of handling it correctly. To get to that point, we want to be sure we're
truly incapable of doing so. libinput has several features that
are handled automatically (and correctly) that users wanted to have
configuration options for initially.
So the answer to this question will almost always be 'no'. A configuration
option is, in most cases, a cop-out.
@section faq_synclient Why don't synclient and syndaemon work with libinput?
Synclient and syndaemon rely on X input device properties that are specific
......
......@@ -9,8 +9,8 @@ EXTRACT_STATIC = YES
MAX_INITIALIZER_LINES = 0
QUIET = YES
INPUT = @INPUT@
IMAGE_PATH = @top_srcdir@/doc/svg \
@top_srcdir@/doc/dot
IMAGE_PATH = "@top_srcdir@/doc/svg" \
"@top_srcdir@/doc/dot"
GENERATE_HTML = YES
SEARCHENGINE = NO
USE_MATHJAX = YES
......@@ -20,11 +20,11 @@ MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
PREDEFINED = LIBINPUT_ATTRIBUTE_PRINTF(f, a)= \
LIBINPUT_ATTRIBUTE_DEPRECATED
DOTFILE_DIRS = @top_srcdir@/doc/dot
DOTFILE_DIRS = "@top_srcdir@/doc/dot"
HTML_HEADER = @top_srcdir@/doc/style/header.html
HTML_FOOTER = @top_srcdir@/doc/style/footer.html
HTML_EXTRA_STYLESHEET = @top_srcdir@/doc/style/bootstrap.css \
@top_srcdir@/doc/style/customdoxygen.css \
@top_srcdir@/doc/style/libinputdoxygen.css
USE_MDFILE_AS_MAINPAGE = @top_srcdir@/README.md
HTML_HEADER = "@top_srcdir@/doc/style/header.html"
HTML_FOOTER = "@top_srcdir@/doc/style/footer.html"
HTML_EXTRA_STYLESHEET = "@top_srcdir@/doc/style/bootstrap.css" \
"@top_srcdir@/doc/style/customdoxygen.css" \
"@top_srcdir@/doc/style/libinputdoxygen.css"
USE_MDFILE_AS_MAINPAGE = "@top_srcdir@/README.md"
This source diff could not be displayed because it is too large. You can view the blob instead.
/**
@page test-suite libinput test suite
The libinput test suite is based on
[Check](http://check.sourceforge.net/doc/check_html/) and runs automatically
during `make check`. Check itself is wrapped into a libinput-specific test
suite called *litest*. Tests are found in `$srcdir/test/`, the main test
suite is `libinput-test-suite-runner`.
The test suite has a make-like job control enabled by the `-j` or `--jobs`
flag and will fork off as many parallel processes as given by this flag. The
default if unspecified is 8. When debugging a specific test case failure it
is recommended to employ test filtures (see @ref test-filtering) and disable
parallel tests. The test suite automatically disables parallel make when run
in gdb.
libinput ships with a number of tests all run automatically on `ninja test`.
The primary test suite is the `libinput-test-suite-runner`. When testing,
the `libinput-test-suite-runner` should always be invoked to check for
behavior changes.
The test suite runner uses
[Check](http://check.sourceforge.net/doc/check_html/) underneath the hood
but most of the functionality is abstracted into *litest* wrappers.
The test suite runner has a make-like job control enabled by the `-j` or
`--jobs` flag and will fork off as many parallel processes as given by this
flag. The default if unspecified is 8. When debugging a specific test case
failure it is recommended to employ test filtures (see @ref test-filtering)
and disable parallel tests. The test suite automatically disables parallel
make when run in gdb.
@section test-config X.Org config to avoid interference
......@@ -28,35 +31,67 @@ with your desktop.
Most tests require the creation of uinput devices and access to the
resulting `/dev/input/eventX` nodes. Some tests require temporary udev rules.
<b>This usually requires the tests to be run as root</b>.
<b>This usually requires the tests to be run as root</b>. If not run as
root, the test suite runner will exit with status 77, interpreted as
"skipped" by ninja.
@section test-filtering Selective running of tests
litest's tests are grouped by test groups and devices. A test group is e.g.
"touchpad:tap" and incorporates all tapping-related tests for touchpads.
Each test function is (usually) run with one or more specific devices.
The `--list` commandline argument shows the list of suites and tests.
litest's tests are grouped into test groups, test names and devices. A test
group is e.g. "touchpad:tap" and incorporates all tapping-related tests for
touchpads. Each test function is (usually) run with one or more specific
devices. The `--list` commandline argument shows the list of suites and
tests. This is useful when trying to figure out if a specific test is
run for a device.
@code
$ ./test/libinput-test-suite-runner --list
device:wheel:
wheel only
blackwidow
device:invalid devices:
no device
device:group:
no device
logitech trackball
MS surface cover
mouse_roccat
wheel only
blackwidow
...
pointer:left-handed:
pointer_left_handed_during_click_multiple_buttons:
trackpoint
ms-surface-cover
mouse-wheelclickcount
mouse-wheelclickangle
low-dpi-mouse
mouse-roccat
mouse-wheel-tilt
mouse
logitech-trackball
cyborg-rat
magicmouse
pointer_left_handed_during_click:
trackpoint
ms-surface-cover
mouse-wheelclickcount
mouse-wheelclickangle
low-dpi-mouse
mouse-roccat
mouse-wheel-tilt
mouse
logitech-trackball
cyborg-rat
litest-magicmouse-device
pointer_left_handed:
trackpoint
ms-surface-cover
mouse-wheelclickcount
mouse-wheelclickangle
low-dpi-mouse
mouse-roccat
mouse-wheel-tilt
mouse
...
@endcode
In the above example, the "device:wheel" suite is run for the "wheel only" and
the "blackwidow" device. Both devices are automatically instantiated through
uinput by litest. The "no device" entry signals that litest does not
instantiate a uinput device for a specific test (though the test itself may
In the above example, the "pointer:left-handed" suite contains multiple
tests, e.g. "pointer_left_handed_during_click" (this is also the function
name of the test, making it easy to grep for). This particular test is run
for various devices including the trackpoint device and the magic mouse
device.
The "no device" entry signals that litest does not instantiate a uinput
device for a specific test (though the test itself may
instantiate one).
The `--filter-test` argument enables selective running of tests through
......@@ -93,7 +128,7 @@ environment variable, if set, also enables verbose mode.
@code
$ ./test/libinput-test-suite-runner --verbose
$ LITEST_VERBOSE=1 make check
$ LITEST_VERBOSE=1 ninja test
@endcode
*/
project('libinput', 'c', 'cpp',
version : '1.9.1',
version : '1.9.4',
license : 'MIT/Expat',
default_options : [ 'c_std=gnu99', 'warning_level=2' ],
meson_version : '>= 0.40.0')
......@@ -156,7 +156,9 @@ src_libinput = [
'src/libinput-private.h',
'src/evdev.c',
'src/evdev.h',
'src/evdev-debounce.c',
'src/evdev-fallback.c',
'src/evdev-fallback.h',
'src/evdev-middle-button.c',
'src/evdev-mt-touchpad.c',
'src/evdev-mt-touchpad.h',
......@@ -298,6 +300,7 @@ if get_option('documentation')
meson.source_root() + '/doc/dot/libinput-stack-gnome.gv',
meson.source_root() + '/doc/dot/evemu.gv',
# svgs
meson.source_root() + '/doc/svg/button-debouncing-wave-diagram.svg',
meson.source_root() + '/doc/svg/button-scrolling.svg',
meson.source_root() + '/doc/svg/clickfinger.svg',
meson.source_root() + '/doc/svg/clickfinger-distance.svg',
......
This diff is collapsed.
This diff is collapsed.
/*
* Copyright © 2010 Intel Corporation
* Copyright © 2013 Jonas Ådahl
* Copyright © 2013-2017 Red Hat, Inc.
* Copyright © 2017 James Ye <jye836@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#ifndef EVDEV_FALLBACK_H
#define EVDEV_FALLBACK_H
#include "evdev.h"
enum debounce_state {
DEBOUNCE_STATE_IS_UP = 100,
DEBOUNCE_STATE_IS_DOWN,
DEBOUNCE_STATE_DOWN_WAITING,
DEBOUNCE_STATE_RELEASE_PENDING,
DEBOUNCE_STATE_RELEASE_DELAYED,
DEBOUNCE_STATE_RELEASE_WAITING,
DEBOUNCE_STATE_MAYBE_SPURIOUS,
DEBOUNCE_STATE_RELEASED,
DEBOUNCE_STATE_PRESS_PENDING,
};
struct fallback_dispatch {
struct evdev_dispatch base;
struct evdev_device *device;
struct libinput_device_config_calibration calibration;
struct {
bool is_enabled;
int angle;
struct matrix matrix;
struct libinput_device_config_rotation config;
} rotation;
struct {
struct device_coords point;
int32_t seat_slot;
} abs;
struct {
int slot;
struct mt_slot *slots;
size_t slots_len;
bool want_hysteresis;
struct device_coords hysteresis_margin;
} mt;
struct device_coords rel;
struct device_coords wheel;
struct {
/* The struct for the tablet mode switch device itself */
struct {
int state;
} sw;
/* The struct for other devices listening to the tablet mode
switch */
struct {
struct evdev_device *sw_device;
struct libinput_event_listener listener;
} other;
} tablet_mode;
/* Bitmask of pressed keys used to ignore initial release events from
* the kernel. */
unsigned long hw_key_mask[NLONGS(KEY_CNT)];
unsigned long last_hw_key_mask[NLONGS(KEY_CNT)];
enum evdev_event_type pending_event;
/* true if we're reading events (i.e. not suspended) but we're
ignoring them */
bool ignore_events;
struct {
#if 0
enum evdev_debounce_state state;
uint64_t button_up_time;
#endif
unsigned int button_code;
uint64_t button_time;
struct libinput_timer timer;
struct libinput_timer timer_short;
enum debounce_state state;
bool spurious_enabled;
} debounce;
struct {
enum switch_reliability reliability;
bool is_closed;
bool is_closed_client_state;
/* We allow up to 3 paired keyboards for the lid switch
* listener. Only one keyboard should exist, but that can
* have more than one event node.
*
* Note: this is a sparse list, any element may have a
* non-NULL device.
*/
struct paired_keyboard {
struct evdev_device *device;
struct libinput_event_listener listener;
} paired_keyboard[3];
} lid;
};
static inline struct fallback_dispatch*
fallback_dispatch(struct evdev_dispatch *dispatch)
{
evdev_verify_dispatch_type(dispatch, DISPATCH_FALLBACK);
return container_of(dispatch, struct fallback_dispatch, base);
}
enum key_type {
KEY_TYPE_NONE,
KEY_TYPE_KEY,
KEY_TYPE_BUTTON,
};
static inline enum key_type
get_key_type(uint16_t code)
{
switch (code) {
case BTN_TOOL_PEN:
case BTN_TOOL_RUBBER:
case BTN_TOOL_BRUSH:
case BTN_TOOL_PENCIL:
case BTN_TOOL_AIRBRUSH:
case BTN_TOOL_MOUSE:
case BTN_TOOL_LENS:
case BTN_TOOL_QUINTTAP:
case BTN_TOOL_DOUBLETAP:
case BTN_TOOL_TRIPLETAP:
case BTN_TOOL_QUADTAP:
case BTN_TOOL_FINGER:
case BTN_TOUCH:
return KEY_TYPE_NONE;
}
if (code >= KEY_ESC && code <= KEY_MICMUTE)
return KEY_TYPE_KEY;
if (code >= BTN_MISC && code <= BTN_GEAR_UP)
return KEY_TYPE_BUTTON;
if (code >= KEY_OK && code <= KEY_LIGHTS_TOGGLE)
return KEY_TYPE_KEY;
if (code >= BTN_DPAD_UP && code <= BTN_DPAD_RIGHT)
return KEY_TYPE_BUTTON;
if (code >= KEY_ALS_TOGGLE && code <= KEY_ONSCREEN_KEYBOARD)
return KEY_TYPE_KEY;
if (code >= BTN_TRIGGER_HAPPY && code <= BTN_TRIGGER_HAPPY40)
return KEY_TYPE_BUTTON;
return KEY_TYPE_NONE;
}
static inline void
hw_set_key_down(struct fallback_dispatch *dispatch, int code, int pressed)
{
long_set_bit_state(dispatch->hw_key_mask, code, pressed);
}
static inline bool
hw_key_has_changed(struct fallback_dispatch *dispatch, int code)
{
return long_bit_is_set(dispatch->hw_key_mask, code) !=
long_bit_is_set(dispatch->last_hw_key_mask, code);
}
static inline void
hw_key_update_last_state(struct fallback_dispatch *dispatch)
{
static_assert(sizeof(dispatch->hw_key_mask) ==
sizeof(dispatch->last_hw_key_mask),
"Mismatching key mask size");
memcpy(dispatch->last_hw_key_mask,
dispatch->hw_key_mask,
sizeof(dispatch->hw_key_mask));
}
static inline bool
hw_is_key_down(struct fallback_dispatch *dispatch, int code)
{
return long_bit_is_set(dispatch->hw_key_mask, code);
}
static inline int
get_key_down_count(struct evdev_device *device, int code)
{
return device->key_count[code];
}
void fallback_init_debounce(struct fallback_dispatch *dispatch);
void fallback_debounce_handle_state(struct fallback_dispatch *dispatch,
uint64_t time);
#endif
......@@ -1055,15 +1055,21 @@ tp_notify_clickpadbutton(struct tp_dispatch *tp,
if (is_topbutton && tp->buttons.trackpoint) {
struct evdev_dispatch *dispatch = tp->buttons.trackpoint->dispatch;
struct input_event event;
struct input_event syn_report = {{ 0, 0 }, EV_SYN, SYN_REPORT, 0 };
event.time = us2tv(time);
event.type = EV_KEY;
event.code = button;
event.value = (state == LIBINPUT_BUTTON_STATE_PRESSED) ? 1 : 0;
syn_report.time = event.time;
dispatch->interface->process(dispatch,
tp->buttons.trackpoint,
&event,
time);
dispatch->interface->process(dispatch,
tp->buttons.trackpoint,
&syn_report,
time);
return 1;
}
......
......@@ -502,6 +502,7 @@ tp_process_trackpoint_button(struct tp_dispatch *tp,
{
struct evdev_dispatch *dispatch;
struct input_event event;
struct input_event syn_report = {{ 0, 0 }, EV_SYN, SYN_REPORT, 0 };
if (!tp->buttons.trackpoint)
return;
......@@ -509,6 +510,7 @@ tp_process_trackpoint_button(struct tp_dispatch *tp,
dispatch = tp->buttons.trackpoint->dispatch;
event = *e;
syn_report.time = e->time;
switch (event.code) {
case BTN_0:
......@@ -527,6 +529,9 @@ tp_process_trackpoint_button(struct tp_dispatch *tp,
dispatch->interface->process(dispatch,
tp->buttons.trackpoint,
&event, time);
dispatch->interface->process(dispatch,
tp->buttons.trackpoint,
&syn_report, time);
}
static void
......@@ -1588,6 +1593,8 @@ tp_interface_process(struct evdev_dispatch *dispatch,
static void
tp_remove_sendevents(struct tp_dispatch *tp)
{
struct paired_keyboard *kbd;
libinput_timer_cancel(&tp->palm.trackpoint_timer);
libinput_timer_cancel(&tp->dwt.keyboard_timer);
......@@ -1596,9 +1603,10 @@ tp_remove_sendevents(struct tp_dispatch *tp)
libinput_device_remove_event_listener(
&tp->palm.trackpoint_listener);
if (tp->dwt.keyboard)
libinput_device_remove_event_listener(
&tp->dwt.keyboard_listener);
ARRAY_FOR_EACH(tp->dwt.paired_keyboard, kbd) {
if (kbd->device)
libinput_device_remove_event_listener(&kbd->listener);
}
if (tp->lid_switch.lid_switch)
libinput_device_remove_event_listener(
......@@ -1964,9 +1972,8 @@ tp_dwt_pair_keyboard(struct evdev_device *touchpad,
struct evdev_device *keyboard)
{
struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
if (tp->dwt.keyboard)
return;
struct paired_keyboard *kbd;
bool found = false;
if ((keyboard->tags & EVDEV_TAG_KEYBOARD) == 0)
return;
......@@ -1974,16 +1981,25 @@ tp_dwt_pair_keyboard(struct evdev_device *touchpad,
if (!tp_want_dwt(touchpad, keyboard))
return;
libinput_device_add_event_listener(&keyboard->base,
&tp->dwt.keyboard_listener,
tp_keyboard_event, tp);
tp->dwt.keyboard = keyboard;
tp->dwt.keyboard_active = false;
ARRAY_FOR_EACH(tp->dwt.paired_keyboard, kbd) {
if (kbd->device)
continue;
evdev_log_debug(touchpad,
"palm: dwt activated with %s<->%s\n",
touchpad->devname,
keyboard->devname);
found = true;
libinput_device_add_event_listener(&keyboard->base,
&kbd->listener,
tp_keyboard_event, tp);
kbd->device = keyboard;
evdev_log_debug(touchpad,
"palm: dwt activated with %s<->%s\n",
touchpad->devname,
keyboard->devname);
break;
}
if (!found)
evdev_log_bug_libinput(touchpad,
"too many internal keyboards for dwt\n");
}
static void
......@@ -2121,6 +2137,7 @@ tp_interface_device_removed(struct evdev_device *device,
struct evdev_device *removed_device)
{
struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
struct paired_keyboard *kbd;
if (removed_device == tp->buttons.trackpoint) {
/* Clear any pending releases for the trackpoint */
......@@ -2134,11 +2151,12 @@ tp_interface_device_removed(struct evdev_device *device,
tp->buttons.trackpoint = NULL;
}
if (removed_device == tp->dwt.keyboard) {
libinput_device_remove_event_listener(
&tp->dwt.keyboard_listener);
tp->dwt.keyboard = NULL;
tp->dwt.keyboard_active = false;
ARRAY_FOR_EACH(tp->dwt.paired_keyboard, kbd) {
if (kbd->device == removed_device) {
libinput_device_remove_event_listener(&kbd->listener);
kbd->device = NULL;
tp->dwt.keyboard_active = false;
}
}
if (removed_device == tp->lid_switch.lid_switch) {
......
......@@ -383,13 +383,20 @@ struct tp_dispatch {
struct libinput_device_config_dwt config;
bool dwt_enabled;
bool keyboard_active;
struct libinput_event_listener keyboard_listener;
struct libinput_timer keyboard_timer;
struct evdev_device *keyboard;
/* We have to allow for more than one device node to be the
* internal dwt keyboard (Razer Blade). But they're the same
* physical device, so we don't care about per-keyboard
* key/modifier masks.
*/
struct paired_keyboard {
struct evdev_device *device;
struct libinput_event_listener listener;
} paired_keyboard[3];
unsigned long key_mask[NLONGS(KEY_CNT)];
unsigned long mod_mask[NLONGS(KEY_CNT)];
bool keyboard_active;
struct libinput_timer keyboard_timer;
uint64_t keyboard_last_press_time;
} dwt;
......
......@@ -1099,17 +1099,17 @@ evdev_read_wheel_click_props(struct evdev_device *device)
/* CLICK_COUNT overrides CLICK_ANGLE */
if (!evdev_read_wheel_click_count_prop(device,
"MOUSE_WHEEL_CLICK_COUNT",
&angles.x))
&angles.y))
evdev_read_wheel_click_prop(device,
"MOUSE_WHEEL_CLICK_ANGLE",
&angles.x);
&angles.y);
if (!evdev_read_wheel_click_count_prop(device,
"MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL",
&angles.y)) {
&angles.x)) {
if (!evdev_read_wheel_click_prop(device,
"MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL",
&angles.y))
angles.y = angles.x;
&angles.x))
angles.x = angles.y;
}
return angles;
......
......@@ -41,13 +41,14 @@
enum evdev_event_type {
EVDEV_NONE,
EVDEV_ABSOLUTE_TOUCH_DOWN,
EVDEV_ABSOLUTE_MOTION,
EVDEV_ABSOLUTE_TOUCH_UP,
EVDEV_ABSOLUTE_MT_DOWN,
EVDEV_ABSOLUTE_MT_MOTION,
EVDEV_ABSOLUTE_MT_UP,
EVDEV_RELATIVE_MOTION,
EVDEV_ABSOLUTE_TOUCH_DOWN = (1 << 0),
EVDEV_ABSOLUTE_MOTION = (1 << 1),
EVDEV_ABSOLUTE_TOUCH_UP = (1 << 2),
EVDEV_ABSOLUTE_MT= (1 << 3),
EVDEV_WHEEL = (1 << 4),
EVDEV_KEY = (1 << 5),
EVDEV_RELATIVE_MOTION = (1 << 6),
EVDEV_BUTTON = (1 << 7),
};
enum evdev_device_seat_capability {
......@@ -150,7 +151,16 @@ enum evdev_debounce_state {
DEBOUNCE_ACTIVE,
};
enum mt_slot_state {
SLOT_STATE_NONE,
SLOT_STATE_BEGIN,
SLOT_STATE_UPDATE,
SLOT_STATE_END,
};
struct mt_slot {
bool dirty;
enum mt_slot_state state;
int32_t seat_slot;
struct device_coords point;
struct device_coords hysteresis_center;
......
......@@ -100,7 +100,7 @@ static const char udev_rule[] =
"\n"
"LABEL=\"mouse_end\"";
TEST_DEVICE("litest-magicmouse-device",
TEST_DEVICE("magicmouse",
.type = LITEST_MAGICMOUSE,
.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
.interface = &interface,
......
......@@ -27,6 +27,7 @@
#include <check.h>
#include <dirent.h>
#include <errno.h>
#include <libgen.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <getopt.h>
......@@ -1071,20 +1072,35 @@ litest_install_model_quirks(struct list *created_files_list)
list_insert(created_files_list, &file->link);
}
static void
litest_init_udev_rules(struct list *created_files)
static inline void
mkdir_p(const char *dir)
{
char *path, *parent;
int rc;
rc = mkdir(UDEV_RULES_D, 0755);
if (rc == -1 && errno != EEXIST)
litest_abort_msg("Failed to create udev rules directory (%s)\n",
strerror(errno));
if (streq(dir, "/"))
return;
path = strdup(dir);
parent = dirname(path);
rc = mkdir(UDEV_HWDB_D, 0755);
if (rc == -1 && errno != EEXIST)
litest_abort_msg("Failed to create udev hwdb directory (%s)\n",
mkdir_p(parent);
rc = mkdir(dir, 0755);
if (rc == -1 && errno != EEXIST) {
litest_abort_msg("Failed to create directory %s (%s)\n",
dir,
strerror(errno));
}
free(path);
}
static void
litest_init_udev_rules(struct list *created_files)
{
mkdir_p(UDEV_RULES_D);
mkdir_p(UDEV_HWDB_D);
litest_install_model_quirks(created_files);
litest_init_all_device_udev_rules(created_files);
......@@ -2002,7 +2018,10 @@ litest_hover_move_two_touches(struct litest_device *d,
}
void
litest_button_click(struct litest_device *d, unsigned int button, bool is_press)
litest_button_click_debounced(struct litest_device *d,
struct libinput *li,
unsigned int button,
bool is_press)
{
struct input_event *ev;
......@@ -2013,7 +2032,9 @@ litest_button_click(struct litest_device *d, unsigned int button, bool is_press)
ARRAY_FOR_EACH(click, ev)
litest_event(d, ev->type, ev->code, ev->value);
libinput_dispatch(li);
litest_timeout_debounce();
libinput_dispatch(li);
}
void
......@@ -2023,7 +2044,7 @@ litest_button_scroll(struct litest_device *dev,
{
struct libinput *li = dev->libinput;
litest_button_click(dev, button, 1);
litest_button_click_debounced(dev, li, button, 1);
libinput_dispatch(li);
litest_timeout_buttonscroll();
......@@ -2033,7 +2054,7 @@ litest_button_scroll(struct litest_device *dev,
litest_event(dev, EV_REL, REL_Y, dy);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
litest_button_click(dev, button, 0);
litest_button_click_debounced(dev, li, button, 0);
libinput_dispatch(li);
}
......@@ -2041,7 +2062,14 @@ litest_button_scroll(struct litest_device *dev,
void
litest_keyboard_key(struct litest_device *d, unsigned int key, bool is_press)
{
litest_button_click(d, key, is_press);
struct input_event *ev;
struct input_event click[] = {
{ .type = EV_KEY, .code = key, .value = is_press ? 1 : 0 },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
};
ARRAY_FOR_EACH(click, ev)
litest_event(d, ev->type, ev->code, ev->value);
}
void
......@@ -3154,7 +3182,7 @@ litest_timeout_tapndrag(void)
void
litest_timeout_debounce(void)
{
msleep(15);
msleep(30);
}
void
......
......@@ -596,9 +596,10 @@ litest_hover_move_two_touches(struct litest_device *d,
int steps, int sleep_ms);
void
litest_button_click(struct litest_device *d,
unsigned int button,
bool is_press);
litest_button_click_debounced(struct litest_device *d,
struct libinput *li,
unsigned int button,
bool is_press);
void
litest_button_scroll(struct litest_device *d,
......
......@@ -463,7 +463,7 @@ START_TEST(device_disable_release_buttons)
device = dev->libinput_device;
litest_button_click(dev, BTN_LEFT, true);
litest_button_click_debounced(dev, li, BTN_LEFT, true);
litest_drain_events(li);
status = libinput_device_config_send_events_set_mode(device,
......@@ -497,7 +497,7 @@ START_TEST(device_disable_release_keys)
device = dev->libinput_device;
litest_button_click(dev, KEY_A, true);
litest_button_click_debounced(dev, li, KEY_A, true);
litest_drain_events(li);
status = libinput_device_config_send_events_set_mode(device,
......@@ -616,7 +616,7 @@ START_TEST(device_disable_release_softbutton)
litest_drain_events(li);
litest_touch_down(dev, 0, 90, 90);
litest_button_click(dev, BTN_LEFT, true);
litest_button_click_debounced(dev, li, BTN_LEFT, true);
/* make sure softbutton works */
litest_assert_button_event(li,
......@@ -633,7 +633,7 @@ START_TEST(device_disable_release_softbutton)
litest_assert_empty_queue(li);
litest_button_click(dev, BTN_LEFT, false);
litest_button_click_debounced(dev, li, BTN_LEFT, false);
litest_touch_up(dev, 0);
litest_assert_empty_queue(li);
......@@ -669,8 +669,8 @@ START_TEST(device_disable_topsoftbutton)
litest_drain_events(li);
litest_touch_down(dev, 0, 90, 10);
litest_button_click(dev, BTN_LEFT, true);
litest_button_click(dev, BTN_LEFT, false);
litest_button_click_debounced(dev, li, BTN_LEFT, true);
litest_button_click_debounced(dev, li, BTN_LEFT, false);
litest_touch_up(dev, 0);
litest_wait_for_event(li);
......
......@@ -365,6 +365,61 @@ START_TEST(keyboard_no_buttons)
}
END_TEST
START_TEST(keyboard_frame_order)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
if (!libevdev_has_event_code(dev->evdev, EV_KEY, KEY_A) ||
!libevdev_has_event_code(dev->evdev, EV_KEY, KEY_LEFTSHIFT))
return;
litest_drain_events(li);
litest_event(dev, EV_KEY, KEY_LEFTSHIFT, 1);
litest_event(dev, EV_KEY, KEY_A, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
litest_assert_key_event(li,
KEY_LEFTSHIFT,
LIBINPUT_KEY_STATE_PRESSED);
litest_assert_key_event(li, KEY_A, LIBINPUT_KEY_STATE_PRESSED);
litest_event(dev, EV_KEY, KEY_LEFTSHIFT, 0);
litest_event(dev, EV_KEY, KEY_A, 0);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
litest_assert_key_event(li,
KEY_LEFTSHIFT,
LIBINPUT_KEY_STATE_RELEASED);
litest_assert_key_event(li, KEY_A, LIBINPUT_KEY_STATE_RELEASED);
litest_event(dev, EV_KEY, KEY_A, 1);
litest_event(dev, EV_KEY, KEY_LEFTSHIFT, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
litest_assert_key_event(li, KEY_A, LIBINPUT_KEY_STATE_PRESSED);
litest_assert_key_event(li,
KEY_LEFTSHIFT,
LIBINPUT_KEY_STATE_PRESSED);
litest_event(dev, EV_KEY, KEY_A, 0);
litest_event(dev, EV_KEY, KEY_LEFTSHIFT, 0);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
litest_assert_key_event(li, KEY_A, LIBINPUT_KEY_STATE_RELEASED);
litest_assert_key_event(li,
KEY_LEFTSHIFT,
LIBINPUT_KEY_STATE_RELEASED);
libinput_dispatch(li);
}
END_TEST
START_TEST(keyboard_leds)
{
struct litest_device *dev = litest_current_device();
......@@ -432,6 +487,7 @@ litest_setup_tests_keyboard(void)
litest_add("keyboard:time", keyboard_time_usec, LITEST_KEYS, LITEST_ANY);
litest_add("keyboard:events", keyboard_no_buttons, LITEST_KEYS, LITEST_ANY);
litest_add("keyboard:events", keyboard_frame_order, LITEST_KEYS, LITEST_ANY);
litest_add("keyboard:leds", keyboard_leds, LITEST_ANY, LITEST_ANY);
......
......@@ -416,8 +416,8 @@ START_TEST(event_conversion_tablet)
litest_tablet_proximity_in(dev, 50, 50, axes);
litest_tablet_motion(dev, 60, 50, axes);
litest_button_click(dev, BTN_STYLUS, true);
litest_button_click(dev, BTN_STYLUS, false);
litest_button_click_debounced(dev, li, BTN_STYLUS, true);
litest_button_click_debounced(dev, li, BTN_STYLUS, false);
libinput_dispatch(li);
......@@ -458,7 +458,7 @@ START_TEST(event_conversion_tablet_pad)
struct libinput_event *event;
int events = 0;
litest_button_click(dev, BTN_0, true);
litest_button_click_debounced(dev, li, BTN_0, true);
litest_pad_ring_start(dev, 10);
litest_pad_ring_end(dev);
......
......@@ -68,8 +68,8 @@ START_TEST(pad_time)
if (!libevdev_has_event_code(dev->evdev, EV_KEY, code))
continue;
litest_button_click(dev, code, 1);
litest_button_click(dev, code, 0);
litest_button_click_debounced(dev, li, code, 1);
litest_button_click_debounced(dev, li, code, 0);
libinput_dispatch(li);
switch (code) {
......@@ -98,8 +98,8 @@ START_TEST(pad_time)
litest_drain_events(li);
msleep(10);
litest_button_click(dev, code, 1);
litest_button_click(dev, code, 0);
litest_button_click_debounced(dev, li, code, 1);
litest_button_click_debounced(dev, li, code, 0);
libinput_dispatch(li);
ev = libinput_get_event(li);
......@@ -156,8 +156,8 @@ START_TEST(pad_button)
if (!libevdev_has_event_code(dev->evdev, EV_KEY, code))
continue;
litest_button_click(dev, code, 1);
litest_button_click(dev, code, 0);
litest_button_click_debounced(dev, li, code, 1);
litest_button_click_debounced(dev, li, code, 0);
libinput_dispatch(li);
switch (code) {
......@@ -207,8 +207,8 @@ START_TEST(pad_button_mode_groups)
if (!libevdev_has_event_code(dev->evdev, EV_KEY, code))
continue;
litest_button_click(dev, code, 1);
litest_button_click(dev, code, 0);
litest_button_click_debounced(dev, li, code, 1);
litest_button_click_debounced(dev, li, code, 0);
libinput_dispatch(li);
switch (code) {
......
This diff is collapsed.
......@@ -1041,7 +1041,7 @@ START_TEST(clickpad_finger_pin)
litest_touch_move_to(dev, 0, 48, 48, 50, 50, 10, 1);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
litest_button_click(dev, BTN_LEFT, true);
litest_button_click_debounced(dev, li, BTN_LEFT, true);
litest_drain_events(li);
litest_touch_move_to(dev, 0, 50, 50, 50 + dist, 50 + dist, 10, 1);
......@@ -1050,7 +1050,7 @@ START_TEST(clickpad_finger_pin)
litest_assert_empty_queue(li);
litest_button_click(dev, BTN_LEFT, false);
litest_button_click_debounced(dev, li, BTN_LEFT, false);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON);
/* still pinned after release */
......@@ -1490,7 +1490,7 @@ START_TEST(clickpad_softbutton_hover_into_buttons)
litest_touch_move_to(dev, 0, 90, 90, 91, 91, 1, 0);
litest_button_click(dev, BTN_LEFT, true);
litest_button_click_debounced(dev, li, BTN_LEFT, true);
libinput_dispatch(li);
litest_assert_button_event(li,
......@@ -1498,7 +1498,7 @@ START_TEST(clickpad_softbutton_hover_into_buttons)
LIBINPUT_BUTTON_STATE_PRESSED);
litest_assert_empty_queue(li);
litest_button_click(dev, BTN_LEFT, false);
litest_button_click_debounced(dev, li, BTN_LEFT, false);
litest_touch_up(dev, 0);
litest_assert_button_event(li,
......
......@@ -332,8 +332,8 @@ START_TEST(touchpad_1fg_multitap_n_drag_click)
litest_touch_down(dev, 0, 50, 50);
libinput_dispatch(li);
litest_button_click(dev, BTN_LEFT, true);
litest_button_click(dev, BTN_LEFT, false);
litest_button_click_debounced(dev, li, BTN_LEFT, true);
litest_button_click_debounced(dev, li, BTN_LEFT, false);
libinput_dispatch(li);
for (ntaps = 0; ntaps <= range; ntaps++) {
......@@ -627,8 +627,8 @@ START_TEST(touchpad_1fg_multitap_n_drag_tap_click)
litest_touch_up(dev, 0);
litest_touch_down(dev, 0, 70, 50);
litest_button_click(dev, BTN_LEFT, true);
litest_button_click(dev, BTN_LEFT, false);
litest_button_click_debounced(dev, li, BTN_LEFT, true);
litest_button_click_debounced(dev, li, BTN_LEFT, false);
libinput_dispatch(li);
litest_assert_button_event(li,
......@@ -799,8 +799,8 @@ START_TEST(touchpad_1fg_tap_n_drag_draglock_tap_click)
litest_touch_up(dev, 0);
litest_touch_down(dev, 0, 50, 50);
litest_button_click(dev, BTN_LEFT, true);
litest_button_click(dev, BTN_LEFT, false);
litest_button_click_debounced(dev, li, BTN_LEFT, true);
litest_button_click_debounced(dev, li, BTN_LEFT, false);
libinput_dispatch(li);
litest_assert_button_event(li, BTN_LEFT,
......
This diff is collapsed.
......@@ -43,9 +43,9 @@ START_TEST(trackpoint_middlebutton)
litest_drain_events(li);
/* A quick middle button click should get reported normally */
litest_button_click(dev, BTN_MIDDLE, 1);
litest_button_click_debounced(dev, li, BTN_MIDDLE, 1);
msleep(2);
litest_button_click(dev, BTN_MIDDLE, 0);
litest_button_click_debounced(dev, li, BTN_MIDDLE, 0);
litest_wait_for_event(li);
......@@ -173,7 +173,7 @@ START_TEST(trackpoint_topsoftbuttons_left_handed_trackpoint)
litest_touch_down(touchpad, 0, 5, 5);
libinput_dispatch(li);
litest_button_click(touchpad, BTN_LEFT, true);
litest_button_click_debounced(touchpad, li, BTN_LEFT, true);
libinput_dispatch(li);
event = libinput_get_event(li);
......@@ -184,7 +184,7 @@ START_TEST(trackpoint_topsoftbuttons_left_handed_trackpoint)
ck_assert(device == trackpoint->libinput_device);
libinput_event_destroy(event);
litest_button_click(touchpad, BTN_LEFT, false);
litest_button_click_debounced(touchpad, li, BTN_LEFT, false);
libinput_dispatch(li);
event = libinput_get_event(li);
litest_is_button_event(event,
......@@ -216,7 +216,7 @@ START_TEST(trackpoint_topsoftbuttons_left_handed_touchpad)
litest_touch_down(touchpad, 0, 5, 5);
libinput_dispatch(li);
litest_button_click(touchpad, BTN_LEFT, true);
litest_button_click_debounced(touchpad, li, BTN_LEFT, true);
libinput_dispatch(li);
event = libinput_get_event(li);
......@@ -225,7 +225,7 @@ START_TEST(trackpoint_topsoftbuttons_left_handed_touchpad)
ck_assert(device == trackpoint->libinput_device);
libinput_event_destroy(event);
litest_button_click(touchpad, BTN_LEFT, false);
litest_button_click_debounced(touchpad, li, BTN_LEFT, false);
libinput_dispatch(li);
event = libinput_get_event(li);
litest_is_button_event(event,
......@@ -260,7 +260,7 @@ START_TEST(trackpoint_topsoftbuttons_left_handed_both)
litest_touch_down(touchpad, 0, 5, 5);
libinput_dispatch(li);
litest_button_click(touchpad, BTN_LEFT, true);
litest_button_click_debounced(touchpad, li, BTN_LEFT, true);
libinput_dispatch(li);
event = libinput_get_event(li);
......@@ -271,7 +271,7 @@ START_TEST(trackpoint_topsoftbuttons_left_handed_both)
ck_assert(device == trackpoint->libinput_device);
libinput_event_destroy(event);
litest_button_click(touchpad, BTN_LEFT, false);
litest_button_click_debounced(touchpad, li, BTN_LEFT, false);
libinput_dispatch(li);
event = libinput_get_event(li);
litest_is_button_event(event,
......
......@@ -747,6 +747,9 @@ print_switch_event(struct libinput_event *ev)
case LIBINPUT_SWITCH_LID:
which = "lid";
break;
case LIBINPUT_SWITCH_TABLET_MODE:
which = "tablet-mode";
break;
default:
abort();
}
......
......@@ -62,6 +62,9 @@ Enable or disable natural scrolling
.B \-\-enable\-left\-handed|\-\-disable\-left\-handed
Enable or disable left handed button configuration
.TP 8
.B \-\-enable\-middlebutton|\-\-disable\-middlebutton
Enable or disable middle button emulation
.TP 8
.B \-\-enable\-dwt|\-\-disable\-dwt
Enable or disable disable-while-typing
.TP 8
......
......@@ -314,6 +314,12 @@ print_device_notify(struct libinput_event *ev)
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_TABLET_PAD))
printf("tablet-pad");
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_GESTURE))
printf("gesture");
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_SWITCH))
printf("switch");
printf("\n");
printf("Tap-to-click: %s\n", tap_default(dev));
......
......@@ -26,9 +26,15 @@
import sys
import argparse
import evdev
import evdev.ecodes
import pyudev
try:
import evdev
import evdev.ecodes
import pyudev
except ModuleNotFoundError as e:
print('Error: {}'.format(str(e)), file=sys.stderr)
print('One or more python modules are missing. Please install those '
'modules and re-run this tool.')
sys.exit(1)
class Range(object):
......@@ -180,6 +186,9 @@ class Device(object):
self.path = path
self.device = evdev.InputDevice(self.path)
print("Using {}: {}\n".format(self.device.name, self.path))
# capabilities returns a dict with the EV_* codes as key,
# each of which is a list of tuples of (code, AbsInfo)
#
......
......@@ -9,7 +9,7 @@ libinput\-measure\-touch-size \- measure touch size and orientation of devices
The
.B "libinput measure touch\-size"
tool measures the size and orientation of a touch as provided by the kernel.
an interactive tool. When executed, the tool will prompt the user to
This is an interactive tool. When executed, the tool will prompt the user to
interact with the touch device. On termination, the tool prints a summary of the
values seen. This data should be attached to any
touch\-size\-related bug report.
......
......@@ -26,9 +26,15 @@
import sys
import argparse
import evdev
import evdev.ecodes
import pyudev
try:
import evdev
import evdev.ecodes
import pyudev
except ModuleNotFoundError as e:
print('Error: {}'.format(str(e)), file=sys.stderr)
print('One or more python modules are missing. Please install those '
'modules and re-run this tool.')
sys.exit(1)
class Range(object):
......@@ -139,6 +145,9 @@ class Device(object):
self.path = path
self.device = evdev.InputDevice(self.path)
print("Using {}: {}\n".format(self.device.name, self.path))
# capabilities rturns a dict with the EV_* codes as key,
# each of which is a list of tuples of (code, AbsInfo)
#
......
.TH libinput-measure-touchpad-tap "1" "" "libinput @LIBINPUT_VERSION@" "libinput Manual"
.SH NAME
libinput\-measure\-touchpad\-tap \- measure tap-to-click properities of devices
libinput\-measure\-touchpad\-tap \- measure tap-to-click properties of devices
.SH SYNOPSIS
.B libinput measure touchpad\-tap [\-\-help] [\-\-format=\fI<format>\fB] \fI[/dev/input/event0]\fR
.SH DESCRIPTION
......
......@@ -26,9 +26,15 @@
import sys
import argparse
import evdev
import evdev.ecodes
import pyudev
try:
import evdev
import evdev.ecodes
import pyudev
except ModuleNotFoundError as e:
print('Error: {}'.format(str(e)), file=sys.stderr)
print('One or more python modules are missing. Please install those '
'modules and re-run this tool.')
sys.exit(1)
MINIMUM_EVENT_COUNT = 1000
......@@ -54,6 +60,8 @@ class Device(object):
self.device = evdev.InputDevice(self.path)
print("Using {}: {}\n".format(self.device.name, path))
self.deltas = []
self.nxdeltas = 0
self.nydeltas = 0
......@@ -183,7 +191,7 @@ def main(args):
except KeyboardInterrupt:
device.print_summary()
except (PermissionError, OSError):
print("Error: failed to open device")
print("Error: failed to open device. Are you running as root?")
except InvalidDeviceError as e:
print("Error: {}".format(e))
......
......@@ -54,7 +54,7 @@ Measure tap-to-click time
.B libinput\-measure\-touchpad\-pressure(1)
Measure touch pressure