From d77d486dd85c4eecb7730151ec5d42fe71c72e27 Mon Sep 17 00:00:00 2001 From: Christopher Chen Date: Wed, 28 Jun 2017 00:58:32 -0700 Subject: [PATCH 1/3] initial python app version --- README.md | 3 + python-app/main.py | 118 ++++++++++++++++++++++++++++++++++++ python-app/requirements.txt | 1 + 3 files changed, 122 insertions(+) create mode 100755 python-app/main.py create mode 100644 python-app/requirements.txt diff --git a/README.md b/README.md index 4b77261..386fe72 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,9 @@ which use the device may be confused by that and could crash. `networktablet` will display a status line for every touch/motion event it receives. +Addendum: + +The `python-app` directory contains an optional program implementing a workaround for the common problem of `networktablet` executing and connecting and receiving input events but GIMP being unable to see a device named "Network Tablet". You may need to install the dependencies with `pip3 install -r requirements.txt` Part 2: App ----------- diff --git a/python-app/main.py b/python-app/main.py new file mode 100755 index 0000000..09545dc --- /dev/null +++ b/python-app/main.py @@ -0,0 +1,118 @@ +#!/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() diff --git a/python-app/requirements.txt b/python-app/requirements.txt new file mode 100644 index 0000000..1f21f99 --- /dev/null +++ b/python-app/requirements.txt @@ -0,0 +1 @@ +pyautogui From a8444865ce97ffa2c79b3124d8bbc83d3c4eb231 Mon Sep 17 00:00:00 2001 From: Christopher Chen Date: Wed, 28 Jun 2017 01:00:53 -0700 Subject: [PATCH 2/3] handle exit --- python-app/main.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/python-app/main.py b/python-app/main.py index 09545dc..2bb704c 100755 --- a/python-app/main.py +++ b/python-app/main.py @@ -96,22 +96,25 @@ def main(): 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) + 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 movement_manager.button_states.get(0, None) == 1: + pyautogui.mouseDown(button="left") + else: + pyautogui.mouseUp(button="left") + except ValueError: + pass + except KeyboardInterrupt: + quit() if __name__ == '__main__': From 80412ecd423d6bcfb0941e8a3bc9abee47b44c3f Mon Sep 17 00:00:00 2001 From: Christopher Chen Date: Wed, 28 Jun 2017 01:12:02 -0700 Subject: [PATCH 3/3] words --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 386fe72..9c94b5f 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ which use the device may be confused by that and could crash. `networktablet` will display a status line for every touch/motion event it receives. -Addendum: +### "Network Tablet not listed" workaround ### The `python-app` directory contains an optional program implementing a workaround for the common problem of `networktablet` executing and connecting and receiving input events but GIMP being unable to see a device named "Network Tablet". You may need to install the dependencies with `pip3 install -r requirements.txt` @@ -144,3 +144,4 @@ Donate If you find GfxTablet useful, please feel free to [send a donation](https://gfxtablet.bitfire.at/donate). +