Created
September 8, 2015 02:45
-
-
Save jboecker/deef671f940a244b1403 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Python3 script to manage an external Lenovo LT1423p monitor | |
Requirements: xrandr, wmctrl, displaylink service | |
""" | |
LAPTOP_WACOM_DEVICES=[ | |
"Wacom Serial Penabled 1FG Touchscreen stylus", | |
"Wacom Serial Penabled 1FG Touchscreen eraser" | |
] | |
LT1423P_WACOM_DEVICES=[ | |
"Wacom HID Pen stylus", | |
"Wacom HID Pen eraser", | |
"ELAN Touchscreen" | |
] | |
WACOM_DEVICE_MAP={ "laptop": LAPTOP_WACOM_DEVICES, | |
"lt1423p": LT1423P_WACOM_DEVICES } | |
LAPTOP_OUTPUT_NAME="LVDS1" | |
LT1423P_OUTPUT_NAME="DVI-1-0" | |
MPXPOINTER_WINDOW_CLASSES=["xournal.Xournal"] | |
DISABLE_TOUCHSCREEN=True | |
DISABLE_HOVER_CLICK=True | |
import sys | |
import os | |
import subprocess | |
import re | |
import time | |
def teardown_mpx(): | |
if subprocess.call(["xinput", "list", "Wacom pointer"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0: | |
os.system('xinput remove-master "Wacom pointer" AttachToMaster "Virtual core pointer" "Virtual core keyboard"') | |
def setup_mpx(wantMPX): | |
# first, remove MPX setup in any case, so this becomes an idempotent operation | |
teardown_mpx() | |
if wantMPX: | |
os.system('xinput create-master Wacom') | |
while 1: | |
if subprocess.call(["xinput", "list", "Wacom pointer"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0: | |
break | |
for device in LT1423P_WACOM_DEVICES: | |
os.system('xinput reattach "{0}" "Wacom pointer"'.format(device)) | |
# set the correct client pointer for all xournal windows | |
clientPointer = "Virtual core pointer" | |
if wantMPX: | |
clientPointer = "Wacom pointer" | |
for line in subprocess.getoutput("wmctrl -lx").split("\n"): | |
windowId, desktopNumber, windowClass, *rest = line.split() | |
if windowClass in MPXPOINTER_WINDOW_CLASSES: | |
os.system('xinput --set-cp "{0}" "{1}"'.format(windowId, clientPointer)) | |
def get_screen_size(): | |
screen0line = subprocess.getoutput('xrandr -q | grep "^Screen 0"') | |
# e.g.: Screen 0: minimum 320 x 200, current 1024 x 768, maximum 8192 x 8192 | |
width, height = map(int, re.match(r".*current (\d+) x (\d+),", screen0line).groups()) | |
return (width, height) | |
def get_output_rect(outputname): | |
line = subprocess.getoutput('xrandr -q | grep "^{0}"'.format(outputname)) | |
# e.g.: LVDS1 connected 1024x768+0+0 (normal left inverted right x axis y axis) 245mm x 184mm | |
width, height, xoffset, yoffset = map(int, re.match(r".* (\d+)x(\d+)\+(\d+)\+(\d+) ", line).groups()) | |
return (width, height, xoffset, yoffset) | |
def get_output_rotation(outputname): | |
line = subprocess.getoutput('xrandr --verbose | grep "^{0}"'.format(outputname)) | |
# e.g.: LVDS1 connected 768x1024+0+0 (0x43) left (normal left inverted right x axis y axis) 245mm x 184mm | |
rot = line.split(" ")[5] | |
if rot.startswith("("): | |
rot = "normal" | |
return rot | |
def set_wacom_output(outputname): | |
set_wacom_rect("laptop", *get_output_rect(outputname)) | |
def set_wacom_rect(device_name, width, height, xoffset, yoffset): | |
# see http://www.x.org/wiki/XInputCoordinateTransformationMatrixUsage | |
screenwidth, screenheight = get_screen_size() | |
SCALE_X=float(width)/screenwidth | |
SCALE_Y=float(height)/screenheight | |
TRANSLATE_X=xoffset/screenwidth | |
TRANSLATE_Y=yoffset/screenheight | |
matrix = "{0[SCALE_X]} 0 {0[TRANSLATE_X]} 0 {0[SCALE_Y]}f {0[TRANSLATE_Y]} 0 0 1".format(locals()) | |
for device in WACOM_DEVICE_MAP[device_name]: | |
cmd = 'xinput set-prop "{0}" "Coordinate Transformation Matrix" '.format(device) + matrix | |
os.system(cmd) | |
def wait_for_lt1423p_devices(): | |
sys.stdout.write("waiting for LT1423P input devices...") | |
sys.stdout.flush() | |
while 1: | |
devices_found = 0 | |
devices_wanted = len(LT1423P_WACOM_DEVICES) | |
for device in LT1423P_WACOM_DEVICES: | |
if (subprocess.call(["xinput", "list", device], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0): | |
devices_found += 1 | |
if devices_found == devices_wanted: | |
sys.stdout.write("\n") | |
return | |
sys.stdout.write(".") | |
sys.stdout.flush() | |
time.sleep(0.1) | |
if __name__=="__main__": | |
if len(sys.argv) <= 1: | |
print("""Usage: lt1423p.py <on|setcp|off> | |
Warning: DisplayLink and/or multi-pointer X support is not very stable. | |
To avoid crashing the X server, you should follow this sequence of actions precisely. | |
(And don't try to rotate the image on your LT1423p, either...) | |
Connecting: | |
* plug in the LT1423p and wait for its boot sequence to finish | |
* run "lt1423p.py on" | |
* Launch your xournal windows, run "lt1423p.py setcp" | |
Disconnecting: | |
* run "lt1423p.py off" and unplug the monitor when it tells you to | |
""") | |
else: | |
if sys.argv[1] == "on": | |
print('Plug in the LT1423p and wait for the "Power Saving Mode" message, then press return.') | |
dummy=input() | |
os.system("xrandr --setprovideroutputsource 1 0") | |
os.system("xrandr --output {0} --auto --below {1}".format(LT1423P_OUTPUT_NAME, LAPTOP_OUTPUT_NAME)) | |
wait_for_lt1423p_devices() | |
if DISABLE_HOVER_CLICK: | |
os.system('xinput --set-prop "Wacom HID Pen stylus" "Wacom Hover Click" 0') | |
if DISABLE_TOUCHSCREEN: | |
os.system('xinput disable "ELAN Touchscreen"') # disable the touchscreen on the LT1423p | |
set_wacom_rect("laptop", *get_output_rect(LAPTOP_OUTPUT_NAME)) | |
set_wacom_rect("lt1423p", *get_output_rect(LT1423P_OUTPUT_NAME)) | |
setup_mpx(True) | |
elif sys.argv[1] == "setcp": | |
setup_mpx(True) | |
elif sys.argv[1] == "off": | |
setup_mpx(False) | |
print("Save all your work before you continue!") | |
print("To reduce the chance of crashing the X server, close all applications that") | |
print("were using MPX before running this script.") | |
print("") | |
print("waiting for monitor to be disconnected...") | |
while 1: | |
if "{0} disconnected".format(LT1423P_OUTPUT_NAME) in subprocess.getoutput("xrandr -q"): | |
break | |
os.system("xrandr --output {0} --off".format(LT1423P_OUTPUT_NAME)) | |
#print("xrandr --output {0} --off".format(LT1423P_OUTPUT_NAME)) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment