|
|
|
@ -1,5 +1,8 @@
@@ -1,5 +1,8 @@
|
|
|
|
|
import logging |
|
|
|
|
import struct |
|
|
|
|
import subprocess |
|
|
|
|
from screeninfo import get_monitors |
|
|
|
|
import time |
|
|
|
|
|
|
|
|
|
logging.basicConfig(format='%(message)s') |
|
|
|
|
log = logging.getLogger(__name__) |
|
|
|
@ -24,6 +27,7 @@ def create_local_device():
@@ -24,6 +27,7 @@ def create_local_device():
|
|
|
|
|
|
|
|
|
|
# Set device properties to emulate those of Wacom tablets |
|
|
|
|
device.name = 'reMarkable tablet' |
|
|
|
|
|
|
|
|
|
device.id = { |
|
|
|
|
'bustype': 0x18, # i2c |
|
|
|
|
'vendor': 0x056a, # wacom |
|
|
|
@ -85,6 +89,28 @@ def create_local_device():
@@ -85,6 +89,28 @@ def create_local_device():
|
|
|
|
|
return device.create_uinput_device() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# remap screen coordinates to wacom coordinates |
|
|
|
|
def remap(x, y, wacom_width, wacom_height, monitor_width, |
|
|
|
|
monitor_height, mode, orientation=None): |
|
|
|
|
|
|
|
|
|
if orientation in ('bottom', 'top'): |
|
|
|
|
x, y = y, x |
|
|
|
|
monitor_width, monitor_height = monitor_height, monitor_width |
|
|
|
|
|
|
|
|
|
ratio_width, ratio_height = wacom_width / monitor_width, wacom_height / monitor_height |
|
|
|
|
|
|
|
|
|
if mode == 'fit': |
|
|
|
|
scaling = max(ratio_width, ratio_height) |
|
|
|
|
elif mode == 'fill': |
|
|
|
|
scaling = min(ratio_width, ratio_height) |
|
|
|
|
else: |
|
|
|
|
raise NotImplementedError |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
scaling * (x - (monitor_width - wacom_width / scaling) / 2), |
|
|
|
|
scaling * (y - (monitor_height - wacom_height / scaling) / 2) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
def pipe_device(args, remote_device, local_device): |
|
|
|
|
""" |
|
|
|
|
Pipe events from a remote device to a local device. |
|
|
|
@ -94,7 +120,65 @@ def pipe_device(args, remote_device, local_device):
@@ -94,7 +120,65 @@ def pipe_device(args, remote_device, local_device):
|
|
|
|
|
remote_device (paramiko.ChannelFile): read-only stream of input events |
|
|
|
|
local_device: local virtual input device to write events to |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
# give time for virtual device creation before running xinput commands |
|
|
|
|
time.sleep(1) |
|
|
|
|
|
|
|
|
|
# set orientation with xinput |
|
|
|
|
orientation = {'left': 0, 'bottom': 1, 'top': 2, 'right': 3}[args.orientation] |
|
|
|
|
result = subprocess.run( |
|
|
|
|
'xinput --set-prop "reMarkable tablet stylus" "Wacom Rotation" {}'.format(orientation), |
|
|
|
|
capture_output=True, |
|
|
|
|
shell=True |
|
|
|
|
) |
|
|
|
|
if result.returncode != 0: |
|
|
|
|
log.warning("Error setting orientation: %s", result.stderr) |
|
|
|
|
|
|
|
|
|
# set monitor to use |
|
|
|
|
monitor = get_monitors()[args.monitor] |
|
|
|
|
log.debug('Chose monitor: {}'.format(monitor)) |
|
|
|
|
result = subprocess.run( |
|
|
|
|
'xinput --map-to-output "reMarkable tablet stylus" {}'.format(monitor.name), |
|
|
|
|
capture_output=True, |
|
|
|
|
shell=True |
|
|
|
|
) |
|
|
|
|
if result.returncode != 0: |
|
|
|
|
log.warning("Error setting monitor: %s", result.stderr) |
|
|
|
|
|
|
|
|
|
# set stylus pressure |
|
|
|
|
result = subprocess.run( |
|
|
|
|
'xinput --set-prop "reMarkable tablet stylus" "Wacom Pressure Threshold" {}'.format(args.threshold), |
|
|
|
|
capture_output=True, |
|
|
|
|
shell=True |
|
|
|
|
) |
|
|
|
|
if result.returncode != 0: |
|
|
|
|
log.warning("Error setting pressure threshold: %s", result.stderr) |
|
|
|
|
|
|
|
|
|
# set fitting mode |
|
|
|
|
min_x, min_y = remap( |
|
|
|
|
0, 0, |
|
|
|
|
MAX_ABS_X, MAX_ABS_Y, monitor.width, monitor.height, |
|
|
|
|
args.mode, |
|
|
|
|
args.orientation |
|
|
|
|
) |
|
|
|
|
max_x, max_y = remap( |
|
|
|
|
monitor.width, monitor.height, |
|
|
|
|
MAX_ABS_X, MAX_ABS_Y, monitor.width, monitor.height, |
|
|
|
|
args.mode, |
|
|
|
|
args.orientation |
|
|
|
|
) |
|
|
|
|
log.debug("Wacom tablet area: {} {} {} {}".format(min_x, min_y, max_x, max_y)) |
|
|
|
|
result = subprocess.run( |
|
|
|
|
'xinput --set-prop "reMarkable tablet stylus" "Wacom Tablet Area" \ |
|
|
|
|
{} {} {} {}'.format(min_x, min_y, max_x, max_y), |
|
|
|
|
capture_output=True, |
|
|
|
|
shell=True |
|
|
|
|
) |
|
|
|
|
if result.returncode != 0: |
|
|
|
|
log.warning("Error setting fit: %s", result.stderr) |
|
|
|
|
|
|
|
|
|
import libevdev |
|
|
|
|
|
|
|
|
|
# While debug mode is active, we log events grouped together between |
|
|
|
|
# SYN_REPORT events. Pending events for the next log are stored here |
|
|
|
|
pending_events = [] |
|
|
|
|