mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2025-12-20 12:40:05 +01:00
203 lines
6.5 KiB
Python
203 lines
6.5 KiB
Python
|
|
#!/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)
|