mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2025-12-20 03:20:05 +01:00
tools: add a tool to analyze the finger count from a recording
Given a libinput recording, print the timestamps of any finger changes, i.e. which slots are currently logically down. For example: Timestamp | Rel time | Slots | -------------------------------------- 0.000000 | +0.000s | + | | | | 0.454631 | +0.454s | | | | | 5.065401 | +4.610s | + | | | | 6.140281 | +1.074s | + | + | | | 7.410377 | +1.270s | | + | | | 7.420200 | +0.009s | | | | | 11.233108 | +3.812s | + | + | | | 11.850206 | +0.617s | | | | | 13.827740 | +1.977s | + | | | | 14.704027 | +0.876s | + | + | | | 16.050577 | +1.346s | + | | | | 16.905186 | +0.854s | | | | | This data is available with the per-slot-delta tool but the output here is more compressed, making it easier to detect stuck fingers. Pressure thresholds are not currently supported. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
parent
9925594257
commit
ff598741a9
5 changed files with 254 additions and 0 deletions
|
|
@ -110,6 +110,7 @@ intended to be run by users.
|
|||
%{_libexecdir}/libinput/libinput-replay
|
||||
%{_libexecdir}/libinput/libinput-analyze
|
||||
%{_libexecdir}/libinput/libinput-analyze-per-slot-delta
|
||||
%{_libexecdir}/libinput/libinput-analyze-touch-down-state
|
||||
%{_mandir}/man1/libinput-debug-gui.1*
|
||||
%{_mandir}/man1/libinput-debug-tablet.1*
|
||||
%{_mandir}/man1/libinput-measure.1*
|
||||
|
|
@ -125,6 +126,7 @@ intended to be run by users.
|
|||
%{_mandir}/man1/libinput-replay.1*
|
||||
%{_mandir}/man1/libinput-analyze.1*
|
||||
%{_mandir}/man1/libinput-analyze-per-slot-delta.1*
|
||||
%{_mandir}/man1/libinput-analyze-touch-down-state.1*
|
||||
|
||||
%files test
|
||||
%{_libexecdir}/libinput/libinput-test-suite
|
||||
|
|
|
|||
|
|
@ -560,6 +560,7 @@ configure_file(input : 'tools/libinput-analyze.man',
|
|||
|
||||
src_python_tools = files(
|
||||
'tools/libinput-analyze-per-slot-delta.py',
|
||||
'tools/libinput-analyze-touch-down-state.py',
|
||||
'tools/libinput-measure-fuzz.py',
|
||||
'tools/libinput-measure-touchpad-size.py',
|
||||
'tools/libinput-measure-touchpad-tap.py',
|
||||
|
|
@ -589,6 +590,7 @@ src_man = files(
|
|||
'tools/libinput-measure-touchpad-pressure.man',
|
||||
'tools/libinput-measure-touch-size.man',
|
||||
'tools/libinput-analyze-per-slot-delta.man',
|
||||
'tools/libinput-analyze-touch-down-state.man',
|
||||
)
|
||||
|
||||
foreach m : src_man
|
||||
|
|
|
|||
45
tools/libinput-analyze-touch-down-state.man
Normal file
45
tools/libinput-analyze-touch-down-state.man
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
.TH libinput-analyze-touch-down-state "1"
|
||||
.SH NAME
|
||||
libinput\-analyze\-touch\-down\-state \- analyze the touch states
|
||||
.SH SYNOPSIS
|
||||
.B libinput analyze touch-down-state [\-\-help] [options] \fIrecording.yml\fI
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
The
|
||||
.B "libinput analyze touch\-down\state"
|
||||
tool analyzes a recording made with
|
||||
.B "libinput record"
|
||||
and prints "down" state of each touch. This tool aids with the detection of stuck touches.
|
||||
.PP
|
||||
This is a debugging tool only, its output may change at any time. Do not
|
||||
rely on the output.
|
||||
.SH OPTIONS
|
||||
.TP 8
|
||||
.B \-\-help
|
||||
Print help
|
||||
.TP 8
|
||||
.B \-\-use-st
|
||||
Use the single-touch BTN_TOOL_ bits instead of the slot state. The output
|
||||
will only show the "highest" finger down at any time. For examples, where
|
||||
two fingers are down, only the second slot will be marked as down.
|
||||
.SH OUTPUT
|
||||
An example output for a two-finger alternating sequence below.
|
||||
.PP
|
||||
.nf
|
||||
.sf
|
||||
6.140281 +1062ms: x | x
|
||||
7.410377 +1257ms: | x
|
||||
7.420200 +9ms: |
|
||||
11.233108 +3812ms: x | x
|
||||
11.245721 +12ms: x | x
|
||||
11.850206 +604ms: |
|
||||
13.827740 +1977ms: x |
|
||||
13.839723 +11ms: x |
|
||||
14.704027 +864ms: x | x
|
||||
14.716691 +12ms: x | x
|
||||
.fi
|
||||
.in
|
||||
.SH LIBINPUT
|
||||
Part of the
|
||||
.B libinput(1)
|
||||
suite
|
||||
202
tools/libinput-analyze-touch-down-state.py
Executable file
202
tools/libinput-analyze-touch-down-state.py
Executable file
|
|
@ -0,0 +1,202 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8
|
||||
# vim: set expandtab shiftwidth=4:
|
||||
# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
|
||||
#
|
||||
# Copyright © 2020 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.
|
||||
#
|
||||
#
|
||||
# Prints the down/up state of each touch slot
|
||||
#
|
||||
# Input is a libinput record yaml file
|
||||
|
||||
import argparse
|
||||
import enum
|
||||
import sys
|
||||
import yaml
|
||||
import libevdev
|
||||
|
||||
|
||||
class Slot:
|
||||
class State(enum.Enum):
|
||||
NONE = "NONE"
|
||||
BEGIN = "BEGIN"
|
||||
UPDATE = "UPDATE"
|
||||
END = "END"
|
||||
|
||||
def __init__(self, index):
|
||||
self._state = Slot.State.NONE
|
||||
self.index = index
|
||||
self.used = False
|
||||
|
||||
def begin(self):
|
||||
assert self.state == Slot.State.NONE
|
||||
self.state = Slot.State.BEGIN
|
||||
|
||||
def end(self):
|
||||
assert self.state in (Slot.State.BEGIN, Slot.State.UPDATE)
|
||||
self.state = Slot.State.END
|
||||
|
||||
def sync(self):
|
||||
if self.state == Slot.State.BEGIN:
|
||||
self.state = Slot.State.UPDATE
|
||||
elif self.state == Slot.State.END:
|
||||
self.state = Slot.State.NONE
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
return self._state
|
||||
|
||||
@state.setter
|
||||
def state(self, newstate):
|
||||
assert newstate in Slot.State
|
||||
|
||||
if newstate != Slot.State.NONE:
|
||||
self.used = True
|
||||
self._state = newstate
|
||||
|
||||
@property
|
||||
def is_active(self):
|
||||
return self.state in (Slot.State.BEGIN, Slot.State.UPDATE)
|
||||
|
||||
def __str__(self):
|
||||
return "+" if self.state in (Slot.State.BEGIN, Slot.State.UPDATE) else " "
|
||||
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description="Print the state of touches over time")
|
||||
parser.add_argument(
|
||||
"--use-st", action="store_true", help="Ignore slots, use the BTN_TOOL bits"
|
||||
)
|
||||
parser.add_argument(
|
||||
"path", metavar="recording", nargs=1, help="Path to libinput-record YAML file"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
yml = yaml.safe_load(open(args.path[0]))
|
||||
device = yml["devices"][0]
|
||||
absinfo = device["evdev"]["absinfo"]
|
||||
try:
|
||||
nslots = absinfo[libevdev.EV_ABS.ABS_MT_SLOT.value][1] + 1
|
||||
except KeyError:
|
||||
args.use_st = True
|
||||
|
||||
tool_slot_map = {
|
||||
libevdev.EV_KEY.BTN_TOOL_FINGER: 0,
|
||||
libevdev.EV_KEY.BTN_TOOL_PEN: 0,
|
||||
libevdev.EV_KEY.BTN_TOOL_DOUBLETAP: 1,
|
||||
libevdev.EV_KEY.BTN_TOOL_TRIPLETAP: 2,
|
||||
libevdev.EV_KEY.BTN_TOOL_QUADTAP: 3,
|
||||
libevdev.EV_KEY.BTN_TOOL_QUINTTAP: 4,
|
||||
}
|
||||
if args.use_st:
|
||||
for bit in tool_slot_map:
|
||||
if bit.value in device["evdev"]["codes"][libevdev.EV_KEY.value]:
|
||||
nslots = max(nslots, tool_slot_map[bit])
|
||||
|
||||
slots = [Slot(i) for i in range(0, nslots)]
|
||||
# We claim the first slots are used just to make the formatting
|
||||
# more consistent
|
||||
for i in range(min(5, len(slots))):
|
||||
slots[i].used = True
|
||||
|
||||
slot = 0
|
||||
last_time = None
|
||||
last_slot_state = None
|
||||
header = "Timestamp | Rel time | Slots |"
|
||||
print(header)
|
||||
print("-" * len(header))
|
||||
|
||||
def events():
|
||||
for event in device["events"]:
|
||||
for evdev in event["evdev"]:
|
||||
yield evdev
|
||||
|
||||
for evdev in events():
|
||||
e = libevdev.InputEvent(
|
||||
code=libevdev.evbit(evdev[2], evdev[3]),
|
||||
value=evdev[4],
|
||||
sec=evdev[0],
|
||||
usec=evdev[1],
|
||||
)
|
||||
|
||||
# single-touch formatting is simpler than multitouch, it'll just
|
||||
# show the highest finger down rather than the correct output.
|
||||
if args.use_st:
|
||||
if e.code in tool_slot_map:
|
||||
slot = tool_slot_map[e.code]
|
||||
s = slots[slot]
|
||||
if e.value:
|
||||
s.begin()
|
||||
else:
|
||||
s.end()
|
||||
else:
|
||||
if e.code == libevdev.EV_ABS.ABS_MT_SLOT:
|
||||
slot = e.value
|
||||
s = slots[slot]
|
||||
# bcm5974 cycles through slot numbers, so let's say all below
|
||||
# our current slot number was used
|
||||
for sl in slots[: slot + 1]:
|
||||
sl.used = True
|
||||
else:
|
||||
s = slots[slot]
|
||||
if e.code == libevdev.EV_ABS.ABS_MT_TRACKING_ID:
|
||||
if e.value == -1:
|
||||
s.end()
|
||||
else:
|
||||
s.begin()
|
||||
elif e.code in (
|
||||
libevdev.EV_ABS.ABS_MT_POSITION_X,
|
||||
libevdev.EV_ABS.ABS_MT_POSITION_Y,
|
||||
libevdev.EV_ABS.ABS_MT_PRESSURE,
|
||||
libevdev.EV_ABS.ABS_MT_TOUCH_MAJOR,
|
||||
libevdev.EV_ABS.ABS_MT_TOUCH_MINOR,
|
||||
):
|
||||
# If recording started after touch down
|
||||
if s.state == Slot.State.NONE:
|
||||
s.begin()
|
||||
|
||||
if e.code == libevdev.EV_SYN.SYN_REPORT:
|
||||
current_slot_state = tuple(s.is_active for s in slots)
|
||||
|
||||
if current_slot_state != last_slot_state:
|
||||
if last_time is None:
|
||||
last_time = e.sec * 1000000 + e.usec
|
||||
tdelta = 0
|
||||
else:
|
||||
t = e.sec * 1000000 + e.usec
|
||||
tdelta = int((t - last_time) / 1000) / 1000
|
||||
last_time = t
|
||||
|
||||
fmt = " | ".join([str(s) for s in slots if s.used])
|
||||
print(
|
||||
"{:2d}.{:06d} | {:+7.3f}s | {}".format(e.sec, e.usec, tdelta, fmt)
|
||||
)
|
||||
|
||||
last_slot_state = current_slot_state
|
||||
|
||||
for s in slots:
|
||||
s.sync()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
|
|
@ -24,6 +24,9 @@ Features that can be analyzed include
|
|||
.TP 8
|
||||
.B libinput\-analyze\-per-slot-delta(1)
|
||||
analyze the delta per event per slot
|
||||
.TP 8
|
||||
.B libinput\-analyze\-touch-down-state(1)
|
||||
analyze the state of each touch in a recording
|
||||
.SH LIBINPUT
|
||||
Part of the
|
||||
.B libinput(1)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue