#!/usr/bin/env python3 # https://en.wikipedia.org/wiki/Extended_Display_Identification_Data # TODO: This is a real mess, it's a transition from the setup-desktop.sh shell # script to something a little more flexible. But it mostly doesn't work # yet and is just subbing in for parse-edid. # TODO: List screens if none are known # TODO: Zooming # xrandr --output eDP-1 --mode 2880x1620 --panning 2880x1620 #bigmodes=( 3840x2160 3200x1800 ) #modes=( 2560x1440 2048x1152 1920x1080 1600x900 1368x768 # 1360x768 1280x720 1024x576 960x540 864x486 # 720x405 640x360 ) import re import os import sys import struct from collections import namedtuple from subprocess import check_output, call BUILTIN_DISPLAY_RESOLUTIONS = [ "1600x900", "1920x1080", "2048x1152", "2880x1620"] BEST_RESOLUTION = BUILTIN_DISPLAY_RESOLUTIONS[2] DRM_PATH = "/sys/class/drm" EDID_MAGIC = b'\x00\xff\xff\xff\xff\xff\xff\x00' EDID_14 = b'\x01\x04' UNSPEC_TEXT = 0xFE SERIAL = 0xFF NAME = 0xFC MonitorData = namedtuple( "MonitorData", ("card", "port", "name", "serial", "text")) def parse_xrandr_query(): monitors = {} monitor = None for line in check_output(("xrandr", "-q")).split(b"\n"): line = line.decode("us-ascii") if " connected " in line: monitor = line.split(" ")[0] monitors[monitor] = [] elif monitor and line.startswith(" "): monitors[monitor].append(line.strip().split(" ")[0]) return monitors def parse_card_port(p): card, port = p.split("/")[-1].split("-", 1) return int(card[len("card"):]), port def parse_descriptor(card, port, md): name, serial, text = None, None, None for i in range(54, 108, 18): d = md[i:i+18] dtype, data = d[3], d[5:] prep = lambda dd: dd.decode("us-ascii", errors="ignore").strip() if dtype == UNSPEC_TEXT: text = prep(data) elif dtype == SERIAL: serial = prep(data) elif dtype == NAME: name = prep(data) return MonitorData(card, port, name, serial, text) def parse_edid(data, path): if data[0:8] != EDID_MAGIC: raise Exception("Not EDID") if data[18:20] != EDID_14: raise Exception("Not EDID 1.4") card, port = parse_card_port(path) return parse_descriptor(card, port, data) def get_monitor_edid(path): edid = os.path.join(path, "edid") if not os.path.exists(edid): raise Exception("No card with that name {!r}".format(path)) with open(edid, "rb") as fp: data = fp.read() return parse_edid(data, path) def enumerate_monitors(): monitors = [] for path in os.listdir(DRM_PATH): if not re.match("^card\d+-", path): continue drm_path = os.path.join(DRM_PATH, path) monitors.append(get_monitor_edid(drm_path)) return monitors def script_main(args): all_resolutions = parse_xrandr_query() # Do first to refresh /sys all_monitors = enumerate_monitors() for monitor in all_monitors: resolutions = all_resolutions[monitor.port] # 27" Dell monitors if monitor == "DELL U2715H": call(["xrandr", "--output", "DP-1","--auto", "--output", "DP-2", "--auto", "--right-of", "DP-1", "--output", "eDP-1", "--off"]) break elif monitor == "AMX_HDMI1_A2": call(["xrandr", "--output", "eDP-1", "--mode", BEST_RESOLUTION, "--output", monitor.card, "--auto", "--right-of", "eDP-1"]) break elif monitor == "DELL U3415W": call(["xrandr", "--output", "eDP-1", "--mode", BEST_RESOLUTION, "--output", monitor.card, "--mode", "3440x1440", "--right-of", "eDP-1"]) break elif monitor == "CS-CODECPLUS": call(["xrandr", "--output", "eDP-1", "--mode", BEST_RESOLUTION, "--output", monitor.card, "--auto", "--right-of", "eDP-1"]) break else: call(["xrandr", "--output", "eDP-1", "--mode", BEST_RESOLUTION, "--output", "DP-1", "--off", "--output", "DP-2", "--off"]) break def main(args): print(get_monitor_edid(args[1]).name) if __name__ == "__main__": #sys.exit(main(sys.argv)) sys.stdin.mode = "rb" print(parse_edid(sys.stdin.buffer.read(), "/card1-eDP-1").text)