Skip to content

Commit

Permalink
Add GUI to position target range, and stretch mode (#55)
Browse files Browse the repository at this point in the history
* Add stretch mode

* Update readme with stretch mode

* Add rect gui

* readme update

* shuffle some code around, rename to --region

Co-authored-by: evan <evan@evanw.org>
  • Loading branch information
dmelcer9 and Evidlo committed Dec 15, 2021
1 parent 1eff905 commit 24ca4f6
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 31 deletions.
46 changes: 25 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ pip install remarkable-mouse
remouse
```

By default, `10.11.99.1` is used as the address. Seems to work pretty well wirelessly, too. By default ssh-agent is used to authenticate if it is available, otherwise you are asked for your password. Find your password in the reMarkable's [settings menu](https://remarkablewiki.com/tech/ssh).
By default, `10.11.99.1` is used as the address. Find your password in the reMarkable's [settings menu](https://remarkablewiki.com/tech/ssh). If you are on Linux using X11, you can use the `--evdev` option for pressure support.

To use the `--region` flag, you may need to install the `python3-tk` or `python3-tkinter` package with your package manager.

# Examples

Expand All @@ -34,23 +36,25 @@ remouse --key ~/.ssh/remarkable

# Usage

usage: remouse [-h] [--debug] [--key PATH] [--password PASSWORD]
[--address ADDRESS] [--mode {fit,fill}]
[--orientation {top,left,right,bottom}] [--monitor NUM]
[--threshold THRESH] [--evdev]

use reMarkable tablet as a mouse input

optional arguments:
-h, --help show this help message and exit
--debug enable debug messages
--key PATH ssh private key
--password PASSWORD ssh password
--address ADDRESS device address
--mode {fit,fill} scale setting
--orientation {top,left,right,bottom}
position of tablet buttons
--monitor NUM monitor to output to
--threshold THRESH stylus pressure threshold (default 600)
--evdev use evdev to support pen pressure (requires root,
Linux only)
```
usage: remouse [-h] [--debug] [--key PATH] [--password PASSWORD] [--address ADDRESS] [--mode {fit,fill,stretch}] [--orientation {top,left,right,bottom}] [--monitor NUM] [--region] [--threshold THRESH]
[--evdev]
use reMarkable tablet as a mouse input
optional arguments:
-h, --help show this help message and exit
--debug enable debug messages
--key PATH ssh private key
--password PASSWORD ssh password
--address ADDRESS device address
--mode {fit,fill,stretch}
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.
--orientation {top,left,right,bottom}
position of tablet buttons
--monitor NUM monitor to output to
--region Use a GUI to position the output area. Overrides --monitor
--threshold THRESH stylus pressure threshold (default 600)
--evdev use evdev to support pen pressure (requires root, Linux only)
```
67 changes: 67 additions & 0 deletions remarkable_mouse/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env python

import logging
import sys
from screeninfo import get_monitors, Monitor

logging.basicConfig(format='%(message)s')
log = logging.getLogger('remouse')

# get info of where we want to map the tablet to
def get_monitor(region, monitor_num, orientation):
if region is not None:
monitor = get_region(orientation)
else:
monitor = get_monitors()[monitor_num]

return monitor

# Pop up a window, ask the user to move the window, and then get the position of the window's contents
def get_region(orientation):
try:
import tkinter as tk
from tkinter import ttk
except ImportError:
print(
"Unable to import tkinter; please follow the instructions at https://tkdocs.com/tutorial/install.html to install it")
sys.exit(1)

window = tk.Tk()

# A bit of an ugly hack to get this function to run synchronously
# Ideally would use full async support, but this solution required minimal changes to rest of code
selected_pos = None

def on_click():
nonlocal selected_pos
selected_pos = Monitor(
window.winfo_x(),
window.winfo_y(),
window.winfo_width(),
window.winfo_height(),
name="Fake monitor from region selection"
)
window.destroy()

confirm = ttk.Button(window, text="Drag and resize this button to the desired mouse range, then click",
command=on_click)
confirm.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.E, tk.W))

window.columnconfigure(0, weight=1)
window.rowconfigure(0, weight=1)

window.attributes('-alpha', 0.5)
window.title("Remarkable Mouse")

if orientation == 'bottom' or orientation == 'top':
window.geometry("702x936")
else:
window.geometry("936x702")

window.mainloop()

if selected_pos is None:
log.debug("Window closed without giving mouse range")
sys.exit(1)

return selected_pos
22 changes: 15 additions & 7 deletions remarkable_mouse/pynput.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import struct
from screeninfo import get_monitors

from .common import get_monitor

logging.basicConfig(format='%(message)s')
log = logging.getLogger('remouse')

Expand Down Expand Up @@ -45,26 +47,32 @@ 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 = max(ratio_width, ratio_height)
scaling_x = max(ratio_width, ratio_height)
scaling_y = scaling_x
elif mode == 'fit':
scaling = min(ratio_width, ratio_height)
scaling_x = min(ratio_width, ratio_height)
scaling_y = scaling_x
elif mode == 'stretch':
scaling_x = ratio_width
scaling_y = ratio_height
else:
raise NotImplementedError

return (
scaling * (x - (wacom_width - monitor_width / scaling) / 2),
scaling * (y - (wacom_height - monitor_height / scaling) / 2)
scaling_x * (x - (wacom_width - monitor_width / scaling_x) / 2),
scaling_y * (y - (wacom_height - monitor_height / scaling_y) / 2)
)


def read_tablet(rm_inputs, *, orientation, monitor, threshold, mode):
def read_tablet(rm_inputs, *, orientation, monitor_num, region, 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 (int): monitor number to map to
monitor_num (int): monitor number to map to
region (boolean): whether to selection mapping region with region tool
threshold (int): pressure threshold
mode (str): mapping mode
"""
Expand All @@ -76,7 +84,7 @@ def read_tablet(rm_inputs, *, orientation, monitor, threshold, mode):

mouse = Controller()

monitor = get_monitors()[monitor]
monitor = get_monitor(monitor_num, region, orientation)
log.debug('Chose monitor: {}'.format(monitor))

while True:
Expand Down
11 changes: 8 additions & 3 deletions remarkable_mouse/remarkable_mouse.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,13 @@ 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'], help="scale setting")
parser.add_argument('--orientation', default='right', choices=['top', 'left', 'right', 'bottom'], help="position of charging port")
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('--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)")

Expand Down Expand Up @@ -146,7 +150,8 @@ def main():
read_tablet(
rm_inputs,
orientation=args.orientation,
monitor=args.monitor,
monitor_num=args.monitor,
region=args.region,
threshold=args.threshold,
mode=args.mode,
)
Expand Down

0 comments on commit 24ca4f6

Please sign in to comment.