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.
 
 

180 lines
4.9 KiB

#!/usr/bin/env python
import logging
import sys
from screeninfo import get_monitors, Monitor
from .codes import codes, types
logging.basicConfig(format='%(message)s')
log = logging.getLogger('remouse')
wacom_max_y = 15725
wacom_max_x = 20967
def get_monitor(region, monitor_num, orientation):
""" Get info of where we want to map the tablet to
Args:
region (boolean): whether to prompt the user to select a region
monitor_num (int): index of monitor to use. Implies region=False
orientation (str): Location of tablet charging port.
('top', 'bottom', 'left', 'right')
Returns:
screeninfo.Monitor
(width, height): total size of all screens put together
"""
# compute size of box encompassing all screens
max_x, max_y = 0, 0
for m in get_monitors():
x = m.x + m.width
y = m.y + m.height
max_x = max(x, max_x)
max_y = max(y, max_y)
if region:
x, y, width, height = get_region(orientation)
monitor = Monitor(
x, y, width, height,
name="Fake monitor from region selection"
)
else:
monitor = get_monitors()[monitor_num]
log.debug(f"Chose monitor: {monitor}")
log.debug(f"Screen size: ({max_x}, {max_y})")
return monitor, (max_x, max_y)
def get_region(orientation):
""" Show tkwindow to user to select mouse bounds
Args:
orientation (str): Location of tablet charging port.
('top', 'bottom', 'left', 'right')
Returns:
x (int), y (int), width (int), height (int)
"""
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
window_bounds = None
def on_click():
nonlocal window_bounds
window_bounds = (
window.winfo_x(), window.winfo_y(),
window.winfo_width(), window.winfo_height()
)
window.destroy()
confirm = ttk.Button(
window,
text="Resize and move this window, then click or press Enter",
command=on_click
)
confirm.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.E, tk.W))
window.bind('<Return>', lambda _: on_click())
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")
# block here
window.mainloop()
if window_bounds is None:
log.debug("Window closed without giving mouse range")
sys.exit(1)
return window_bounds
# remap wacom coordinates to screen coordinates
def remap(x, y, wacom_max_x, wacom_max_y, monitor_width,
monitor_height, mode, orientation):
if orientation == 'right':
x, y = wacom_max_x - x, wacom_max_y - y
if orientation == 'left':
pass
if orientation == 'top':
x, y = wacom_max_y - y, x
wacom_max_x, wacom_max_y = wacom_max_y, wacom_max_x
if orientation == 'bottom':
x, y = y, wacom_max_x - x
wacom_max_x, wacom_max_y = wacom_max_y, wacom_max_x
ratio_width, ratio_height = monitor_width / wacom_max_x, monitor_height / wacom_max_y
if mode == 'fill':
scaling_x = max(ratio_width, ratio_height)
scaling_y = scaling_x
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
else:
raise NotImplementedError
return (
scaling_x * (x - (wacom_max_x - monitor_width / scaling_x) / 2),
scaling_y * (y - (wacom_max_y - monitor_height / scaling_y) / 2)
)
def the_cooler_remap(x, y, monitor: Monitor):
single_mon_ratio = monitor.width / monitor.height
input_ratio = wacom_max_x / wacom_max_y
if single_mon_ratio > input_ratio: # monitor is wider than input in ratio, set yscale >1
xscale = 1.0
yscale = single_mon_ratio / input_ratio
else: # opposite: monitor is taller than input
yscale = 1.0
xscale = single_mon_ratio / input_ratio
return (
x * xscale,
y * yscale
)
def cool_monitor_mapping(x, y, monitor, wmax, hmax):
return (
x * monitor.width / wmax,
y * monitor.height / hmax
)
# log evdev event to console
def log_event(e_time, e_millis, e_type, e_code, e_value):
log.debug('{}.{:0>6} - {: <9} {: <15} {: >6}'.format(
e_time,
e_millis,
types[e_type],
codes[e_type][e_code],
e_value
))