Skip to content

Instantly share code, notes, and snippets.

@nocnokneo
Last active August 29, 2015 14:04
Show Gist options
  • Save nocnokneo/3722097d2fc29b0d676a to your computer and use it in GitHub Desktop.
Save nocnokneo/3722097d2fc29b0d676a to your computer and use it in GitHub Desktop.
multidispay_touchscreen_autoconfig.py
# Copy this file to /etc/xdg/autostart/
# multidisplay_touchscreen_autoconfig.py must be executable and in your system PATH
[Desktop Entry]
Exec=multidisplay_touchscreen_autoconfig.py
Name=Autoconfigure touchscreens for use on a multi-monitor system
Type=Application
NoDisplay=true
#!/usr/bin/env python
import subprocess
import re
import binascii
import logging
# A dictionary that maps (vendorId,productId) => touchscreenDevice
# TODO: Make configurable
touchscreen_db = { ('ACR', 0x9703) : 'Advanced Silicon S.A CoolTouch(TM) System'
('FIM', 0x6145) : 'USB Touchscreen 0dfc:0001' }
# Copied from edid.py in Chromium OS
def parse_edid(blob):
"""EDID Parser (light-weight parse-edid replacement).
Simple parsing of EDID. The full-feature parser (parse-edid) has
many more dependencies, and so is too heavy-weight for use here.
TODO(hungte) Use parse-edid if it becomes practical/available.
Specifically once we know that it will be available on all of our
systems.
Args:
blob: a binary blob with encoded EDID.
Returns:
A dict of extracted keys to extracted EDID fields. Return None if
the blob is not a valid EDID record, and also log warning messages
indicating the reason for parsing failure.
"""
def read_short(offset):
return ((ord(blob[offset]) << 8) | ord(blob[offset + 1]))
# Constants lifted from EDID documentation.
VERSION = 0x01
MAGIC = '\x00\xff\xff\xff\xff\xff\xff\x00'
MAGIC_OFFSET = 0
MANUFACTURER_ID_OFFSET = 8
PRODUCT_ID_OFFSET = 10
VERSION_OFFSET = 18
REVISION_OFFSET = 19
PIXEL_CLOCK_OFFSET = 54
HORIZONTAL_OFFSET = 56
HORIZONTAL_HIGH_OFFSET = 58
VERTICAL_OFFSET = 59
VERTICAL_HIGH_OFFSET = 61
CHECKSUM_OFFSET = 127
MINIMAL_SIZE = 128
MANUFACTURER_ID_BITS = 5
# Check size, magic, and version
if len(blob) < MINIMAL_SIZE:
logging.warning("EDID parsing error: length too small.")
return None
if (blob[MAGIC_OFFSET:(MAGIC_OFFSET + len(MAGIC))] != MAGIC):
logging.warning("EDID parse error: incorrect header.")
return None
if ord(blob[VERSION_OFFSET]) != VERSION:
logging.warning("EDID parse error: unsupported EDID version.")
return None
# Verify checksum
if sum([ord(char) for char in blob[:CHECKSUM_OFFSET+1]]) % 0x100 != 0:
logging.warning("EDID parse error: checksum error.")
return None
# Currently we don't support EDID not using pixel clock
pixel_clock = read_short(PIXEL_CLOCK_OFFSET)
if not pixel_clock:
logging.warning("EDID parse error: "
"non-pixel clock format is not supported yet.")
return None
# Extract manufactuer
vendor_name = ''
vendor_code = read_short(MANUFACTURER_ID_OFFSET)
# vendor_code: [0 | char1 | char2 | char3]
for i in range(2, -1, -1):
vendor_char = (vendor_code >> (i * MANUFACTURER_ID_BITS)) & 0x1F
vendor_char = chr(vendor_char + ord('@'))
vendor_name += vendor_char
product_id = read_short(PRODUCT_ID_OFFSET)
width = (ord(blob[HORIZONTAL_OFFSET]) |
((ord(blob[HORIZONTAL_HIGH_OFFSET]) >> 4) << 8))
height = (ord(blob[VERTICAL_OFFSET]) |
((ord(blob[VERTICAL_HIGH_OFFSET]) >> 4) << 8))
return { 'vendor': vendor_name,
'product_id': product_id,
'width': width,
'height': height }
def connected_display_hardware():
"""Returns a list of tuples that map outputs to the corresponding connected display hardware
hardware. For example:
>>> connected_display_hardware()
[('DP-0', ('SAM', 44545)), ('DP-3', ('ACR', 38659))]
"""
xrandr_prop = subprocess.check_output(['xrandr', '--prop'])
# Regex that matches two groups:
# (1) The display device name
# (2) The EDID ascii hex string (with spaces)
display_edid_regex = re.compile(r"^(\S+) connected .*\n^\s*EDID: *((?:\n\t\t[\da-fA-F]{32})*)", re.MULTILINE)
display_edid_iter = display_edid_regex.finditer(xrandr_prop)
supported_touchscreen_found = False
connections = []
for match in display_edid_iter:
output, edid_ascii = match.groups()
edid_blob = binascii.unhexlify(re.sub(r"\s*", "", edid_ascii))
edid = parse_edid(edid_blob)
connections.append( (output, (edid['vendor'], edid['product_id'])) )
return connections
def main():
print "Establishing correspondance between display and touch input devices ..."
for output, display in connected_display_hardware():
if touchscreen_db.has_key(display):
touch_input_device = touchscreen_db[display]
try:
touch_input_device_id = subprocess.check_output(['xinput', '--list', '--id-only', touch_input_device]).strip()
except subprocess.CalledProcessError:
logging.warning("Could not find touch input device '%s' for display %s. Is it connected?" % (touch_input_device, display))
continue
print "\t%s => %s" % (display, touch_input_device)
subprocess.check_call(['xinput', '--map-to-output', touch_input_device_id, output])
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment