1a46c0ec8Sopenharmony_ci#!/usr/bin/env python3 2a46c0ec8Sopenharmony_ci# -*- coding: utf-8 3a46c0ec8Sopenharmony_ci# vim: set expandtab shiftwidth=4: 4a46c0ec8Sopenharmony_ci# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */ 5a46c0ec8Sopenharmony_ci# 6a46c0ec8Sopenharmony_ci# Copyright © 2020 Red Hat, Inc. 7a46c0ec8Sopenharmony_ci# 8a46c0ec8Sopenharmony_ci# Permission is hereby granted, free of charge, to any person obtaining a 9a46c0ec8Sopenharmony_ci# copy of this software and associated documentation files (the 'Software'), 10a46c0ec8Sopenharmony_ci# to deal in the Software without restriction, including without limitation 11a46c0ec8Sopenharmony_ci# the rights to use, copy, modify, merge, publish, distribute, sublicense, 12a46c0ec8Sopenharmony_ci# and/or sell copies of the Software, and to permit persons to whom the 13a46c0ec8Sopenharmony_ci# Software is furnished to do so, subject to the following conditions: 14a46c0ec8Sopenharmony_ci# 15a46c0ec8Sopenharmony_ci# The above copyright notice and this permission notice (including the next 16a46c0ec8Sopenharmony_ci# paragraph) shall be included in all copies or substantial portions of the 17a46c0ec8Sopenharmony_ci# Software. 18a46c0ec8Sopenharmony_ci# 19a46c0ec8Sopenharmony_ci# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20a46c0ec8Sopenharmony_ci# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21a46c0ec8Sopenharmony_ci# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22a46c0ec8Sopenharmony_ci# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23a46c0ec8Sopenharmony_ci# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24a46c0ec8Sopenharmony_ci# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25a46c0ec8Sopenharmony_ci# DEALINGS IN THE SOFTWARE. 26a46c0ec8Sopenharmony_ci# 27a46c0ec8Sopenharmony_ci# 28a46c0ec8Sopenharmony_ci# Prints the down/up state of each touch slot 29a46c0ec8Sopenharmony_ci# 30a46c0ec8Sopenharmony_ci# Input is a libinput record yaml file 31a46c0ec8Sopenharmony_ci 32a46c0ec8Sopenharmony_ciimport argparse 33a46c0ec8Sopenharmony_ciimport enum 34a46c0ec8Sopenharmony_ciimport sys 35a46c0ec8Sopenharmony_ciimport yaml 36a46c0ec8Sopenharmony_ciimport libevdev 37a46c0ec8Sopenharmony_ci 38a46c0ec8Sopenharmony_ci 39a46c0ec8Sopenharmony_ciclass Slot: 40a46c0ec8Sopenharmony_ci class State(enum.Enum): 41a46c0ec8Sopenharmony_ci NONE = "NONE" 42a46c0ec8Sopenharmony_ci BEGIN = "BEGIN" 43a46c0ec8Sopenharmony_ci UPDATE = "UPDATE" 44a46c0ec8Sopenharmony_ci END = "END" 45a46c0ec8Sopenharmony_ci 46a46c0ec8Sopenharmony_ci def __init__(self, index): 47a46c0ec8Sopenharmony_ci self._state = Slot.State.NONE 48a46c0ec8Sopenharmony_ci self.index = index 49a46c0ec8Sopenharmony_ci self.used = False 50a46c0ec8Sopenharmony_ci 51a46c0ec8Sopenharmony_ci def begin(self): 52a46c0ec8Sopenharmony_ci assert self.state == Slot.State.NONE 53a46c0ec8Sopenharmony_ci self.state = Slot.State.BEGIN 54a46c0ec8Sopenharmony_ci 55a46c0ec8Sopenharmony_ci def end(self): 56a46c0ec8Sopenharmony_ci assert self.state in (Slot.State.BEGIN, Slot.State.UPDATE) 57a46c0ec8Sopenharmony_ci self.state = Slot.State.END 58a46c0ec8Sopenharmony_ci 59a46c0ec8Sopenharmony_ci def sync(self): 60a46c0ec8Sopenharmony_ci if self.state == Slot.State.BEGIN: 61a46c0ec8Sopenharmony_ci self.state = Slot.State.UPDATE 62a46c0ec8Sopenharmony_ci elif self.state == Slot.State.END: 63a46c0ec8Sopenharmony_ci self.state = Slot.State.NONE 64a46c0ec8Sopenharmony_ci 65a46c0ec8Sopenharmony_ci @property 66a46c0ec8Sopenharmony_ci def state(self): 67a46c0ec8Sopenharmony_ci return self._state 68a46c0ec8Sopenharmony_ci 69a46c0ec8Sopenharmony_ci @state.setter 70a46c0ec8Sopenharmony_ci def state(self, newstate): 71a46c0ec8Sopenharmony_ci assert newstate in Slot.State 72a46c0ec8Sopenharmony_ci 73a46c0ec8Sopenharmony_ci if newstate != Slot.State.NONE: 74a46c0ec8Sopenharmony_ci self.used = True 75a46c0ec8Sopenharmony_ci self._state = newstate 76a46c0ec8Sopenharmony_ci 77a46c0ec8Sopenharmony_ci @property 78a46c0ec8Sopenharmony_ci def is_active(self): 79a46c0ec8Sopenharmony_ci return self.state in (Slot.State.BEGIN, Slot.State.UPDATE) 80a46c0ec8Sopenharmony_ci 81a46c0ec8Sopenharmony_ci def __str__(self): 82a46c0ec8Sopenharmony_ci return "+" if self.state in (Slot.State.BEGIN, Slot.State.UPDATE) else " " 83a46c0ec8Sopenharmony_ci 84a46c0ec8Sopenharmony_ci 85a46c0ec8Sopenharmony_cidef main(argv): 86a46c0ec8Sopenharmony_ci parser = argparse.ArgumentParser(description="Print the state of touches over time") 87a46c0ec8Sopenharmony_ci parser.add_argument( 88a46c0ec8Sopenharmony_ci "--use-st", action="store_true", help="Ignore slots, use the BTN_TOOL bits" 89a46c0ec8Sopenharmony_ci ) 90a46c0ec8Sopenharmony_ci parser.add_argument( 91a46c0ec8Sopenharmony_ci "path", metavar="recording", nargs=1, help="Path to libinput-record YAML file" 92a46c0ec8Sopenharmony_ci ) 93a46c0ec8Sopenharmony_ci args = parser.parse_args() 94a46c0ec8Sopenharmony_ci 95a46c0ec8Sopenharmony_ci yml = yaml.safe_load(open(args.path[0])) 96a46c0ec8Sopenharmony_ci device = yml["devices"][0] 97a46c0ec8Sopenharmony_ci absinfo = device["evdev"]["absinfo"] 98a46c0ec8Sopenharmony_ci try: 99a46c0ec8Sopenharmony_ci nslots = absinfo[libevdev.EV_ABS.ABS_MT_SLOT.value][1] + 1 100a46c0ec8Sopenharmony_ci except KeyError: 101a46c0ec8Sopenharmony_ci args.use_st = True 102a46c0ec8Sopenharmony_ci 103a46c0ec8Sopenharmony_ci tool_slot_map = { 104a46c0ec8Sopenharmony_ci libevdev.EV_KEY.BTN_TOOL_FINGER: 0, 105a46c0ec8Sopenharmony_ci libevdev.EV_KEY.BTN_TOOL_PEN: 0, 106a46c0ec8Sopenharmony_ci libevdev.EV_KEY.BTN_TOOL_DOUBLETAP: 1, 107a46c0ec8Sopenharmony_ci libevdev.EV_KEY.BTN_TOOL_TRIPLETAP: 2, 108a46c0ec8Sopenharmony_ci libevdev.EV_KEY.BTN_TOOL_QUADTAP: 3, 109a46c0ec8Sopenharmony_ci libevdev.EV_KEY.BTN_TOOL_QUINTTAP: 4, 110a46c0ec8Sopenharmony_ci } 111a46c0ec8Sopenharmony_ci if args.use_st: 112a46c0ec8Sopenharmony_ci for bit in tool_slot_map: 113a46c0ec8Sopenharmony_ci if bit.value in device["evdev"]["codes"][libevdev.EV_KEY.value]: 114a46c0ec8Sopenharmony_ci nslots = max(nslots, tool_slot_map[bit]) 115a46c0ec8Sopenharmony_ci 116a46c0ec8Sopenharmony_ci slots = [Slot(i) for i in range(0, nslots)] 117a46c0ec8Sopenharmony_ci # We claim the first slots are used just to make the formatting 118a46c0ec8Sopenharmony_ci # more consistent 119a46c0ec8Sopenharmony_ci for i in range(min(5, len(slots))): 120a46c0ec8Sopenharmony_ci slots[i].used = True 121a46c0ec8Sopenharmony_ci 122a46c0ec8Sopenharmony_ci slot = 0 123a46c0ec8Sopenharmony_ci last_time = None 124a46c0ec8Sopenharmony_ci last_slot_state = None 125a46c0ec8Sopenharmony_ci header = "Timestamp | Rel time | Slots |" 126a46c0ec8Sopenharmony_ci print(header) 127a46c0ec8Sopenharmony_ci print("-" * len(header)) 128a46c0ec8Sopenharmony_ci 129a46c0ec8Sopenharmony_ci def events(): 130a46c0ec8Sopenharmony_ci for event in device["events"]: 131a46c0ec8Sopenharmony_ci for evdev in event["evdev"]: 132a46c0ec8Sopenharmony_ci yield evdev 133a46c0ec8Sopenharmony_ci 134a46c0ec8Sopenharmony_ci for evdev in events(): 135a46c0ec8Sopenharmony_ci e = libevdev.InputEvent( 136a46c0ec8Sopenharmony_ci code=libevdev.evbit(evdev[2], evdev[3]), 137a46c0ec8Sopenharmony_ci value=evdev[4], 138a46c0ec8Sopenharmony_ci sec=evdev[0], 139a46c0ec8Sopenharmony_ci usec=evdev[1], 140a46c0ec8Sopenharmony_ci ) 141a46c0ec8Sopenharmony_ci 142a46c0ec8Sopenharmony_ci # single-touch formatting is simpler than multitouch, it'll just 143a46c0ec8Sopenharmony_ci # show the highest finger down rather than the correct output. 144a46c0ec8Sopenharmony_ci if args.use_st: 145a46c0ec8Sopenharmony_ci if e.code in tool_slot_map: 146a46c0ec8Sopenharmony_ci slot = tool_slot_map[e.code] 147a46c0ec8Sopenharmony_ci s = slots[slot] 148a46c0ec8Sopenharmony_ci if e.value: 149a46c0ec8Sopenharmony_ci s.begin() 150a46c0ec8Sopenharmony_ci else: 151a46c0ec8Sopenharmony_ci s.end() 152a46c0ec8Sopenharmony_ci else: 153a46c0ec8Sopenharmony_ci if e.code == libevdev.EV_ABS.ABS_MT_SLOT: 154a46c0ec8Sopenharmony_ci slot = e.value 155a46c0ec8Sopenharmony_ci s = slots[slot] 156a46c0ec8Sopenharmony_ci # bcm5974 cycles through slot numbers, so let's say all below 157a46c0ec8Sopenharmony_ci # our current slot number was used 158a46c0ec8Sopenharmony_ci for sl in slots[: slot + 1]: 159a46c0ec8Sopenharmony_ci sl.used = True 160a46c0ec8Sopenharmony_ci else: 161a46c0ec8Sopenharmony_ci s = slots[slot] 162a46c0ec8Sopenharmony_ci if e.code == libevdev.EV_ABS.ABS_MT_TRACKING_ID: 163a46c0ec8Sopenharmony_ci if e.value == -1: 164a46c0ec8Sopenharmony_ci s.end() 165a46c0ec8Sopenharmony_ci else: 166a46c0ec8Sopenharmony_ci s.begin() 167a46c0ec8Sopenharmony_ci elif e.code in ( 168a46c0ec8Sopenharmony_ci libevdev.EV_ABS.ABS_MT_POSITION_X, 169a46c0ec8Sopenharmony_ci libevdev.EV_ABS.ABS_MT_POSITION_Y, 170a46c0ec8Sopenharmony_ci libevdev.EV_ABS.ABS_MT_PRESSURE, 171a46c0ec8Sopenharmony_ci libevdev.EV_ABS.ABS_MT_TOUCH_MAJOR, 172a46c0ec8Sopenharmony_ci libevdev.EV_ABS.ABS_MT_TOUCH_MINOR, 173a46c0ec8Sopenharmony_ci ): 174a46c0ec8Sopenharmony_ci # If recording started after touch down 175a46c0ec8Sopenharmony_ci if s.state == Slot.State.NONE: 176a46c0ec8Sopenharmony_ci s.begin() 177a46c0ec8Sopenharmony_ci 178a46c0ec8Sopenharmony_ci if e.code == libevdev.EV_SYN.SYN_REPORT: 179a46c0ec8Sopenharmony_ci current_slot_state = tuple(s.is_active for s in slots) 180a46c0ec8Sopenharmony_ci 181a46c0ec8Sopenharmony_ci if current_slot_state != last_slot_state: 182a46c0ec8Sopenharmony_ci if last_time is None: 183a46c0ec8Sopenharmony_ci last_time = e.sec * 1000000 + e.usec 184a46c0ec8Sopenharmony_ci tdelta = 0 185a46c0ec8Sopenharmony_ci else: 186a46c0ec8Sopenharmony_ci t = e.sec * 1000000 + e.usec 187a46c0ec8Sopenharmony_ci tdelta = int((t - last_time) / 1000) / 1000 188a46c0ec8Sopenharmony_ci last_time = t 189a46c0ec8Sopenharmony_ci 190a46c0ec8Sopenharmony_ci fmt = " | ".join([str(s) for s in slots if s.used]) 191a46c0ec8Sopenharmony_ci print( 192a46c0ec8Sopenharmony_ci "{:2d}.{:06d} | {:+7.3f}s | {}".format(e.sec, e.usec, tdelta, fmt) 193a46c0ec8Sopenharmony_ci ) 194a46c0ec8Sopenharmony_ci 195a46c0ec8Sopenharmony_ci last_slot_state = current_slot_state 196a46c0ec8Sopenharmony_ci 197a46c0ec8Sopenharmony_ci for s in slots: 198a46c0ec8Sopenharmony_ci s.sync() 199a46c0ec8Sopenharmony_ci 200a46c0ec8Sopenharmony_ci 201a46c0ec8Sopenharmony_ciif __name__ == "__main__": 202a46c0ec8Sopenharmony_ci main(sys.argv) 203