|
|
|
@ -1,5 +1,8 @@
|
|
|
|
import logging
|
|
|
|
import logging
|
|
|
|
import struct
|
|
|
|
import struct
|
|
|
|
|
|
|
|
import subprocess
|
|
|
|
|
|
|
|
from screeninfo import get_monitors
|
|
|
|
|
|
|
|
import time
|
|
|
|
|
|
|
|
|
|
|
|
logging.basicConfig(format='%(message)s')
|
|
|
|
logging.basicConfig(format='%(message)s')
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
@ -24,6 +27,7 @@ def create_local_device():
|
|
|
|
|
|
|
|
|
|
|
|
# Set device properties to emulate those of Wacom tablets
|
|
|
|
# Set device properties to emulate those of Wacom tablets
|
|
|
|
device.name = 'reMarkable tablet'
|
|
|
|
device.name = 'reMarkable tablet'
|
|
|
|
|
|
|
|
|
|
|
|
device.id = {
|
|
|
|
device.id = {
|
|
|
|
'bustype': 0x18, # i2c
|
|
|
|
'bustype': 0x18, # i2c
|
|
|
|
'vendor': 0x056a, # wacom
|
|
|
|
'vendor': 0x056a, # wacom
|
|
|
|
@ -85,6 +89,28 @@ def create_local_device():
|
|
|
|
return device.create_uinput_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):
|
|
|
|
def pipe_device(args, remote_device, local_device):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Pipe events from a remote device to a local device.
|
|
|
|
Pipe events from a remote device to a local device.
|
|
|
|
@ -94,7 +120,65 @@ def pipe_device(args, remote_device, local_device):
|
|
|
|
remote_device (paramiko.ChannelFile): read-only stream of input events
|
|
|
|
remote_device (paramiko.ChannelFile): read-only stream of input events
|
|
|
|
local_device: local virtual input device to write events to
|
|
|
|
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
|
|
|
|
import libevdev
|
|
|
|
|
|
|
|
|
|
|
|
# While debug mode is active, we log events grouped together between
|
|
|
|
# While debug mode is active, we log events grouped together between
|
|
|
|
# SYN_REPORT events. Pending events for the next log are stored here
|
|
|
|
# SYN_REPORT events. Pending events for the next log are stored here
|
|
|
|
pending_events = []
|
|
|
|
pending_events = []
|
|
|
|
|