From d93addc8d8e309edce5787f4aa5d8e6f26d6d715 Mon Sep 17 00:00:00 2001 From: Marco Bazzani Date: Fri, 31 Dec 2021 15:26:25 +0100 Subject: [PATCH 1/3] initial 2 finger scroll support --- remarkable_mouse/ft5406.py | 227 +++++++++++++++++++++++++++ remarkable_mouse/pynput.py | 98 +++++++++--- remarkable_mouse/remarkable_mouse.py | 12 +- remarkable_mouse/version.py | 2 +- 4 files changed, 306 insertions(+), 33 deletions(-) create mode 100644 remarkable_mouse/ft5406.py diff --git a/remarkable_mouse/ft5406.py b/remarkable_mouse/ft5406.py new file mode 100644 index 0000000..3e8c9da --- /dev/null +++ b/remarkable_mouse/ft5406.py @@ -0,0 +1,227 @@ +import glob +import io +import os +import errno +import struct +from collections import namedtuple +import threading +import time +import select +import queue + +TOUCH_X = 0 +TOUCH_Y = 1 + +TouchEvent = namedtuple('TouchEvent', ('timestamp', 'type', 'code', 'value')) + +EV_SYN = 0 +EV_ABS = 3 + +ABS_X = 0 +ABS_Y = 1 + +ABS_MT_SLOT = 0x2f # 47 MT slot being modified +ABS_MT_POSITION_X = 0x35 # 53 Center X of multi touch position +ABS_MT_POSITION_Y = 0x36 # 54 Center Y of multi touch position +ABS_MT_TRACKING_ID = 0x39 # 57 Unique ID of initiated contact + +TS_PRESS = 1 +TS_RELEASE = 0 +TS_MOVE = 2 + +class Touch(object): + def __init__(self, slot, x, y): + self.slot = slot + + self._x = x + self._y = y + self.last_x = -1 + self.last_y = -1 + + self._id = -1 + self.events = [] + self.on_move = None + self.on_press = None + self.on_release = None + + @property + def position(self): + return (self.x, self.y) + + @property + def last_position(self): + return (self.last_x, self.last_y) + + @property + def valid(self): + return self.id > -1 + + @property + def id(self): + return self._id + + @id.setter + def id(self, value): + if value != self._id: + if value == -1 and not TS_RELEASE in self.events: + self.events.append(TS_RELEASE) + elif not TS_PRESS in self.events: + self.events.append(TS_PRESS) + + self._id = value + + @property + def x(self): + return self._x + + @x.setter + def x(self, value): + if value != self._x and not TS_MOVE in self.events: + self.events.append(TS_MOVE) + self.last_x = self._x + self._x = value + + @property + def y(self): + return self._y + + @y.setter + def y(self, value): + if value != self._y and not TS_MOVE in self.events: + self.events.append(TS_MOVE) + self.last_y = self._y + self._y = value + + def handle_events(self, fingers): + """Run outstanding press/release/move events""" + for event in self.events: + if event == TS_MOVE and callable(self.on_move): + self.on_move(event, self, fingers) + if event == TS_PRESS and callable(self.on_press): + fingers+=1 + self.on_press(event, self, fingers) + if event == TS_RELEASE and callable(self.on_release): + fingers-=1 + self.on_release(event, self, fingers) + self.events = [] + return fingers + + +class Touches(list): + @property + def valid(self): + return [touch for touch in self if touch.valid] + +class Touchscreen(object): + + TOUCHSCREEN_EVDEV_NAME = 'FT5406 memory based driver' + EVENT_FORMAT = str('2IHHi') + EVENT_SIZE = struct.calcsize(EVENT_FORMAT) + + def __init__(self, device=None, f_channel=None, f_stream=None): + self._device = self.TOUCHSCREEN_EVDEV_NAME if device is None else device + self._running = False + self._thread = None + self._f_poll = select.poll() + + if f_channel and f_stream: + self._f_device = f_channel + self._f_stream = f_stream + else: + self._f_device = io.open(self._touch_device(), 'rb', self.EVENT_SIZE) + + self._f_poll.register(self._f_device, select.POLLIN) + self.position = Touch(0, 0, 0) + self.touches = Touches([Touch(x, 0, 0) for x in range(11)]) + self._event_queue = queue.Queue() + self._touch_slot = 0 + self.fingers = 0 + + def _run(self): + self._running = True + while self._running: + self.poll() + #time.sleep(0.0001) + + def run(self): + if self._thread is not None: + return + + self._thread = threading.Thread(target=self._run) + self._thread.start() + + def stop(self): + if self._thread is None: + return + + self._running = False + self._thread.join() + self._thread = None + + @property + def _current_touch(self): + return self.touches[self._touch_slot] + + def close(self): + self._f_device.close() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_tb): + self.close() + + def __iter__(self): + pass + + def _lazy_read(self): + while self._wait_for_events(): + event = self._f_stream.read(self.EVENT_SIZE) + if not event: + break + yield event + + def _get_pending_events(self): + for event in self._lazy_read(): + (tv_sec, tv_usec, type, code, value) = struct.unpack(self.EVENT_FORMAT, event) + self._event_queue.put(TouchEvent(tv_sec + (tv_usec / 1000000), type, code, value)) + + def _wait_for_events(self, timeout=2): + return self._f_poll.poll(timeout) + + def poll(self): + self._get_pending_events() + + while not self._event_queue.empty(): + event = self._event_queue.get() + self._event_queue.task_done() + + if event.type == EV_SYN: # Sync + for touch in self.touches: + self.fingers = touch.handle_events(self.fingers) + return self.touches + + if event.type == EV_ABS: # Absolute cursor position + if event.code == ABS_MT_SLOT: + self._touch_slot = event.value + + if event.code == ABS_MT_TRACKING_ID: + self._current_touch.id = event.value + + if event.code == ABS_MT_POSITION_X: + self._current_touch.x = event.value + + if event.code == ABS_MT_POSITION_Y: + self._current_touch.y = event.value + + if event.code == ABS_X: + self.position.x = event.value + + if event.code == ABS_Y: + self.position.y = event.value + + return [] + + + def read(self): + return next(iter(self)) diff --git a/remarkable_mouse/pynput.py b/remarkable_mouse/pynput.py index b9d2e76..5d0155e 100644 --- a/remarkable_mouse/pynput.py +++ b/remarkable_mouse/pynput.py @@ -1,12 +1,17 @@ import logging import struct +import threading from screeninfo import get_monitors +from pynput.mouse import Button, Controller + +from remarkable_mouse.ft5406 import Touchscreen, TS_MOVE, TS_PRESS, TS_RELEASE -from .common import get_monitor logging.basicConfig(format='%(message)s') log = logging.getLogger('remouse') +# see https://github.com/canselcik/libremarkable/blob/master/src/input/ecodes.rs + # evtype_sync = 0 # evtype_key = 1 e_type_abs = 3 @@ -17,9 +22,10 @@ e_code_stylus_xpos = 1 e_code_stylus_ypos = 0 e_code_stylus_pressure = 24 -# evcode_finger_xpos = 53 -# evcode_finger_ypos = 54 -# evcode_finger_pressure = 58 +evcode_finger_xpos = 53 +evcode_finger_ypos = 54 +evcode_finger_pressure = 58 +evcode_finger_count = 57 # wacom digitizer dimensions wacom_width = 15725 @@ -47,66 +53,107 @@ def remap(x, y, wacom_width, wacom_height, monitor_width, ratio_width, ratio_height = monitor_width / wacom_width, monitor_height / wacom_height if mode == 'fill': - scaling_x = max(ratio_width, ratio_height) - scaling_y = scaling_x + scaling = max(ratio_width, ratio_height) elif mode == 'fit': - scaling_x = min(ratio_width, ratio_height) - scaling_y = scaling_x - elif mode == 'stretch': - scaling_x = ratio_width - scaling_y = ratio_height + scaling = min(ratio_width, ratio_height) else: raise NotImplementedError return ( - scaling_x * (x - (wacom_width - monitor_width / scaling_x) / 2), - scaling_y * (y - (wacom_height - monitor_height / scaling_y) / 2) + scaling * (x - (wacom_width - monitor_width / scaling) / 2), + scaling * (y - (wacom_height - monitor_height / scaling) / 2) ) -def read_tablet(rm_inputs, *, orientation, monitor_num, region, threshold, mode): +def read_tablet(rm_inputs, *, orientation, monitor, threshold, mode): """Loop forever and map evdev events to mouse Args: rm_inputs (dictionary of paramiko.ChannelFile): dict of pen, button and touch input streams orientation (str): tablet orientation - monitor_num (int): monitor number to map to - region (boolean): whether to selection mapping region with region tool + monitor (int): monitor number to map to threshold (int): pressure threshold mode (str): mapping mode """ - from pynput.mouse import Button, Controller + monitor = get_monitors()[monitor] + log.debug('Chose monitor: {}'.format(monitor)) + + mouse = threading.Thread(target=move_mouse, args=(rm_inputs, orientation, monitor, threshold, mode)) + gesture = threading.Thread(target=do_gesture, args=(rm_inputs, orientation, monitor, threshold, mode)) + mouse.daemon = True + gesture.daemon = True + mouse.start() + gesture.start() + mouse.join() + gesture.join() +def do_gesture(rm_inputs, orientation, monitor, threshold, mode): + mouse = Controller() + import signal + + ts = Touchscreen("pt_mt", rm_inputs['touch'].channel, rm_inputs['touch']) + + def handle_event(event, touch, fingers): + px, py = remap( + *touch.position, + wacom_width, wacom_height, + monitor.width, monitor.height, + mode, orientation + ) + lpx, lpy = remap( + *touch.last_position, + wacom_width, wacom_height, + monitor.width, monitor.height, + mode, orientation + ) + + if touch.slot == 0 and fingers == 2: + mouse.scroll(px-lpx, py-lpy) + + for touch in ts.touches: + touch.on_press = handle_event + touch.on_release = handle_event + touch.on_move = handle_event + + ts.run() + + try: + signal.pause() + except KeyboardInterrupt: + print("Stopping thread...") + ts.stop() + exit() + + + +def move_mouse(rm_inputs, orientation, monitor, threshold, mode): lifted = True new_x = new_y = False mouse = Controller() - monitor = get_monitor(monitor_num, region, orientation) - log.debug('Chose monitor: {}'.format(monitor)) - while True: _, _, e_type, e_code, e_value = struct.unpack('2IHHi', rm_inputs['pen'].read(16)) + log.debug(f'PEN: {e_type:02x} {e_code:02x} {e_value:02x} \t###\t {e_type} {e_code} {e_value}') if e_type == e_type_abs: - # handle x direction if e_code == e_code_stylus_xpos: - log.debug(e_value) + #log.debug(e_value) x = e_value new_x = True # handle y direction if e_code == e_code_stylus_ypos: - log.debug('\t{}'.format(e_value)) + #log.debug('\t{}'.format(e_value)) y = e_value new_y = True # handle draw if e_code == e_code_stylus_pressure: - log.debug('\t\t{}'.format(e_value)) + #log.debug('\t\t{}'.format(e_value)) if e_value > threshold: if lifted: log.debug('PRESS') @@ -132,3 +179,6 @@ def read_tablet(rm_inputs, *, orientation, monitor_num, region, threshold, mode) monitor.y + mapped_y - mouse.position[1] ) new_x = new_y = False + + + diff --git a/remarkable_mouse/remarkable_mouse.py b/remarkable_mouse/remarkable_mouse.py index e7958fe..7c1d1c2 100755 --- a/remarkable_mouse/remarkable_mouse.py +++ b/remarkable_mouse/remarkable_mouse.py @@ -18,6 +18,7 @@ default_key = os.path.expanduser('~/.ssh/remarkable') + def open_rm_inputs(*, address, key, password): """ Open a remote input device via SSH. @@ -112,13 +113,9 @@ def main(): parser.add_argument('--key', type=str, metavar='PATH', help="ssh private key") parser.add_argument('--password', default=None, type=str, help="ssh password") parser.add_argument('--address', default='10.11.99.1', type=str, help="device address") - parser.add_argument('--mode', default='fill', choices=['fit', 'fill', 'stretch'], help="""Scale setting. - Fit (default): take up the entire tablet, but not necessarily the entire monitor. - Fill: take up the entire monitor, but not necessarily the entire tablet. - Stretch: take up both the entire tablet and monitor, but don't maintain aspect ratio.""") - parser.add_argument('--orientation', default='right', choices=['top', 'left', 'right', 'bottom'], help="position of tablet buttons") + parser.add_argument('--mode', default='fill', choices=['fit', 'fill'], help="scale setting") + parser.add_argument('--orientation', default='right', choices=['top', 'left', 'right', 'bottom'], help="position of charging port") parser.add_argument('--monitor', default=0, type=int, metavar='NUM', help="monitor to output to") - parser.add_argument('--region', action='store_true', default=False, help="Use a GUI to position the output area. Overrides --monitor") parser.add_argument('--threshold', metavar='THRESH', default=600, type=int, help="stylus pressure threshold (default 600)") parser.add_argument('--evdev', action='store_true', default=False, help="use evdev to support pen pressure (requires root, Linux only)") @@ -150,8 +147,7 @@ def main(): read_tablet( rm_inputs, orientation=args.orientation, - monitor_num=args.monitor, - region=args.region, + monitor=args.monitor, threshold=args.threshold, mode=args.mode, ) diff --git a/remarkable_mouse/version.py b/remarkable_mouse/version.py index 98d739c..5220ac1 100644 --- a/remarkable_mouse/version.py +++ b/remarkable_mouse/version.py @@ -1 +1 @@ -__version__ = '6.0.0' +__version__ = '7.0.0' From adcf9e3af6cfc4bd5093023ac26df8f891fe5201 Mon Sep 17 00:00:00 2001 From: Marco Bazzani Date: Mon, 3 Jan 2022 00:25:12 +0100 Subject: [PATCH 2/3] Ignoring scrolling due to metacarpus rubbing on the surface while writing added support for finger move added support for finger click --- remarkable_mouse/ft5406.py | 76 ++++++++-------- remarkable_mouse/pynput.py | 126 ++++++++++++++++++++------- remarkable_mouse/remarkable_mouse.py | 2 +- 3 files changed, 135 insertions(+), 69 deletions(-) diff --git a/remarkable_mouse/ft5406.py b/remarkable_mouse/ft5406.py index 3e8c9da..f442728 100644 --- a/remarkable_mouse/ft5406.py +++ b/remarkable_mouse/ft5406.py @@ -1,13 +1,9 @@ -import glob -import io -import os -import errno import struct from collections import namedtuple import threading -import time import select import queue +from datetime import date, datetime TOUCH_X = 0 TOUCH_Y = 1 @@ -25,8 +21,8 @@ ABS_MT_POSITION_Y = 0x36 # 54 Center Y of multi touch position ABS_MT_TRACKING_ID = 0x39 # 57 Unique ID of initiated contact -TS_PRESS = 1 TS_RELEASE = 0 +TS_PRESS = 1 TS_MOVE = 2 class Touch(object): @@ -43,6 +39,9 @@ def __init__(self, slot, x, y): self.on_move = None self.on_press = None self.on_release = None + self.presstime = 0 + self.releasetime = 0 + @property def position(self): @@ -92,19 +91,20 @@ def y(self, value): self.last_y = self._y self._y = value - def handle_events(self, fingers): + def handle_events(self, touchscreen, raw_event): """Run outstanding press/release/move events""" for event in self.events: if event == TS_MOVE and callable(self.on_move): - self.on_move(event, self, fingers) + self.on_move(event, self, touchscreen, raw_event) if event == TS_PRESS and callable(self.on_press): - fingers+=1 - self.on_press(event, self, fingers) + self.presstime = datetime.now().timestamp() + touchscreen.fingers+=1 + self.on_press(event, self, touchscreen, raw_event) if event == TS_RELEASE and callable(self.on_release): - fingers-=1 - self.on_release(event, self, fingers) + self.releasetime = datetime.now().timestamp() + touchscreen.fingers-=1 + self.on_release(event, self, touchscreen, raw_event) self.events = [] - return fingers class Touches(list): @@ -113,35 +113,40 @@ def valid(self): return [touch for touch in self if touch.valid] class Touchscreen(object): - - TOUCHSCREEN_EVDEV_NAME = 'FT5406 memory based driver' EVENT_FORMAT = str('2IHHi') EVENT_SIZE = struct.calcsize(EVENT_FORMAT) - def __init__(self, device=None, f_channel=None, f_stream=None): - self._device = self.TOUCHSCREEN_EVDEV_NAME if device is None else device + + def __init__(self,f_channel, f_stream): self._running = False self._thread = None self._f_poll = select.poll() - if f_channel and f_stream: - self._f_device = f_channel - self._f_stream = f_stream - else: - self._f_device = io.open(self._touch_device(), 'rb', self.EVENT_SIZE) + self._f_device = f_channel + self._f_stream = f_stream self._f_poll.register(self._f_device, select.POLLIN) - self.position = Touch(0, 0, 0) - self.touches = Touches([Touch(x, 0, 0) for x in range(11)]) + self.touches = Touches([Touch(indx, 0, 0) for indx in range(11)]) # sometimes it sends an 11th finger self._event_queue = queue.Queue() self._touch_slot = 0 self.fingers = 0 + self.last_timestamp = {} + self.current_timestamp = {} + + def update_timestamp(self, event): + ts = (datetime.now().timestamp()) + self.last_timestamp[event] = self.current_timestamp.get(event,0) + self.current_timestamp[event] = ts + + + def get_delta_time(self, event): + return self.current_timestamp.get(event,0) - self.last_timestamp.get(event,0) def _run(self): self._running = True while self._running: self.poll() - #time.sleep(0.0001) + def run(self): if self._thread is not None: @@ -184,7 +189,9 @@ def _lazy_read(self): def _get_pending_events(self): for event in self._lazy_read(): (tv_sec, tv_usec, type, code, value) = struct.unpack(self.EVENT_FORMAT, event) - self._event_queue.put(TouchEvent(tv_sec + (tv_usec / 1000000), type, code, value)) + ts = tv_sec + (tv_usec / 1000000) + event = TouchEvent(ts, type, code, value) + self._event_queue.put(event) def _wait_for_events(self, timeout=2): return self._f_poll.poll(timeout) @@ -195,12 +202,6 @@ def poll(self): while not self._event_queue.empty(): event = self._event_queue.get() self._event_queue.task_done() - - if event.type == EV_SYN: # Sync - for touch in self.touches: - self.fingers = touch.handle_events(self.fingers) - return self.touches - if event.type == EV_ABS: # Absolute cursor position if event.code == ABS_MT_SLOT: self._touch_slot = event.value @@ -213,12 +214,11 @@ def poll(self): if event.code == ABS_MT_POSITION_Y: self._current_touch.y = event.value - - if event.code == ABS_X: - self.position.x = event.value - - if event.code == ABS_Y: - self.position.y = event.value + + if event.type == EV_SYN: # Sync + for touch in self.touches: + touch.handle_events(self,event) + return self.touches return [] diff --git a/remarkable_mouse/pynput.py b/remarkable_mouse/pynput.py index 5d0155e..3cac48b 100644 --- a/remarkable_mouse/pynput.py +++ b/remarkable_mouse/pynput.py @@ -3,9 +3,12 @@ import threading from screeninfo import get_monitors from pynput.mouse import Button, Controller +from queue import Empty, LifoQueue -from remarkable_mouse.ft5406 import Touchscreen, TS_MOVE, TS_PRESS, TS_RELEASE +from remarkable_mouse.ft5406 import TouchEvent, Touchscreen, TS_MOVE, TS_PRESS, TS_RELEASE +from datetime import datetime +MONITORS = get_monitors() logging.basicConfig(format='%(message)s') log = logging.getLogger('remouse') @@ -34,7 +37,6 @@ # finger_width = 767 # finger_height = 1023 - # remap wacom coordinates to screen coordinates def remap(x, y, wacom_width, wacom_height, monitor_width, monitor_height, mode, orientation): @@ -65,7 +67,7 @@ def remap(x, y, wacom_width, wacom_height, monitor_width, ) -def read_tablet(rm_inputs, *, orientation, monitor, threshold, mode): +def read_tablet(rm_inputs, *, orientation, monitor_idx, threshold, mode): """Loop forever and map evdev events to mouse Args: @@ -77,11 +79,12 @@ def read_tablet(rm_inputs, *, orientation, monitor, threshold, mode): mode (str): mapping mode """ - monitor = get_monitors()[monitor] + monitor = MONITORS[monitor_idx] log.debug('Chose monitor: {}'.format(monitor)) + q = LifoQueue() - mouse = threading.Thread(target=move_mouse, args=(rm_inputs, orientation, monitor, threshold, mode)) - gesture = threading.Thread(target=do_gesture, args=(rm_inputs, orientation, monitor, threshold, mode)) + mouse = threading.Thread(target=handle_touch, args=(rm_inputs, orientation, monitor, mode, q)) + gesture = threading.Thread(target=handle_pen, args=(rm_inputs, orientation, monitor, threshold, mode, q)) mouse.daemon = True gesture.daemon = True mouse.start() @@ -89,28 +92,92 @@ def read_tablet(rm_inputs, *, orientation, monitor, threshold, mode): mouse.join() gesture.join() -def do_gesture(rm_inputs, orientation, monitor, threshold, mode): +def clean_queue(q): + while not q.empty(): + try: + q.get(False) + except Empty: + continue + q.task_done() + + +def get_or_none(q): + msg = None + try: + msg = q.get(False) + q.task_done() + except Empty: + pass + clean_queue(q) # ignore old ones and keep the queue clean + return msg + +def get_current_monitor(): + global every + mouse = Controller() + for x, _monitor in enumerate(MONITORS): + if _monitor.x < mouse.position[0] < _monitor.x+_monitor.width and _monitor.y < mouse.position[1] < -_monitor.y+_monitor.height: + return _monitor + + + +def handle_touch(rm_inputs, orientation, monitor, mode, q): mouse = Controller() import signal + speed = 10 + + ts = Touchscreen(rm_inputs['touch'].channel, rm_inputs['touch']) + + def handle_event(event, touch, touchscreen: Touchscreen, raw_event: TouchEvent): + touchscreen.update_timestamp(event) + fingers = touchscreen.fingers + from_pen = get_or_none(q) # get the last one + delta_t = 10 # set high delay in case no message has been recieved + + + if from_pen: + delta_t = raw_event.timestamp-from_pen.timestamp + + if event == TS_PRESS: + touch.last_x, touch.last_y = touch.position + + if 0 < (touch.releasetime - touch.presstime) < 0.2: + mouse.press(Button.left) + mouse.release(Button.left) - ts = Touchscreen("pt_mt", rm_inputs['touch'].channel, rm_inputs['touch']) - def handle_event(event, touch, fingers): px, py = remap( - *touch.position, - wacom_width, wacom_height, - monitor.width, monitor.height, - mode, orientation - ) + *touch.position, + wacom_width, wacom_height, + monitor.width, monitor.height, + mode, orientation + ) lpx, lpy = remap( - *touch.last_position, - wacom_width, wacom_height, - monitor.width, monitor.height, - mode, orientation - ) + *touch.last_position, + wacom_width, wacom_height, + monitor.width, monitor.height, + mode, orientation + ) + + dx = px-lpx + dy = py-lpy + + dt = touchscreen.get_delta_time(event) + + if fingers == 2 and delta_t > 1: + mouse.scroll(dx, dy) + + if fingers == 1 and delta_t > 1: + mouse.move(speed*dx, speed*dy) + + log.debug( + f'{["Release","Press","Move"][event]}\t'+ + f'{px}\t{lpx}\t{py}\t{lpy}\t{fingers}\t{touch.slot}\t'+ + f'{dx}\t{dy}\t{dt}\t' + f'{mouse.position}\t{get_current_monitor()}' + ) + + - if touch.slot == 0 and fingers == 2: - mouse.scroll(px-lpx, py-lpy) for touch in ts.touches: touch.on_press = handle_event @@ -127,33 +194,32 @@ def handle_event(event, touch, fingers): exit() - -def move_mouse(rm_inputs, orientation, monitor, threshold, mode): +def handle_pen(rm_inputs, orientation, monitor, threshold, mode, q): + mouse = Controller() lifted = True new_x = new_y = False - mouse = Controller() - while True: - _, _, e_type, e_code, e_value = struct.unpack('2IHHi', rm_inputs['pen'].read(16)) - log.debug(f'PEN: {e_type:02x} {e_code:02x} {e_value:02x} \t###\t {e_type} {e_code} {e_value}') + tv_sec, tv_usec, e_type, e_code, e_value = struct.unpack('2IHHi', rm_inputs['pen'].read(16)) + q.put(TouchEvent(tv_sec + (tv_usec / 1000000), e_type, e_code, e_value)) + + _monitor = get_current_monitor() + if _monitor and _monitor != monitor: + monitor = _monitor if e_type == e_type_abs: # handle x direction if e_code == e_code_stylus_xpos: - #log.debug(e_value) x = e_value new_x = True # handle y direction if e_code == e_code_stylus_ypos: - #log.debug('\t{}'.format(e_value)) y = e_value new_y = True # handle draw if e_code == e_code_stylus_pressure: - #log.debug('\t\t{}'.format(e_value)) if e_value > threshold: if lifted: log.debug('PRESS') diff --git a/remarkable_mouse/remarkable_mouse.py b/remarkable_mouse/remarkable_mouse.py index 7c1d1c2..3a7546b 100755 --- a/remarkable_mouse/remarkable_mouse.py +++ b/remarkable_mouse/remarkable_mouse.py @@ -147,7 +147,7 @@ def main(): read_tablet( rm_inputs, orientation=args.orientation, - monitor=args.monitor, + monitor_idx=args.monitor, threshold=args.threshold, mode=args.mode, ) From f035192fcb7018c423371e13b5e03953732183eb Mon Sep 17 00:00:00 2001 From: Marco Bazzani Date: Mon, 3 Jan 2022 00:32:23 +0100 Subject: [PATCH 3/3] 3 finger drag --- remarkable_mouse/ft5406.py | 3 +++ remarkable_mouse/pynput.py | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/remarkable_mouse/ft5406.py b/remarkable_mouse/ft5406.py index f442728..7f03239 100644 --- a/remarkable_mouse/ft5406.py +++ b/remarkable_mouse/ft5406.py @@ -98,10 +98,12 @@ def handle_events(self, touchscreen, raw_event): self.on_move(event, self, touchscreen, raw_event) if event == TS_PRESS and callable(self.on_press): self.presstime = datetime.now().timestamp() + touchscreen.last_fingers = touchscreen.fingers touchscreen.fingers+=1 self.on_press(event, self, touchscreen, raw_event) if event == TS_RELEASE and callable(self.on_release): self.releasetime = datetime.now().timestamp() + touchscreen.last_fingers = touchscreen.fingers touchscreen.fingers-=1 self.on_release(event, self, touchscreen, raw_event) self.events = [] @@ -130,6 +132,7 @@ def __init__(self,f_channel, f_stream): self._event_queue = queue.Queue() self._touch_slot = 0 self.fingers = 0 + self.last_fingers = 0 self.last_timestamp = {} self.current_timestamp = {} diff --git a/remarkable_mouse/pynput.py b/remarkable_mouse/pynput.py index 3cac48b..4d63f45 100644 --- a/remarkable_mouse/pynput.py +++ b/remarkable_mouse/pynput.py @@ -130,6 +130,7 @@ def handle_touch(rm_inputs, orientation, monitor, mode, q): def handle_event(event, touch, touchscreen: Touchscreen, raw_event: TouchEvent): touchscreen.update_timestamp(event) fingers = touchscreen.fingers + last_fingers = touchscreen.last_fingers from_pen = get_or_none(q) # get the last one delta_t = 10 # set high delay in case no message has been recieved @@ -162,13 +163,24 @@ def handle_event(event, touch, touchscreen: Touchscreen, raw_event: TouchEvent): dy = py-lpy dt = touchscreen.get_delta_time(event) + + if delta_t < 1: + return - if fingers == 2 and delta_t > 1: + if fingers == 2: mouse.scroll(dx, dy) - if fingers == 1 and delta_t > 1: + if fingers == 1: + mouse.move(speed*dx, speed*dy) + + if fingers == 3 and last_fingers == 2: + mouse.press(Button.left) mouse.move(speed*dx, speed*dy) + if last_fingers == 3: + mouse.move(speed*dx, speed*dy) + mouse.release(Button.left) + log.debug( f'{["Release","Press","Move"][event]}\t'+ f'{px}\t{lpx}\t{py}\t{lpy}\t{fingers}\t{touch.slot}\t'+