import logging import struct import subprocess from screeninfo import get_monitors import time from itertools import cycle from socket import timeout as TimeoutError import libevdev from .codes import codes, types from .common import get_monitor, remap, wacom_max_x, wacom_max_y, log_event, the_cooler_remap, cool_monitor_mapping logging.basicConfig(format='%(message)s') log = logging.getLogger('remouse') 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 pen' device.id = { 'bustype': 0x03, # usb '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) device.enable(libevdev.EV_KEY.BTN_0) device.enable(libevdev.EV_KEY.BTN_1) device.enable(libevdev.EV_KEY.BTN_2) inputs = ( # touch inputs (libevdev.EV_ABS.ABS_MT_POSITION_X, 0, 767, 2531), (libevdev.EV_ABS.ABS_MT_POSITION_Y, 0, 1023, 2531), (libevdev.EV_ABS.ABS_MT_PRESSURE, 0, 255, None), (libevdev.EV_ABS.ABS_MT_TOUCH_MAJOR, 0, 255, None), (libevdev.EV_ABS.ABS_MT_TOUCH_MINOR, 0, 255, None), (libevdev.EV_ABS.ABS_MT_ORIENTATION, -127, 127, None), (libevdev.EV_ABS.ABS_MT_SLOT, 0, 31, None), (libevdev.EV_ABS.ABS_MT_TOOL_TYPE, 0, 1, None), (libevdev.EV_ABS.ABS_MT_TRACKING_ID, 0, 65535, None), # pen inputs (libevdev.EV_ABS.ABS_X, 0, 20967, 2531), # cyttps5_mt driver (libevdev.EV_ABS.ABS_Y, 0, 15725, 2531), # cyttsp5_mt (libevdev.EV_ABS.ABS_PRESSURE, 0, 4095, None), (libevdev.EV_ABS.ABS_DISTANCE, 0, 255, None), (libevdev.EV_ABS.ABS_TILT_X, -9000, 9000, None), (libevdev.EV_ABS.ABS_TILT_Y, -9000, 9000, None) ) for code, minimum, maximum, resolution in inputs: device.enable( code, libevdev.InputAbsInfo( minimum=minimum, maximum=maximum, resolution=resolution ) ) return device.create_uinput_device() def read_tablet(rm_inputs, *, orientation, monitor_num, region, threshold, mode): """Pipe rM evdev events to local device Args: rm_inputs (dictionary of paramiko.ChannelFile): dict of pen, button and touch input streams orientation (str): tablet orientation monitor_num (int): monitor number to map to threshold (int): pressure threshold mode (str): mapping mode """ local_device = create_local_device() log.debug("Created virtual input device '{}'".format(local_device.devnode)) monitor, (tot_width, tot_height) = get_monitor(region, monitor_num, orientation) pending_events = [] x = y = 0 # loop inputs forever # for input_name, stream in cycle(rm_inputs.items()): stream = rm_inputs['pen'] while True: try: data = stream.read(16) except TimeoutError: continue e_time, e_millis, e_type, e_code, e_value = struct.unpack('2IHHi', data) # intercept EV_ABS events and modify coordinates if types[e_type] == 'EV_ABS': # handle x direction if codes[e_type][e_code] == 'ABS_X': x = e_value # handle y direction if codes[e_type][e_code] == 'ABS_Y': y = e_value if mode == 'cool': mapx, mapy = the_cooler_remap(x, y, monitor) mapped_x, mapped_y = cool_monitor_mapping(mapx, mapy, monitor, tot_width, tot_height) else: mapped_x, mapped_y = remap( x, y, wacom_max_x, wacom_max_y, # 20k something | presumably 3840 | 7680 15k something, # -> 10k something presumably 15k something wacom_max_x * (monitor.width / tot_width), wacom_max_y * (monitor.height / tot_height), mode, orientation ) mapped_x += wacom_max_x * (monitor.x / tot_width) mapped_y += wacom_max_y * (monitor.y / tot_height) # reinsert modified values into evdev event if codes[e_type][e_code] == 'ABS_X': e_value = int(mapped_x) if codes[e_type][e_code] == 'ABS_Y': e_value = int(mapped_y) # pass events directly to libevdev e_bit = libevdev.evbit(e_type, e_code) e = libevdev.InputEvent(e_bit, value=e_value) local_device.send_events([e]) if log.level == logging.DEBUG: log_event(e_time, e_millis, e_type, e_code, e_value)