mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2025-12-20 04:30:06 +01:00
touchpad: add a tool to measure the touchpad fuzz
Well, I say "measure" but really at this point it just reads the properties/axes and then does it's best to auto-generate a hwdb entry that matches the user's hardware and sets a fuzz value on the device. Ideally this reduces the number of hand-holding required in bugzillas. There are plenty of things that can go wrong, so our fallback is still to throw up our hands and point to the documentation. https://bugs.freedesktop.org/show_bug.cgi?id=98839 Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
parent
d7ff5a8f0d
commit
3251ba2af0
7 changed files with 606 additions and 2 deletions
|
|
@ -10,6 +10,7 @@
|
|||
- @subpage t440_support
|
||||
- @subpage touchpad_jumping_cursor
|
||||
- @subpage absolute_coordinate_ranges
|
||||
- @subpage touchpad_jitter
|
||||
|
||||
@page touchscreens Touchscreens
|
||||
|
||||
|
|
|
|||
90
doc/touchpad-jitter.dox
Normal file
90
doc/touchpad-jitter.dox
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
@page touchpad_jitter Touchpad jitter
|
||||
|
||||
Touchpad jitter describes random movement by a few pixels even when the
|
||||
user's finger is unmoving.
|
||||
|
||||
libinput has a mechanism called a **hysteresis** to avoid that jitter. When
|
||||
active, movement with in the **hysteresis margin** is discarded. If the
|
||||
movement delta is larger than the margin, the movement is passed on as
|
||||
pointer movement. This is a simplified summary, developers should
|
||||
read the implementation of the hysteresis in `src/evdev.c`.
|
||||
|
||||
libinput uses the kernel `fuzz` value to determine the size of the
|
||||
hysteresis. Users should override this with a udev hwdb entry where the
|
||||
device itself does not provide the correct value.
|
||||
|
||||
@section touchpad_jitter_fuzz_override Overriding the hysteresis margins
|
||||
|
||||
libinput provides the debugging tool `libinput measure fuzz` to help edit or
|
||||
test a fuzz value. This tool is interactive and provides a udev hwdb entry
|
||||
that matches the device. To check if a fuzz is currently present, simply run
|
||||
without arguments or with the touchpad's device node:
|
||||
|
||||
@verbatim
|
||||
$ sudo libinput measure fuzz
|
||||
Using Synaptics TM2668-002: /dev/input/event17
|
||||
Checking udev property... not set
|
||||
Checking axes... x=16 y=16
|
||||
@endverbatim
|
||||
|
||||
In the above output, the axis fuzz is set to 16. To set a specific fuzz, run
|
||||
with the `--fuzz=<value>` argument.
|
||||
|
||||
@verbatim
|
||||
$ sudo libinput measure fuzz --fuzz=8
|
||||
@endverbatim
|
||||
|
||||
The tool will attempt to construct a hwdb file that matches your touchpad
|
||||
device. Follow the printed prompts.
|
||||
|
||||
In the ideal case, the tool will provide you with a file that can be
|
||||
submitted to the systemd repo for inclusion.
|
||||
|
||||
However, hwdb entry creation is difficult to automate and it's likely
|
||||
that the tools fails in doing so, especially if an existing entry is already
|
||||
present.
|
||||
|
||||
Below is the outline of what a user needs to do to override a device's fuzz
|
||||
value in case the `libinput measure fuzz` tool fails.
|
||||
|
||||
Check with `udevadm info /sys/class/input/eventX` (replace your device node
|
||||
number) whether an existing hwdb override exists. If the `EVDEV_ABS_`
|
||||
properties are present, the hwdb overried exists. Find the file that
|
||||
contains that entry, most likely in `/etc/udev/hwdb.d` or
|
||||
`/usr/lib/udev/hwdb.d`.
|
||||
|
||||
The content of the property is a set of values in the format
|
||||
`EVDEV_ABS_00=min:max:resolution:fuzz`. You need to set the `fuzz` part,
|
||||
leaving the remainder of the property as-is. Values may be empty, e.g. a
|
||||
property that only sets resolution and fuzz reads as `EVDEV_ABS_00=::32:8`.
|
||||
|
||||
If no properties exist, your hwdb.entry should look approximately like this:
|
||||
@verbatim
|
||||
evdev:name:Synaptics TM2668-002:dmi:*:svnLENOVO*:pvrThinkPadT440s*:
|
||||
EVDEV_ABS_00=:::8
|
||||
EVDEV_ABS_01=:::8
|
||||
EVDEV_ABS_35=:::8
|
||||
EVDEV_ABS_36=:::8
|
||||
@endverbatim
|
||||
|
||||
Substitute the `name` field with the device name (see the output of
|
||||
`libinput measure fuzz` and the DMI match content with your hardware. See
|
||||
@ref hwdb_modifying for details.
|
||||
|
||||
Once the hwdb entry has been modified, added, or created, @ref
|
||||
hwdb_reloading "reload the hwdb". Once reloaded, @ref libinput-record
|
||||
"libinput record" should show the new fuzz value for the axes.
|
||||
|
||||
Restart the host and libinput should pick up the revised fuzz values.
|
||||
|
||||
@section kernel_fuzz Kernel fuzz
|
||||
|
||||
A fuzz set on an absolute axis in the kernel causes the kernel to apply
|
||||
hysteresis-like behavior to the axis. Unfortunately, this behavior leads to
|
||||
inconsistent deltas. To avoid this, libinput sets the kernel fuzz on the
|
||||
device to 0 to disable this kernel behavior but remembers what the fuzz was
|
||||
on startup. The fuzz is stored in the `LIBINPUT_FUZZ_XX` udev property, on
|
||||
startup libinput will check that property as well as the axis itself.
|
||||
|
||||
*/
|
||||
10
meson.build
10
meson.build
|
|
@ -322,6 +322,7 @@ if get_option('documentation')
|
|||
meson.source_root() + '/doc/tools.dox',
|
||||
meson.source_root() + '/doc/touchpad-jumping-cursors.dox',
|
||||
meson.source_root() + '/doc/touchpad-pressure.dox',
|
||||
meson.source_root() + '/doc/touchpad-jitter.dox',
|
||||
meson.source_root() + '/doc/touchpads.dox',
|
||||
meson.source_root() + '/doc/trackpoints.dox',
|
||||
meson.source_root() + '/doc/what-is-libinput.dox',
|
||||
|
|
@ -450,6 +451,15 @@ configure_file(input : 'tools/libinput-measure.man',
|
|||
install_dir : join_paths(get_option('mandir'), 'man1')
|
||||
)
|
||||
|
||||
install_data('tools/libinput-measure-fuzz',
|
||||
install_dir : libinput_tool_path)
|
||||
configure_file(input : 'tools/libinput-measure-fuzz.man',
|
||||
output : 'libinput-measure-fuzz.1',
|
||||
configuration : man_config,
|
||||
install : true,
|
||||
install_dir : join_paths(get_option('mandir'), 'man1')
|
||||
)
|
||||
|
||||
install_data('tools/libinput-measure-touchpad-tap',
|
||||
install_dir : libinput_tool_path)
|
||||
configure_file(input : 'tools/libinput-measure-touchpad-tap.man',
|
||||
|
|
|
|||
|
|
@ -192,7 +192,10 @@ tp_detect_wobbling(struct tp_dispatch *tp,
|
|||
t->hysteresis.x_motion_history |= (1 << 2);
|
||||
if (t->hysteresis.x_motion_history == r_l_r) {
|
||||
tp->hysteresis.enabled = true;
|
||||
evdev_log_debug(tp->device, "hysteresis enabled\n");
|
||||
evdev_log_debug(tp->device,
|
||||
"hysteresis enabled. "
|
||||
"See %stouchpad_jitter.html for details\n",
|
||||
HTTP_DOC_LINK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3127,7 +3130,10 @@ tp_init_hysteresis(struct tp_dispatch *tp)
|
|||
tp->hysteresis.margin.y = ymargin;
|
||||
tp->hysteresis.enabled = (ax->fuzz || ay->fuzz);
|
||||
if (tp->hysteresis.enabled)
|
||||
evdev_log_debug(tp->device, "hysteresis enabled\n");
|
||||
evdev_log_debug(tp->device,
|
||||
"hysteresis enabled. "
|
||||
"See %stouchpad_jitter.html for details\n",
|
||||
HTTP_DOC_LINK);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
464
tools/libinput-measure-fuzz
Executable file
464
tools/libinput-measure-fuzz
Executable file
|
|
@ -0,0 +1,464 @@
|
|||
#!/usr/bin/env python3
|
||||
# vim: set expandtab shiftwidth=4:
|
||||
# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
|
||||
#
|
||||
# Copyright © 2018 Red Hat, Inc.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import subprocess
|
||||
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)
|
||||
|
||||
|
||||
DEFAULT_HWDB_FILE = '/usr/lib/udev/hwdb.d/60-evdev.hwdb'
|
||||
OVERRIDE_HWDB_FILE = '/etc/udev/hwdb.d/99-touchpad-fuzz-override.hwdb'
|
||||
|
||||
|
||||
class tcolors:
|
||||
GREEN = '\033[92m'
|
||||
RED = '\033[91m'
|
||||
BOLD = '\033[1m'
|
||||
NORMAL = '\033[0m'
|
||||
|
||||
|
||||
def print_bold(msg, **kwargs):
|
||||
print(tcolors.BOLD + msg + tcolors.NORMAL, **kwargs)
|
||||
|
||||
|
||||
def print_green(msg, **kwargs):
|
||||
print(tcolors.BOLD + tcolors.GREEN + msg + tcolors.NORMAL, **kwargs)
|
||||
|
||||
|
||||
def print_red(msg, **kwargs):
|
||||
print(tcolors.BOLD + tcolors.RED + msg + tcolors.NORMAL, **kwargs)
|
||||
|
||||
|
||||
class InvalidConfigurationError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidDeviceError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Device(object):
|
||||
def __init__(self, path):
|
||||
if path is None:
|
||||
self.path = self.find_touch_device()
|
||||
else:
|
||||
self.path = path
|
||||
|
||||
self.device = evdev.InputDevice(self.path)
|
||||
self.name = self.device.name
|
||||
context = pyudev.Context()
|
||||
self.udev_device = pyudev.Devices.from_device_file(context, self.path)
|
||||
|
||||
def find_touch_device(self):
|
||||
context = pyudev.Context()
|
||||
for device in context.list_devices(subsystem='input'):
|
||||
if not device.get('ID_INPUT_TOUCHPAD', 0):
|
||||
continue
|
||||
|
||||
if not device.device_node or \
|
||||
not device.device_node.startswith('/dev/input/event'):
|
||||
continue
|
||||
|
||||
return device.device_node
|
||||
|
||||
print('Unable to find a touch device.', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
def check_property(self):
|
||||
'''Return a tuple of (xfuzz, yfuzz) with the fuzz as set in the libinput
|
||||
property. Returns None if the property doesn't exist'''
|
||||
|
||||
axes = {
|
||||
0x00: self.udev_device.get('LIBINPUT_FUZZ_00'),
|
||||
0x01: self.udev_device.get('LIBINPUT_FUZZ_01'),
|
||||
0x35: self.udev_device.get('LIBINPUT_FUZZ_35'),
|
||||
0x36: self.udev_device.get('LIBINPUT_FUZZ_36'),
|
||||
}
|
||||
|
||||
if axes[0x35] is not None:
|
||||
if axes[0x35] != axes[0x00]:
|
||||
raise InvalidConfigurationError('fuzz for ABS_X differs from ABS_MT_POSITION_X')
|
||||
|
||||
if axes[0x36] is not None:
|
||||
if axes[0x36] != axes[0x01]:
|
||||
raise InvalidConfigurationError('fuzz for ABS_Y differs from ABS_MT_POSITION_Y')
|
||||
|
||||
xfuzz = axes[0x35] or axes[0x00]
|
||||
yfuzz = axes[0x36] or axes[0x01]
|
||||
|
||||
if xfuzz is None and yfuzz is None:
|
||||
return None
|
||||
|
||||
if ((xfuzz is not None and yfuzz is None) or
|
||||
(xfuzz is None and yfuzz is not None)):
|
||||
raise InvalidConfigurationError('fuzz should be set for both axes')
|
||||
|
||||
return (xfuzz, yfuzz)
|
||||
|
||||
def check_axis(self):
|
||||
'''
|
||||
Returns a duple of (xfuzz, yfuzz) with the fuzz as set on the device
|
||||
axis. Returns None if no fuzz is set.
|
||||
'''
|
||||
# capabilities returns a dict with the EV_* codes as key,
|
||||
# each of which is a list of tuples of (code, AbsInfo)
|
||||
#
|
||||
# Get the abs list first (or empty list if missing),
|
||||
# then extract the touch major absinfo from that
|
||||
caps = self.device.capabilities(absinfo=True).get(
|
||||
evdev.ecodes.EV_ABS, []
|
||||
)
|
||||
codes = [cap[0] for cap in caps]
|
||||
|
||||
if evdev.ecodes.ABS_X not in codes or evdev.ecodes.ABS_Y not in codes:
|
||||
raise InvalidDeviceError('device does not have x/y axes')
|
||||
|
||||
if (evdev.ecodes.ABS_MT_POSITION_X in codes) != (evdev.ecodes.ABS_MT_POSITION_Y in codes):
|
||||
raise InvalidDeviceError('device does not have both multitouch axes')
|
||||
|
||||
axes = {
|
||||
0x00: None,
|
||||
0x01: None,
|
||||
0x35: None,
|
||||
0x36: None,
|
||||
}
|
||||
|
||||
for c in caps:
|
||||
if c[0] in axes.keys():
|
||||
axes[c[0]] = c[1].fuzz
|
||||
|
||||
xfuzz = axes[0x35] or axes[0x00]
|
||||
yfuzz = axes[0x36] or axes[0x01]
|
||||
|
||||
if xfuzz is None and yfuzz is None:
|
||||
return None
|
||||
|
||||
return (xfuzz, yfuzz)
|
||||
|
||||
|
||||
def print_fuzz(what, fuzz):
|
||||
print(' Checking {}... '.format(what), end='')
|
||||
if fuzz is None:
|
||||
print('not set')
|
||||
elif fuzz == (0, 0):
|
||||
print('is zero')
|
||||
else:
|
||||
print('x={} y={}'.format(*fuzz))
|
||||
|
||||
|
||||
def handle_existing_entry(device, fuzz):
|
||||
# This is getting messy because we don't really know where the entry
|
||||
# could be or how the match rule looks like. So we just check the
|
||||
# default location only.
|
||||
# For the match comparison, we search for the property value in the
|
||||
# file. If there is more than one entry that uses the same
|
||||
# overrides this will generate false positives.
|
||||
# If the lines aren't in the same order in the file, it'll be a false
|
||||
# negative.
|
||||
overrides = {
|
||||
0x00: device.udev_device.get('EVDEV_ABS_00'),
|
||||
0x01: device.udev_device.get('EVDEV_ABS_01'),
|
||||
0x35: device.udev_device.get('EVDEV_ABS_35'),
|
||||
0x36: device.udev_device.get('EVDEV_ABS_36'),
|
||||
}
|
||||
|
||||
has_existing_rules = False
|
||||
for key, value in overrides.items():
|
||||
if value is not None:
|
||||
has_existing_rules = True
|
||||
break
|
||||
if not has_existing_rules:
|
||||
return False
|
||||
|
||||
print_red('Error! ', end='')
|
||||
print('This device already has axis overrides defined')
|
||||
print('')
|
||||
print_bold('Searching for existing override...')
|
||||
|
||||
# Construct a template that looks like a hwdb entry (values only) from
|
||||
# the udev property values
|
||||
template = [' EVDEV_ABS_00={}'.format(overrides[0x00]),
|
||||
' EVDEV_ABS_01={}'.format(overrides[0x01])]
|
||||
if overrides[0x35] is not None:
|
||||
template += [' EVDEV_ABS_35={}'.format(overrides[0x35]),
|
||||
' EVDEV_ABS_36={}'.format(overrides[0x36])]
|
||||
|
||||
found = False
|
||||
print('Checking in {}... '.format(OVERRIDE_HWDB_FILE), end='')
|
||||
entry, prefix, lineno = check_file_for_lines(OVERRIDE_HWDB_FILE, template)
|
||||
if entry is not None:
|
||||
print_green('found')
|
||||
print('The existing hwdb entry can be overwritten');
|
||||
return False
|
||||
else:
|
||||
print_red('not found')
|
||||
print('Checking in {}... '.format(DEFAULT_HWDB_FILE, template), end='')
|
||||
entry, prefix, lineno = check_file_for_lines(DEFAULT_HWDB_FILE, template)
|
||||
if entry is not None:
|
||||
print_green('found')
|
||||
else:
|
||||
print_red('not found')
|
||||
print('The device has a hwdb override defined but it\'s not where I expected it to be.');
|
||||
print('Please look at the libinput documentation for more details.')
|
||||
print('Exiting now.')
|
||||
return True
|
||||
|
||||
print_bold('Probable entry for this device found in line {}:'.format(lineno))
|
||||
print('\n'.join(prefix + entry))
|
||||
print('')
|
||||
|
||||
print_bold('Suggested new entry for this device:')
|
||||
new_entry = []
|
||||
for i in range(0, len(template)):
|
||||
parts = entry[i].split(':')
|
||||
while len(parts) < 4:
|
||||
parts.append('')
|
||||
parts[3] = str(fuzz)
|
||||
new_entry.append(':'.join(parts))
|
||||
print('\n'.join(prefix + new_entry))
|
||||
print('')
|
||||
|
||||
# Not going to overwrite the 60-evdev.hwdb entry with this program, too
|
||||
# risky. And it may not be our device match anyway.
|
||||
print_bold('You must now:')
|
||||
print('\n'.join((
|
||||
'1. Check the above suggestion for sanity. Does it match your device?',
|
||||
'2. Open {} and amend the existing entry'.format(DEFAULT_HWDB_FILE),
|
||||
' as recommended above',
|
||||
'',
|
||||
' The property format is:',
|
||||
' EVDEV_ABS_00=min:max:resolution:fuzz',
|
||||
'',
|
||||
' Leave the entry as-is and only add or amend the fuzz value.',
|
||||
' A non-existent value can be skipped, e.g. this entry sets the ',
|
||||
' resolution to 32 and the fuzz to 8',
|
||||
' EVDEV_ABS_00=::32:8',
|
||||
'',
|
||||
'3. Save the edited file',
|
||||
'4. Say Y to the next prompt')))
|
||||
|
||||
cont = input('Continue? [Y/n] ')
|
||||
if cont == 'n':
|
||||
raise KeyboardInterrupt
|
||||
|
||||
if test_hwdb_entry(device, fuzz):
|
||||
print_bold('Please test the new fuzz setting by restarting libinput')
|
||||
print_bold('Then submit a pull request for this hwdb entry change to '
|
||||
'to systemd at http://github.com/systemd/systemd')
|
||||
else:
|
||||
print_bold('The new fuzz setting did not take effect.')
|
||||
print_bold('Did you edit the correct file?')
|
||||
print('Please look at the libinput documentation for more details.')
|
||||
print('Exiting now.')
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def reload_and_trigger_udev(device):
|
||||
import time
|
||||
|
||||
print('Running udevadm hwdb --update')
|
||||
subprocess.run(['udevadm', 'hwdb', '--update'], check=True)
|
||||
syspath = device.path.replace('/dev/input/', '/sys/class/input/')
|
||||
time.sleep(1)
|
||||
print('Running udevadm trigger {}'.format(syspath))
|
||||
subprocess.run(['udevadm', 'trigger', syspath], check=True)
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def test_hwdb_entry(device, fuzz):
|
||||
reload_and_trigger_udev(device)
|
||||
print_bold('Testing... ', end='')
|
||||
|
||||
d = Device(device.path)
|
||||
f = d.check_axis()
|
||||
if fuzz == f[0] and fuzz == f[1]:
|
||||
print_green('Success')
|
||||
return True
|
||||
else:
|
||||
print_red('Error')
|
||||
return False
|
||||
|
||||
|
||||
def check_file_for_lines(path, template):
|
||||
'''
|
||||
Checks file at path for the lines given in template. If found, the
|
||||
return value is a tuple of the matching lines and the prefix (i.e. the
|
||||
two lines before the matching lines)
|
||||
'''
|
||||
try:
|
||||
lines = [l[:-1] for l in open(path).readlines()]
|
||||
idx = -1
|
||||
try:
|
||||
while idx < len(lines) - 1:
|
||||
idx += 1
|
||||
line = lines[idx]
|
||||
if not line.startswith(' EVDEV_ABS_00'):
|
||||
continue
|
||||
if lines[idx:idx + len(template)] != template:
|
||||
continue
|
||||
|
||||
return (lines[idx:idx + len(template)], lines[idx - 2:idx], idx)
|
||||
|
||||
except IndexError:
|
||||
pass
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
return (None, None, None)
|
||||
|
||||
|
||||
def write_udev_rule(device, fuzz):
|
||||
'''Write out a udev rule that may match the device, run udevadm trigger and
|
||||
check if the udev rule worked. Of course, there's plenty to go wrong...
|
||||
'''
|
||||
print('')
|
||||
print_bold('Guessing a udev rule to overwrite the fuzz')
|
||||
|
||||
# Some devices match better on pvr, others on pn, so we get to try both. yay
|
||||
modalias = open('/sys/class/dmi/id/modalias').readlines()[0]
|
||||
ms = modalias.split(':')
|
||||
svn, pn, pvr = None, None, None
|
||||
for m in ms:
|
||||
if m.startswith('svn'):
|
||||
svn = m
|
||||
elif m.startswith('pn'):
|
||||
pn = m
|
||||
elif m.startswith('pvr'):
|
||||
pvr = m
|
||||
|
||||
# Let's print out both to inform and/or confuse the user
|
||||
template = '\n'.join(('# {} {}',
|
||||
'evdev:name:{}:dmi:*:{}*:{}*:',
|
||||
' EVDEV_ABS_00=:::{}',
|
||||
' EVDEV_ABS_01=:::{}',
|
||||
' EVDEV_ABS_35=:::{}',
|
||||
' EVDEV_ABS_36=:::{}',
|
||||
''))
|
||||
rule1 = template.format(svn[3:], device.name, device.name, svn, pvr, fuzz, fuzz, fuzz, fuzz)
|
||||
rule2 = template.format(svn[3:], device.name, device.name, svn, pn, fuzz, fuzz, fuzz, fuzz)
|
||||
|
||||
print('Full modalias is: {}'.format(modalias))
|
||||
print()
|
||||
print_bold('Suggested udev rule, option 1:')
|
||||
print(rule1)
|
||||
print()
|
||||
print_bold('Suggested udev rule, option 2:')
|
||||
print(rule2)
|
||||
print('')
|
||||
|
||||
# The weird hwdb matching behavior means we match on the least specific
|
||||
# rule (i.e. most wildcards) first although that was supposed to be fixed in
|
||||
# systemd 3a04b789c6f1.
|
||||
# Our rule uses dmi strings and will be more specific than what 60-evdev.hwdb
|
||||
# already has. So we basically throw up our hands because we can't do anything
|
||||
# then.
|
||||
if handle_existing_entry(device, fuzz):
|
||||
return
|
||||
|
||||
while True:
|
||||
print_bold('Wich rule do you want to to test? 1 or 2? ', end='')
|
||||
yesno = input('Ctrl+C to exit ')
|
||||
|
||||
if yesno == '1':
|
||||
rule = rule1
|
||||
break
|
||||
elif yesno == '2':
|
||||
rule = rule2
|
||||
break
|
||||
|
||||
fname = OVERRIDE_HWDB_FILE
|
||||
try:
|
||||
fd = open(fname, 'x')
|
||||
except FileExistsError as e:
|
||||
yesno = input('File {} exists, overwrite? [Y/n] '.format(fname))
|
||||
if yesno.lower == 'n':
|
||||
return
|
||||
|
||||
fd = open(fname, 'w')
|
||||
|
||||
fd.write('# File generated by libinput measure fuzz\n\n')
|
||||
fd.write(rule)
|
||||
fd.close()
|
||||
|
||||
if test_hwdb_entry(device, fuzz):
|
||||
print('Your hwdb override file is in {}'.format(fname))
|
||||
print_bold('Please test the new fuzz setting by restarting libinput')
|
||||
print_bold('Then submit a pull request for this hwdb entry to '
|
||||
'systemd at http://github.com/systemd/systemd')
|
||||
else:
|
||||
print('The hwdb entry failed to apply to the device.')
|
||||
print('Removing hwdb file again.')
|
||||
os.remove(fname)
|
||||
reload_and_trigger_udev(device)
|
||||
print_bold('What now?')
|
||||
print('1. Re-run this program and try the other suggested udev rule. If that fails,')
|
||||
print('2. File a bug with the suggested udev rule at http://github.com/systemd/systemd')
|
||||
|
||||
|
||||
def main(args):
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Print fuzz settings'
|
||||
)
|
||||
parser.add_argument('path', metavar='/dev/input/event0',
|
||||
nargs='?', type=str, help='Path to device (optional)')
|
||||
parser.add_argument('--fuzz', type=int, help='Suggsted fuzz')
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
device = Device(args.path)
|
||||
print_bold('Using {}: {}'.format(device.name, device.path))
|
||||
|
||||
fuzz = device.check_property()
|
||||
print_fuzz('udev property', fuzz)
|
||||
|
||||
fuzz = device.check_axis()
|
||||
print_fuzz('axes', fuzz)
|
||||
|
||||
userfuzz = args.fuzz
|
||||
if userfuzz is not None:
|
||||
write_udev_rule(device, userfuzz)
|
||||
|
||||
except PermissionError as e:
|
||||
print('Permission denied, please re-run as root')
|
||||
except InvalidConfigurationError as e:
|
||||
print('Error: {}'.format(e.message))
|
||||
except KeyboardInterrupt as e:
|
||||
print('Exited on user request')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
||||
30
tools/libinput-measure-fuzz.man
Normal file
30
tools/libinput-measure-fuzz.man
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
.TH libinput-measure-fuzz "1"
|
||||
.SH NAME
|
||||
libinput\-measure\-fuzz \- measure absolute axis fuzz
|
||||
.SH SYNOPSIS
|
||||
.B libinput measure fuzz [\-\-help] [options]
|
||||
[\fI/dev/input/event0\fI]
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
The
|
||||
.B "libinput measure fuzz"
|
||||
tool measures the fuzz for an absolute axis on a kernel device. The current
|
||||
implementation does not actually measure anything, it only prints the
|
||||
relevant information available and suggests a udev rule.
|
||||
.PP
|
||||
This is a debugging tool only, its output may change at any time. Do not
|
||||
rely on the output.
|
||||
.PP
|
||||
This tool usually needs to be run as root to have access to the
|
||||
/dev/input/eventX nodes.
|
||||
.SH OPTIONS
|
||||
If a device node is given, this tool opens that device node. Otherwise, this
|
||||
tool searches for the first node that looks like a touchpad device and
|
||||
uses that node.
|
||||
.TP 8
|
||||
.B \-\-help
|
||||
Print help
|
||||
.SH LIBINPUT
|
||||
Part of the
|
||||
.B libinput(1)
|
||||
suite
|
||||
|
|
@ -22,6 +22,9 @@ Print help
|
|||
.SH FEATURES
|
||||
Features that can be measured include
|
||||
.TP 8
|
||||
.B libinput\-measure\-fuzz(1)
|
||||
Measure touch fuzz to avoid pointer jitter
|
||||
.TP 8
|
||||
.B libinput\-measure\-touch\-size(1)
|
||||
Measure touch size and orientation
|
||||
.TP 8
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue