#!/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('', 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 ))