2020-03-12 11:53:22 +10:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
# -*- coding: utf-8
|
|
|
|
|
# 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.
|
|
|
|
|
#
|
|
|
|
|
#
|
|
|
|
|
# Measures the relative motion between touch events (based on slots)
|
|
|
|
|
#
|
|
|
|
|
# Input is a libinput record yaml file
|
|
|
|
|
|
2024-12-19 09:46:11 +10:00
|
|
|
from dataclasses import dataclass, field, replace
|
2024-12-19 08:39:22 +10:00
|
|
|
from enum import Enum
|
|
|
|
|
|
2020-03-12 11:53:22 +10:00
|
|
|
import argparse
|
|
|
|
|
import math
|
|
|
|
|
import sys
|
|
|
|
|
import yaml
|
|
|
|
|
import libevdev
|
|
|
|
|
|
|
|
|
|
|
2021-01-25 13:12:25 +10:00
|
|
|
COLOR_RESET = "\x1b[0m"
|
|
|
|
|
COLOR_RED = "\x1b[6;31m"
|
2025-06-17 09:25:51 +10:00
|
|
|
COLOR_BLUE = "\x1b[6;34m"
|
|
|
|
|
COLOR_GREEN = "\x1b[6;32m"
|
2020-03-12 11:53:22 +10:00
|
|
|
|
|
|
|
|
|
2021-01-25 13:12:25 +10:00
|
|
|
class SlotFormatter:
|
|
|
|
|
def __init__(
|
2024-12-19 09:46:11 +10:00
|
|
|
self,
|
|
|
|
|
is_absolute=False,
|
|
|
|
|
resolution=None,
|
|
|
|
|
threshold=None,
|
|
|
|
|
ignore_below=None,
|
|
|
|
|
show_distance=False,
|
2025-06-17 09:25:51 +10:00
|
|
|
pressure_thresholds=(0, 0),
|
2021-01-25 13:12:25 +10:00
|
|
|
):
|
2020-04-11 11:58:15 +10:00
|
|
|
self.threshold = threshold
|
|
|
|
|
self.ignore_below = ignore_below
|
|
|
|
|
self.resolution = resolution
|
|
|
|
|
self.is_absolute = is_absolute
|
2024-12-19 09:46:11 +10:00
|
|
|
self.show_distance = show_distance
|
2025-06-17 09:25:51 +10:00
|
|
|
self.pressure_thresholds = pressure_thresholds
|
2020-04-11 11:58:15 +10:00
|
|
|
self.slots = []
|
|
|
|
|
self.have_data = False
|
|
|
|
|
self.filtered = False
|
|
|
|
|
|
2024-12-19 09:46:11 +10:00
|
|
|
self.width = 35 if show_distance else 16
|
|
|
|
|
|
2020-04-11 11:58:15 +10:00
|
|
|
def __str__(self):
|
2021-01-25 13:12:25 +10:00
|
|
|
return " | ".join(self.slots)
|
2020-04-11 11:58:15 +10:00
|
|
|
|
|
|
|
|
def format_slot(self, slot):
|
|
|
|
|
if slot.state == SlotState.BEGIN:
|
2021-01-25 13:12:25 +10:00
|
|
|
self.slots.append("+++++++".center(self.width))
|
2020-04-11 11:58:15 +10:00
|
|
|
self.have_data = True
|
|
|
|
|
elif slot.state == SlotState.END:
|
2021-01-25 13:12:25 +10:00
|
|
|
self.slots.append("-------".center(self.width))
|
2020-04-11 11:58:15 +10:00
|
|
|
self.have_data = True
|
|
|
|
|
elif slot.state == SlotState.NONE:
|
2021-01-25 13:12:25 +10:00
|
|
|
self.slots.append(("*" * (self.width - 2)).center(self.width))
|
2020-04-11 11:58:15 +10:00
|
|
|
elif not slot.dirty:
|
2021-01-25 13:12:25 +10:00
|
|
|
self.slots.append(" ".center(self.width))
|
2020-04-11 10:50:42 +10:00
|
|
|
else:
|
2020-04-11 11:58:15 +10:00
|
|
|
if self.resolution is not None:
|
2024-12-19 09:10:42 +10:00
|
|
|
delta = Point(
|
|
|
|
|
slot.delta.x / self.resolution[0],
|
|
|
|
|
slot.delta.y / self.resolution[1],
|
|
|
|
|
)
|
2024-12-19 09:46:11 +10:00
|
|
|
distx = abs(slot.position.x - slot.origin.x)
|
|
|
|
|
disty = abs(slot.position.y - slot.origin.y)
|
|
|
|
|
distance = Point(
|
|
|
|
|
distx / self.resolution[0],
|
|
|
|
|
disty / self.resolution[1],
|
|
|
|
|
)
|
2020-04-11 11:58:15 +10:00
|
|
|
else:
|
2024-12-19 09:10:42 +10:00
|
|
|
delta = Point(slot.delta.x, slot.delta.y)
|
2024-12-19 09:46:11 +10:00
|
|
|
distance = Point(
|
|
|
|
|
abs(slot.position.x - slot.origin.x),
|
|
|
|
|
abs(slot.position.y - slot.origin.y),
|
|
|
|
|
)
|
|
|
|
|
|
2024-12-19 09:10:42 +10:00
|
|
|
if delta.x != 0 and delta.y != 0:
|
|
|
|
|
t = math.atan2(delta.x, delta.y)
|
2020-04-11 11:58:15 +10:00
|
|
|
t += math.pi # in [0, 2pi] range now
|
2020-04-11 10:50:42 +10:00
|
|
|
|
2020-04-11 11:58:15 +10:00
|
|
|
if t == 0:
|
|
|
|
|
t = 0.01
|
|
|
|
|
else:
|
|
|
|
|
t = t * 180.0 / math.pi
|
|
|
|
|
|
2021-01-25 13:12:25 +10:00
|
|
|
directions = ["↖↑", "↖←", "↙←", "↙↓", "↓↘", "→↘", "→↗", "↑↗"]
|
2020-04-11 11:58:15 +10:00
|
|
|
direction = directions[int(t / 45)]
|
2024-12-19 09:10:42 +10:00
|
|
|
elif delta.y == 0:
|
|
|
|
|
if delta.x < 0:
|
2021-01-25 13:12:25 +10:00
|
|
|
direction = "←←"
|
2020-04-11 11:58:15 +10:00
|
|
|
else:
|
2021-01-25 13:12:25 +10:00
|
|
|
direction = "→→"
|
2020-04-11 11:58:15 +10:00
|
|
|
else:
|
2024-12-19 09:10:42 +10:00
|
|
|
if delta.y < 0:
|
2021-01-25 13:12:25 +10:00
|
|
|
direction = "↑↑"
|
2020-04-11 11:58:15 +10:00
|
|
|
else:
|
2021-01-25 13:12:25 +10:00
|
|
|
direction = "↓↓"
|
2020-04-11 11:58:15 +10:00
|
|
|
|
2025-06-17 09:25:51 +10:00
|
|
|
color = COLOR_RESET
|
|
|
|
|
reset = COLOR_RESET
|
2020-04-11 11:58:15 +10:00
|
|
|
if not self.is_absolute:
|
2025-06-17 09:25:51 +10:00
|
|
|
if (
|
|
|
|
|
self.pressure_thresholds[1] > 0
|
|
|
|
|
and slot.pressure > self.pressure_thresholds[1]
|
|
|
|
|
):
|
|
|
|
|
color = COLOR_GREEN
|
|
|
|
|
reset = COLOR_RESET
|
|
|
|
|
elif (
|
|
|
|
|
self.pressure_thresholds[0] > 0
|
|
|
|
|
and slot.pressure > self.pressure_thresholds[0]
|
|
|
|
|
):
|
|
|
|
|
color = COLOR_BLUE
|
|
|
|
|
reset = COLOR_RESET
|
|
|
|
|
|
2020-04-11 11:58:15 +10:00
|
|
|
if self.ignore_below is not None or self.threshold is not None:
|
2024-12-19 09:10:42 +10:00
|
|
|
dist = math.hypot(delta.x, delta.y)
|
2020-04-11 11:58:15 +10:00
|
|
|
if self.ignore_below is not None and dist < self.ignore_below:
|
2021-01-25 13:12:25 +10:00
|
|
|
self.slots.append(" ".center(self.width))
|
2020-04-11 11:58:15 +10:00
|
|
|
self.filtered = True
|
|
|
|
|
return
|
|
|
|
|
if self.threshold is not None and dist >= self.threshold:
|
|
|
|
|
color = COLOR_RED
|
|
|
|
|
reset = COLOR_RESET
|
2024-12-19 09:41:56 +10:00
|
|
|
|
2024-12-19 09:10:42 +10:00
|
|
|
if isinstance(delta.x, int) and isinstance(delta.y, int):
|
2024-12-19 09:41:56 +10:00
|
|
|
coords = f"{delta.x:+4d}/{delta.y:+4d}"
|
2020-04-11 11:58:15 +10:00
|
|
|
else:
|
2024-12-19 09:41:56 +10:00
|
|
|
coords = f"{delta.x:+3.2f}/{delta.y:+03.2f}"
|
2024-12-19 09:46:11 +10:00
|
|
|
|
|
|
|
|
if self.show_distance:
|
|
|
|
|
hypot = math.hypot(distance.x, distance.y)
|
|
|
|
|
distance = (
|
|
|
|
|
f"dist: ({distance.x:3.1f}/{distance.y:3.1f}, {hypot:3.1f})"
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
distance = ""
|
|
|
|
|
|
2024-12-19 09:41:56 +10:00
|
|
|
components = [
|
|
|
|
|
f"{direction}",
|
|
|
|
|
f"{color}",
|
|
|
|
|
coords,
|
2024-12-19 09:46:11 +10:00
|
|
|
distance,
|
2024-12-19 09:41:56 +10:00
|
|
|
f"{reset}",
|
|
|
|
|
]
|
|
|
|
|
|
2024-12-19 09:46:11 +10:00
|
|
|
string = " ".join(c for c in components if c)
|
2020-04-11 11:58:15 +10:00
|
|
|
else:
|
2024-12-19 09:10:42 +10:00
|
|
|
x, y = slot.position.x, slot.position.y
|
2020-04-11 11:58:15 +10:00
|
|
|
string = "{} {}{:4d}/{:4d}{}".format(direction, color, x, y, reset)
|
|
|
|
|
self.have_data = True
|
|
|
|
|
self.slots.append(string.ljust(self.width + len(color) + len(reset)))
|
2020-04-11 10:50:42 +10:00
|
|
|
|
|
|
|
|
|
2024-12-19 08:39:22 +10:00
|
|
|
class SlotState(Enum):
|
2020-03-12 11:53:22 +10:00
|
|
|
NONE = 0
|
|
|
|
|
BEGIN = 1
|
|
|
|
|
UPDATE = 2
|
|
|
|
|
END = 3
|
|
|
|
|
|
|
|
|
|
|
2024-12-19 09:10:42 +10:00
|
|
|
@dataclass
|
|
|
|
|
class Point:
|
|
|
|
|
x: float = 0.0
|
|
|
|
|
y: float = 0.0
|
|
|
|
|
|
|
|
|
|
|
2024-12-19 08:39:22 +10:00
|
|
|
@dataclass
|
2020-03-12 11:53:22 +10:00
|
|
|
class Slot:
|
2024-12-19 08:39:22 +10:00
|
|
|
index: int
|
|
|
|
|
state: SlotState = SlotState.NONE
|
2024-12-19 09:10:42 +10:00
|
|
|
position: Point = field(default_factory=Point)
|
|
|
|
|
delta: Point = field(default_factory=Point)
|
2024-12-19 09:46:11 +10:00
|
|
|
origin: Point = field(default_factory=Point)
|
2025-06-17 09:25:51 +10:00
|
|
|
pressure: int = 0
|
2024-12-19 08:39:22 +10:00
|
|
|
used: bool = False
|
|
|
|
|
dirty: bool = False
|
2020-04-11 13:29:38 +10:00
|
|
|
|
2020-03-12 11:53:22 +10:00
|
|
|
|
|
|
|
|
def main(argv):
|
|
|
|
|
global COLOR_RESET
|
|
|
|
|
global COLOR_RED
|
2025-06-17 09:25:51 +10:00
|
|
|
global COLOR_BLUE
|
|
|
|
|
global COLOR_GREEN
|
2020-03-12 11:53:22 +10:00
|
|
|
|
|
|
|
|
slots = []
|
|
|
|
|
xres, yres = 1, 1
|
|
|
|
|
|
2021-01-25 13:12:25 +10:00
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
|
description="Measure delta between event frames for each slot"
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
"--use-mm", action="store_true", help="Use mm instead of device deltas"
|
|
|
|
|
)
|
2024-12-19 09:46:11 +10:00
|
|
|
parser.add_argument(
|
|
|
|
|
"--show-distance",
|
|
|
|
|
action="store_true",
|
|
|
|
|
help="Show the absolute distance relative to the first position",
|
|
|
|
|
)
|
2021-01-25 13:12:25 +10:00
|
|
|
parser.add_argument(
|
|
|
|
|
"--use-st",
|
|
|
|
|
action="store_true",
|
|
|
|
|
help="Use ABS_X/ABS_Y instead of ABS_MT_POSITION_X/Y",
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
"--use-absolute",
|
|
|
|
|
action="store_true",
|
|
|
|
|
help="Use absolute coordinates, not deltas",
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
"path", metavar="recording", nargs=1, help="Path to libinput-record YAML file"
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
"--threshold",
|
|
|
|
|
type=float,
|
|
|
|
|
default=None,
|
|
|
|
|
help="Mark any delta above this threshold",
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
"--ignore-below",
|
|
|
|
|
type=float,
|
|
|
|
|
default=None,
|
|
|
|
|
help="Ignore any delta below this threshold",
|
|
|
|
|
)
|
2025-06-17 09:25:51 +10:00
|
|
|
parser.add_argument(
|
|
|
|
|
"--pressure-min",
|
|
|
|
|
type=int,
|
2025-06-23 13:47:12 +10:00
|
|
|
default=0,
|
2025-06-17 09:25:51 +10:00
|
|
|
help="Highlight touches above this pressure minimum",
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
"--pressure-max",
|
|
|
|
|
type=int,
|
2025-06-23 13:47:12 +10:00
|
|
|
default=0,
|
2025-06-17 09:25:51 +10:00
|
|
|
help="Highlight touches below this pressure maximum",
|
|
|
|
|
)
|
2020-03-12 11:53:22 +10:00
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
|
|
if not sys.stdout.isatty():
|
2021-01-25 13:12:25 +10:00
|
|
|
COLOR_RESET = ""
|
|
|
|
|
COLOR_RED = ""
|
2025-06-17 09:25:51 +10:00
|
|
|
COLOR_GREEN = ""
|
|
|
|
|
COLOR_BLUE = ""
|
2020-03-12 11:53:22 +10:00
|
|
|
|
|
|
|
|
yml = yaml.safe_load(open(args.path[0]))
|
2021-01-25 13:12:25 +10:00
|
|
|
device = yml["devices"][0]
|
|
|
|
|
absinfo = device["evdev"]["absinfo"]
|
2020-03-12 11:53:22 +10:00
|
|
|
try:
|
|
|
|
|
nslots = absinfo[libevdev.EV_ABS.ABS_MT_SLOT.value][1] + 1
|
|
|
|
|
except KeyError:
|
|
|
|
|
args.use_st = True
|
|
|
|
|
|
|
|
|
|
if args.use_st:
|
|
|
|
|
nslots = 1
|
|
|
|
|
|
2020-04-11 13:29:38 +10:00
|
|
|
slots = [Slot(i) for i in range(0, nslots)]
|
2020-04-11 15:30:12 +10:00
|
|
|
slots[0].used = True
|
2020-03-12 11:53:22 +10:00
|
|
|
|
|
|
|
|
if args.use_mm:
|
|
|
|
|
xres = 1.0 * absinfo[libevdev.EV_ABS.ABS_X.value][4]
|
|
|
|
|
yres = 1.0 * absinfo[libevdev.EV_ABS.ABS_Y.value][4]
|
|
|
|
|
if not xres or not yres:
|
|
|
|
|
print("Error: device doesn't have a resolution, cannot use mm")
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
if args.use_st:
|
|
|
|
|
print("Warning: slot coordinates on FINGER/DOUBLETAP change may be incorrect")
|
2020-03-27 10:07:46 +10:00
|
|
|
slots[0].used = True
|
2020-03-12 11:53:22 +10:00
|
|
|
|
|
|
|
|
slot = 0
|
|
|
|
|
last_time = None
|
2020-03-27 10:30:38 +10:00
|
|
|
tool_bits = {
|
|
|
|
|
libevdev.EV_KEY.BTN_TOUCH: 0,
|
|
|
|
|
libevdev.EV_KEY.BTN_TOOL_DOUBLETAP: 0,
|
|
|
|
|
libevdev.EV_KEY.BTN_TOOL_TRIPLETAP: 0,
|
|
|
|
|
libevdev.EV_KEY.BTN_TOOL_QUADTAP: 0,
|
|
|
|
|
libevdev.EV_KEY.BTN_TOOL_QUINTTAP: 0,
|
|
|
|
|
}
|
2021-03-10 09:44:35 +10:00
|
|
|
btn_state = {
|
|
|
|
|
libevdev.EV_KEY.BTN_LEFT: 0,
|
|
|
|
|
libevdev.EV_KEY.BTN_MIDDLE: 0,
|
|
|
|
|
libevdev.EV_KEY.BTN_RIGHT: 0,
|
|
|
|
|
}
|
2020-03-12 11:53:22 +10:00
|
|
|
|
2020-04-11 11:58:15 +10:00
|
|
|
nskipped_lines = 0
|
|
|
|
|
|
2021-01-25 13:12:25 +10:00
|
|
|
for event in device["events"]:
|
|
|
|
|
for evdev in event["evdev"]:
|
2020-03-12 11:53:22 +10:00
|
|
|
s = slots[slot]
|
2021-01-25 13:12:25 +10:00
|
|
|
e = libevdev.InputEvent(
|
|
|
|
|
code=libevdev.evbit(evdev[2], evdev[3]),
|
|
|
|
|
value=evdev[4],
|
|
|
|
|
sec=evdev[0],
|
|
|
|
|
usec=evdev[1],
|
|
|
|
|
)
|
2020-03-12 11:53:22 +10:00
|
|
|
|
2020-04-16 13:04:05 +10:00
|
|
|
if e.code in tool_bits:
|
|
|
|
|
tool_bits[e.code] = e.value
|
2021-03-10 09:44:35 +10:00
|
|
|
if e.code in btn_state:
|
|
|
|
|
btn_state[e.code] = e.value
|
2020-03-27 10:30:38 +10:00
|
|
|
|
2020-03-12 11:53:22 +10:00
|
|
|
if args.use_st:
|
|
|
|
|
# Note: this relies on the EV_KEY events to come in before the
|
|
|
|
|
# x/y events, otherwise the last/first event in each slot will
|
|
|
|
|
# be wrong.
|
2021-01-25 13:12:25 +10:00
|
|
|
if (
|
|
|
|
|
e.code == libevdev.EV_KEY.BTN_TOOL_FINGER
|
|
|
|
|
or e.code == libevdev.EV_KEY.BTN_TOOL_PEN
|
|
|
|
|
):
|
2020-03-12 11:53:22 +10:00
|
|
|
slot = 0
|
|
|
|
|
s = slots[slot]
|
|
|
|
|
s.dirty = True
|
|
|
|
|
if e.value:
|
|
|
|
|
s.state = SlotState.BEGIN
|
|
|
|
|
else:
|
|
|
|
|
s.state = SlotState.END
|
2020-04-16 13:04:05 +10:00
|
|
|
elif e.code == libevdev.EV_KEY.BTN_TOOL_DOUBLETAP:
|
2020-03-12 11:53:22 +10:00
|
|
|
if len(slots) > 1:
|
|
|
|
|
slot = 1
|
|
|
|
|
s = slots[slot]
|
|
|
|
|
s.dirty = True
|
|
|
|
|
if e.value:
|
|
|
|
|
s.state = SlotState.BEGIN
|
|
|
|
|
else:
|
|
|
|
|
s.state = SlotState.END
|
2025-06-17 09:25:51 +10:00
|
|
|
elif e.code == libevdev.EV_ABS.ABS_PRESSURE:
|
|
|
|
|
s.pressure = e.value
|
2020-03-12 11:53:22 +10:00
|
|
|
else:
|
2020-04-16 13:04:05 +10:00
|
|
|
if e.code == libevdev.EV_ABS.ABS_MT_SLOT:
|
2020-03-12 11:53:22 +10:00
|
|
|
slot = e.value
|
|
|
|
|
s = slots[slot]
|
|
|
|
|
s.dirty = True
|
2020-03-27 10:07:46 +10:00
|
|
|
# bcm5974 cycles through slot numbers, so let's say all below
|
|
|
|
|
# our current slot number was used
|
2021-01-25 13:12:25 +10:00
|
|
|
for sl in slots[: slot + 1]:
|
2020-03-27 10:07:46 +10:00
|
|
|
sl.used = True
|
2020-04-16 13:04:05 +10:00
|
|
|
elif e.code == libevdev.EV_ABS.ABS_MT_TRACKING_ID:
|
2020-03-12 11:53:22 +10:00
|
|
|
if e.value == -1:
|
|
|
|
|
s.state = SlotState.END
|
|
|
|
|
else:
|
|
|
|
|
s.state = SlotState.BEGIN
|
2024-12-19 09:10:42 +10:00
|
|
|
s.delta.x, s.delta.y = 0, 0
|
2020-03-12 11:53:22 +10:00
|
|
|
s.dirty = True
|
2025-06-17 09:25:51 +10:00
|
|
|
elif e.code == libevdev.EV_ABS.ABS_MT_PRESSURE:
|
|
|
|
|
s.pressure = e.value
|
2024-12-19 08:56:39 +10:00
|
|
|
|
|
|
|
|
if args.use_st:
|
|
|
|
|
axes = [libevdev.EV_ABS.ABS_X, libevdev.EV_ABS.ABS_Y]
|
|
|
|
|
else:
|
|
|
|
|
axes = [
|
|
|
|
|
libevdev.EV_ABS.ABS_MT_POSITION_X,
|
|
|
|
|
libevdev.EV_ABS.ABS_MT_POSITION_Y,
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
if e.code in axes:
|
|
|
|
|
s.dirty = True
|
|
|
|
|
|
|
|
|
|
# If recording started after touch down
|
|
|
|
|
if s.state == SlotState.NONE:
|
|
|
|
|
s.state = SlotState.BEGIN
|
2024-12-19 09:10:42 +10:00
|
|
|
s.delta = Point(0, 0)
|
2024-12-19 08:56:39 +10:00
|
|
|
|
|
|
|
|
if e.code in [
|
|
|
|
|
libevdev.EV_ABS.ABS_X,
|
|
|
|
|
libevdev.EV_ABS.ABS_MT_POSITION_X,
|
|
|
|
|
]:
|
|
|
|
|
if s.state == SlotState.UPDATE:
|
2024-12-19 09:10:42 +10:00
|
|
|
s.delta.x = e.value - s.position.x
|
|
|
|
|
s.position.x = e.value
|
2024-12-19 08:56:39 +10:00
|
|
|
elif e.code in [
|
|
|
|
|
libevdev.EV_ABS.ABS_Y,
|
|
|
|
|
libevdev.EV_ABS.ABS_MT_POSITION_Y,
|
|
|
|
|
]:
|
|
|
|
|
if s.state == SlotState.UPDATE:
|
2024-12-19 09:10:42 +10:00
|
|
|
s.delta.y = e.value - s.position.y
|
|
|
|
|
s.position.y = e.value
|
2024-12-19 08:56:39 +10:00
|
|
|
else:
|
|
|
|
|
assert False, f"Invalid axis {e.code}"
|
2020-03-12 11:53:22 +10:00
|
|
|
|
2020-04-16 13:04:05 +10:00
|
|
|
if e.code == libevdev.EV_SYN.SYN_REPORT:
|
2020-03-12 11:53:22 +10:00
|
|
|
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) # ms
|
|
|
|
|
last_time = t
|
|
|
|
|
|
2020-03-27 10:30:38 +10:00
|
|
|
tools = [
|
2021-01-25 13:12:25 +10:00
|
|
|
(libevdev.EV_KEY.BTN_TOOL_QUINTTAP, "QIN"),
|
|
|
|
|
(libevdev.EV_KEY.BTN_TOOL_QUADTAP, "QAD"),
|
|
|
|
|
(libevdev.EV_KEY.BTN_TOOL_TRIPLETAP, "TRI"),
|
|
|
|
|
(libevdev.EV_KEY.BTN_TOOL_DOUBLETAP, "DBL"),
|
|
|
|
|
(libevdev.EV_KEY.BTN_TOUCH, "TOU"),
|
2020-03-27 10:30:38 +10:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
for bit, string in tools:
|
|
|
|
|
if tool_bits[bit]:
|
|
|
|
|
tool_state = string
|
|
|
|
|
break
|
|
|
|
|
else:
|
2021-01-25 13:12:25 +10:00
|
|
|
tool_state = " "
|
|
|
|
|
|
2021-03-10 09:44:35 +10:00
|
|
|
buttons = [
|
|
|
|
|
(libevdev.EV_KEY.BTN_LEFT, "L"),
|
|
|
|
|
(libevdev.EV_KEY.BTN_MIDDLE, "M"),
|
|
|
|
|
(libevdev.EV_KEY.BTN_RIGHT, "R"),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
button_state = (
|
|
|
|
|
"".join([string for bit, string in buttons if btn_state[bit]])
|
|
|
|
|
or "."
|
|
|
|
|
)
|
|
|
|
|
|
2021-01-25 13:12:25 +10:00
|
|
|
fmt = SlotFormatter(
|
|
|
|
|
is_absolute=args.use_absolute,
|
|
|
|
|
resolution=(xres, yres) if args.use_mm else None,
|
|
|
|
|
threshold=args.threshold,
|
|
|
|
|
ignore_below=args.ignore_below,
|
2024-12-19 09:46:11 +10:00
|
|
|
show_distance=args.show_distance,
|
2025-06-17 09:25:51 +10:00
|
|
|
pressure_thresholds=(args.pressure_min, args.pressure_max),
|
2021-01-25 13:12:25 +10:00
|
|
|
)
|
2020-03-27 10:07:46 +10:00
|
|
|
for sl in [s for s in slots if s.used]:
|
2020-04-11 11:58:15 +10:00
|
|
|
fmt.format_slot(sl)
|
|
|
|
|
|
|
|
|
|
sl.dirty = False
|
2024-12-19 09:10:42 +10:00
|
|
|
sl.delta.x, sl.delta.y = 0, 0
|
2020-03-12 11:53:22 +10:00
|
|
|
if sl.state == SlotState.BEGIN:
|
2024-12-19 09:46:11 +10:00
|
|
|
sl.origin = replace(sl.position)
|
2020-03-12 11:53:22 +10:00
|
|
|
sl.state = SlotState.UPDATE
|
|
|
|
|
elif sl.state == SlotState.END:
|
|
|
|
|
sl.state = SlotState.NONE
|
|
|
|
|
|
2020-04-11 11:58:15 +10:00
|
|
|
if fmt.have_data:
|
|
|
|
|
if nskipped_lines > 0:
|
|
|
|
|
print("")
|
|
|
|
|
nskipped_lines = 0
|
2021-01-25 13:12:25 +10:00
|
|
|
print(
|
2021-03-10 09:44:35 +10:00
|
|
|
"{:2d}.{:06d} {:+5d}ms {} {} {}".format(
|
|
|
|
|
e.sec, e.usec, tdelta, tool_state, button_state, fmt
|
2021-01-25 13:12:25 +10:00
|
|
|
)
|
|
|
|
|
)
|
2020-04-11 11:58:15 +10:00
|
|
|
elif fmt.filtered:
|
|
|
|
|
nskipped_lines += 1
|
2021-01-25 13:12:25 +10:00
|
|
|
print(
|
|
|
|
|
"\r",
|
|
|
|
|
" " * 21,
|
|
|
|
|
"... {} below threshold".format(nskipped_lines),
|
|
|
|
|
flush=True,
|
|
|
|
|
end="",
|
|
|
|
|
)
|
2020-03-12 11:53:22 +10:00
|
|
|
|
|
|
|
|
|
2021-01-25 13:12:25 +10:00
|
|
|
if __name__ == "__main__":
|
2021-03-10 09:44:55 +10:00
|
|
|
try:
|
|
|
|
|
main(sys.argv)
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
pass
|