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.
 
 

206 lines
6.0 KiB

import logging
import struct
import subprocess
from screeninfo import get_monitors
import time
logging.basicConfig(format='%(message)s')
log = logging.getLogger(__name__)
# Maximum value that can be reported by the Wacom driver for the X axis
MAX_ABS_X = 20967
# Maximum value that can be reported by the Wacom driver for the Y axis
MAX_ABS_Y = 15725
def create_local_device():
"""
Create a virtual input device on this host that has the same
characteristics as a Wacom tablet.
Returns:
virtual input device
"""
import libevdev
device = libevdev.Device()
# Set device properties to emulate those of Wacom tablets
device.name = 'reMarkable tablet'
device.id = {
'bustype': 0x18, # i2c
'vendor': 0x056a, # wacom
'product': 0,
'version': 54
}
# Enable buttons supported by the digitizer
device.enable(libevdev.EV_KEY.BTN_TOOL_PEN)
device.enable(libevdev.EV_KEY.BTN_TOOL_RUBBER)
device.enable(libevdev.EV_KEY.BTN_TOUCH)
device.enable(libevdev.EV_KEY.BTN_STYLUS)
device.enable(libevdev.EV_KEY.BTN_STYLUS2)
# Enable position, tilt, distance and pressure change events
device.enable(
libevdev.EV_ABS.ABS_X,
libevdev.InputAbsInfo(
minimum=0,
maximum=MAX_ABS_X,
resolution=2531
)
)
device.enable(
libevdev.EV_ABS.ABS_Y,
libevdev.InputAbsInfo(
minimum=0,
maximum=MAX_ABS_Y,
resolution=2531
)
)
device.enable(
libevdev.EV_ABS.ABS_PRESSURE,
libevdev.InputAbsInfo(
minimum=0,
maximum=4095
)
)
device.enable(
libevdev.EV_ABS.ABS_DISTANCE,
libevdev.InputAbsInfo(
minimum=0,
maximum=255
)
)
device.enable(
libevdev.EV_ABS.ABS_TILT_X,
libevdev.InputAbsInfo(
minimum=-9000,
maximum=9000
)
)
device.enable(
libevdev.EV_ABS.ABS_TILT_Y,
libevdev.InputAbsInfo(
minimum=-9000,
maximum=9000
)
)
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.
Args:
args: argparse arguments
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 = []
while True:
e_time, e_millis, e_type, e_code, e_value = struct.unpack('2IHHi', remote_device.read(16))
e_bit = libevdev.evbit(e_type, e_code)
event = libevdev.InputEvent(e_bit, value=e_value)
local_device.send_events([event])
if args.debug:
if e_bit == libevdev.EV_SYN.SYN_REPORT:
event_repr = ', '.join(
'{} = {}'.format(
event.code.name,
event.value
) for event in pending_events
)
log.debug('{}.{:0>6} - {}'.format(e_time, e_millis, event_repr))
pending_events = []
else:
pending_events += [event]