forked from https://github.com/Evidlo/remarkable_mouse | patches include cool mapping mode that actually does proper aspect ratio conversion and fixing it for smartcard ssh setups
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

176 lines
5.4 KiB

#!/bin/env python
# Evan Widloski - 2019-02-23
# Use reMarkable as mouse input
import argparse
import logging
import os
import sys
import struct
from getpass import getpass
import paramiko
from screeninfo import get_monitors
from pynput.mouse import Button, Controller
EV_SYNC = 0
EV_KEY = 1
EV_ABS = 3
WACOM_EVCODE_PRESSURE = 24
WACOM_EVCODE_DISTANCE = 25
WACOM_EVCODE_XTILT = 26
WACOM_EVCODE_YTILT = 27
WACOM_EVCODE_XPOS = 0
WACOM_EVCODE_YPOS = 1
wacom_width = 15725
wacom_height = 20951
wacom_click_pressure_min = 1000
screen_width = 675
screen_height = 900
mouse = Controller()
logging.basicConfig(format='%(message)s')
log = logging.getLogger(__name__)
# remap wacom coordinates in various orientations
def fit(x, y, wacom_width, wacom_height, monitor, orientation):
if orientation == 'vertical':
y = wacom_height - y
elif orientation == 'right':
x, y = y, x
wacom_width, wacom_height = wacom_height, wacom_width
elif orientation == 'left':
x, y = wacom_height - y, wacom_width - x
wacom_width, wacom_height = wacom_height, wacom_width
ratio_width, ratio_height = monitor.width / wacom_width, monitor.height / wacom_height
scaling = ratio_width if ratio_width > ratio_height else ratio_height
return (
scaling * (x - (wacom_width - monitor.width / scaling) / 2),
scaling * (y - (wacom_height - monitor.height / scaling) / 2)
)
def open_eventfile(args):
"""ssh to reMarkable and open event0"""
if args.key is not None:
password = None
try:
pkey = paramiko.RSAKey.from_private_key_file(os.path.expanduser(args.key))
except paramiko.ssh_exception.PasswordRequiredException:
passphrase = getpass(
"Enter passphrase for key '{}': ".format(os.path.expanduser(args.key))
)
pkey = paramiko.RSAKey.from_private_key_file(
os.path.expanduser(args.key),
password=passphrase
)
else:
password = getpass(
"Password for '{}': ".format(args.address)
)
pkey = None
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(
args.address,
username='root',
password=password,
pkey=pkey,
look_for_keys=False
)
print("Connected to {}".format(args.address))
# Start reading events
_, stdout, _ = client.exec_command('cat /dev/input/event0')
return stdout
def read_tablet(args):
"""Loop forever and map evdev events to mouse"""
updates_since_pressure = 0
new_x = new_y = False
monitor = get_monitors()[args.monitor]
log.debug('Chose monitor: {}'.format(monitor))
stdout = open_eventfile(args)
while True:
_, _, e_type, e_code, e_value = struct.unpack('2IHHi', stdout.read(16))
if e_type == EV_ABS:
# handle x direction
if e_code == WACOM_EVCODE_YPOS:
log.debug(f'{e_value}')
x = e_value
new_x = True
updates_since_pressure += 1
# handle y direction
if e_code == WACOM_EVCODE_XPOS:
log.debug(f'\t{e_value}')
updates_since_pressure += 1
y = e_value
new_y = True
# handle draw
if e_code == WACOM_EVCODE_PRESSURE:
log.debug(f'\t\t{e_value}')
updates_since_pressure = 0
if e_value > 1000:
mouse.press(Button.left)
else:
mouse.release(Button.left)
# FIXME: bug
# sometimes the last detected pressure is above threshold
# make sure pen doesnt get stuck down
if updates_since_pressure > 8:
updates_since_pressure = 0
mouse.release(Button.left)
# only move when x and y are updated for smoother mouse
if new_x and new_y:
x, y = fit(x, y, wacom_width, wacom_height, monitor, args.orientation)
mouse.move(
monitor.x + x - mouse.position[0],
monitor.y + y - mouse.position[1]
)
count = 0
new_x = new_y = False
def main():
try:
parser = argparse.ArgumentParser(description="use reMarkable tablet as a mouse input")
parser.add_argument('--orientation', default='left', choices=['vertical', 'left', 'right'])
parser.add_argument('--monitor', default=0, type=int, metavar='NUM', help="monitor to use")
parser.add_argument('--offset', default=(0, 0), type=int, metavar=('x', 'y'), nargs=2, help="offset mapped region on monitor")
parser.add_argument('--debug', action='store_true', default=False, help="enable debug messages")
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")
args = parser.parse_args()
if args.debug:
print('Debugging enabled...')
logging.getLogger('').setLevel(logging.DEBUG)
log.setLevel(logging.DEBUG)
read_tablet(args)
except KeyboardInterrupt:
pass
if __name__ == '__main__':
main()