Browse Source

init commit

master
evan 5 years ago
commit
1daaa1e3fe
  1. 4
      .gitignore
  2. 22
      README.md
  3. 0
      remarkable_mouse/__init__.py
  4. 175
      remarkable_mouse/remarkable_mouse.py
  5. 1
      remarkable_mouse/version.py
  6. 29
      setup.py

4
.gitignore vendored

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
**/*.pyc
dist/
remarkable_mouse.egg-info
**/__pycache__

22
README.md

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
# remarkable_mouse
Use your reMarkable as a graphics tablet.
Special thanks to [canselcik](https://github.com/canselcik/libremarkable) and [LinusCDE](https://github.com/LinusCDE/rmWacomToMouse) for inspiration.
# Usage
``` bash
pip install remarkable-mouse
remouse
```
By default, `10.11.99.1` is used as the address. Seems to work pretty well wirelessly, too.
# Passwordless login
``` bash
ssh-keygen -f ~/.ssh/remarkable -N ''
ssh-copy-id -i ~/.ssh/remarkable.pub root@10.11.99.1
remouse --key ~/.ssh/remarkable
```

0
remarkable_mouse/__init__.py

175
remarkable_mouse/remarkable_mouse.py

@ -0,0 +1,175 @@ @@ -0,0 +1,175 @@
#!/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
)
# 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('4IHHI', stdout.read(24))
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():
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)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
pass

1
remarkable_mouse/version.py

@ -0,0 +1 @@ @@ -0,0 +1 @@
__version__ = '1'

29
setup.py

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
from setuptools import setup
from remarkable_mouse import version
setup(
name='remarkable-mouse',
version=version.__version__,
packages=['remarkable_mouse'],
author="Evan Widloski",
author_email="evan@evanw.org",
description="use reMarkable as a graphics tablet",
long_description=open('README.md').read(),
license="GPLv3",
keywords="remarkable tablet evdev",
url="https://github.com/evidlo/remarkable_mouse",
entry_points={
'console_scripts': [
'remarkable-mouse = remarkable_mouse.remarkable_mouse:main',
'remouse = remarkable_mouse.remarkable_mouse:main'
]
},
install_requires=[
'paramiko',
'screeninfo',
'pynput'
],
classifiers=[
"Programming Language :: Python :: 3",
]
)
Loading…
Cancel
Save