mirror of
https://github.com/rfc2822/GfxTablet
synced 2025-10-03 09:39:16 +02:00
118 lines
3.4 KiB
Python
Executable file
118 lines
3.4 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
import subprocess
|
|
import pyautogui
|
|
from collections import defaultdict
|
|
|
|
|
|
INTMAX = 65536
|
|
WIDTH, HEIGHT = pyautogui.size()
|
|
ASPECT_RATIOS = defaultdict(lambda: (16, 9))
|
|
default_ratios = [
|
|
("Galaxy Note 4", (16, 9)),
|
|
]
|
|
|
|
|
|
class Event:
|
|
|
|
def __str__(self):
|
|
return "{}({})".format(
|
|
self.__class__.__name__,
|
|
', '.join(
|
|
["{}={}".format(k, v) for k, v in self.__dict__.items()]))
|
|
|
|
|
|
class PositionEvent(Event):
|
|
"""Model of position events."""
|
|
|
|
def __init__(self, x, y, pressure):
|
|
super().__init__()
|
|
self.x = x
|
|
self.y = y
|
|
self.pressure = pressure
|
|
|
|
|
|
class ButtonEvent(Event):
|
|
"""Model of button events."""
|
|
|
|
def __init__(self, button_id, button_status):
|
|
super().__init__()
|
|
self.id = button_id
|
|
self.status = button_status
|
|
|
|
|
|
class PositionManager:
|
|
"""Model for handling relative vs. absolute movement and track button states."""
|
|
|
|
def __init__(self, device, aspect_ratio=None):
|
|
if aspect_ratio is None:
|
|
try:
|
|
r = ASPECT_RATIOS[device]
|
|
self.xscale = r[0] / r[1]
|
|
except KeyError:
|
|
raise ValueError("Sorry, that is an unsupported device.")
|
|
self.last_pos_event = None
|
|
self._deltas = [0, 0]
|
|
# only track movement delta when btn id -1 state is 1
|
|
self.button_states = defaultdict(lambda: 0)
|
|
|
|
def consume(self, event):
|
|
if isinstance(event, PositionEvent):
|
|
if self.last_pos_event is not None:
|
|
# skip non-relative movement events
|
|
if self.button_states.get(-1, None) == 1:
|
|
self._deltas[0] = (
|
|
event.x - self.last_pos_event.x) * self.xscale * HEIGHT / INTMAX
|
|
self._deltas[1] = (
|
|
event.y - self.last_pos_event.y) * HEIGHT / INTMAX
|
|
self.last_pos_event = event
|
|
elif isinstance(event, ButtonEvent):
|
|
self.button_states[event.id] = event.status
|
|
return tuple(self._deltas)
|
|
|
|
@property
|
|
def deltas(self):
|
|
return tuple(self._deltas)
|
|
|
|
|
|
def process_line(l):
|
|
if l.startswith("."):
|
|
return PositionEvent(*[int(i.split(":")[1].strip()) for i in l.split(",")])
|
|
elif l.startswith("sent button"):
|
|
ss = l.split(":")[1].strip()
|
|
return ButtonEvent(*[int(i.strip()) for i in ss.split(",")])
|
|
else:
|
|
raise ValueError("Invalid command string: {}".format(l))
|
|
|
|
|
|
def main():
|
|
# necessary to prevent pyautogui calls from causing unnecessary delays
|
|
pyautogui.PAUSE = 0
|
|
cmd = ["../driver-uinput/networktablet"]
|
|
try:
|
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
|
except FileNotFoundError:
|
|
print("ERROR: Did you build the binary?")
|
|
quit()
|
|
|
|
movement_manager = PositionManager("Galaxy Note 4")
|
|
while True:
|
|
line = p.stdout.readline().decode("utf-8")
|
|
if not line:
|
|
break
|
|
try:
|
|
e = process_line(line)
|
|
print(e)
|
|
movement_manager.consume(e)
|
|
dx, dy = movement_manager.deltas
|
|
pyautogui.moveRel(dx, dy, 0)
|
|
|
|
if movement_manager.button_states.get(0, None) == 1:
|
|
pyautogui.mouseDown(button="left")
|
|
else:
|
|
pyautogui.mouseUp(button="left")
|
|
except ValueError:
|
|
pass
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|