mirror of
https://gitlab.freedesktop.org/emersion/libdisplay-info.git
synced 2024-12-25 21:59:08 +01:00
0cc784971b
The Display Monitor Timing specification is a PDF with data tables. A small Python script can be used to extract its contents in a machine-readable form. For now only extract a few fields. This can be extended later. Signed-off-by: Simon Ser <contact@emersion.fr>
116 lines
3.3 KiB
Python
Executable file
116 lines
3.3 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
def parse_hex_byte(s):
|
|
assert(s.endswith("h"))
|
|
s = s[:-1]
|
|
assert(0 <= int(s, 16) <= 255)
|
|
return "0x" + s
|
|
|
|
def parse_hex_list(s):
|
|
if s == "n/a":
|
|
return None
|
|
if s.startswith("("):
|
|
assert(s.endswith(")h"))
|
|
s = s[1:-2]
|
|
l = [b.strip() for b in s.split(",")]
|
|
l = [b[:-1] if b.endswith("h") else b for b in l]
|
|
for b in l:
|
|
assert(0 <= int(b, 16) <= 255)
|
|
return "0x" + "".join(l)
|
|
|
|
def parse_timing(page):
|
|
lines = [l.strip() for l in page.splitlines()]
|
|
if len(lines) < 6:
|
|
return None
|
|
try:
|
|
i = lines.index("Detailed Timing Parameters")
|
|
except ValueError:
|
|
return None
|
|
raw_metadata = lines[1:i]
|
|
raw_params = lines[i+1:]
|
|
|
|
metadata = {}
|
|
for l in raw_metadata:
|
|
if not ": " in l:
|
|
continue
|
|
k, v = l.split(":", 1)
|
|
metadata[k.strip()] = v.strip()
|
|
assert("Resolution" in metadata and "EDID ID" in metadata)
|
|
|
|
if "Proposed" in metadata and not "Adopted" in metadata:
|
|
print("skipping proposed but not adopted timing: " + metadata["Resolution"],
|
|
file=sys.stderr)
|
|
return None
|
|
|
|
params = {}
|
|
for l in raw_params:
|
|
if not " = " in l:
|
|
continue
|
|
tokens = [l.strip() for l in l.split("=")]
|
|
params[tokens[0]] = tokens[1:]
|
|
assert("Timing Name" in params)
|
|
|
|
res = metadata["Resolution"]
|
|
size, rest = res.split(" at ", 1)
|
|
horiz_video, vert_video = size.split("x", 1)
|
|
refresh_rate_hz, rest = rest.split(" Hz ", 1)
|
|
|
|
horiz_video = int(horiz_video.strip())
|
|
vert_video = int(vert_video.strip())
|
|
refresh_rate_hz = float(refresh_rate_hz.strip())
|
|
|
|
ids = {}
|
|
for kv in metadata["EDID ID"].split(";"):
|
|
k, v = kv.split(":")
|
|
ids[k.strip()] = v.strip()
|
|
assert("DMT ID" in ids)
|
|
|
|
dmt_id = parse_hex_byte(ids["DMT ID"])
|
|
edid_std_id = parse_hex_list(ids["Std. 2 Byte Code"])
|
|
cvt_id = parse_hex_list(ids["CVT 3 Byte Code"])
|
|
|
|
return {
|
|
"dmt_id": dmt_id,
|
|
"edid_std_id": 0 if edid_std_id is None else edid_std_id,
|
|
"cvt_id": 0 if cvt_id is None else cvt_id,
|
|
"horiz_video": horiz_video,
|
|
"vert_video": vert_video,
|
|
"refresh_rate_hz": refresh_rate_hz,
|
|
}
|
|
|
|
if len(sys.argv) != 2:
|
|
print("usage: gen-dmt.py <DMT PDF>", file=sys.stderr)
|
|
sys.exit(1)
|
|
in_path = sys.argv[1]
|
|
in_basename = os.path.basename(in_path)
|
|
|
|
tool_dir = os.path.dirname(os.path.realpath(__file__))
|
|
out_path = tool_dir + "/../dmt-table.c"
|
|
|
|
cmd = ["pdftotext", "-nodiag", "-layout", in_path, "-"]
|
|
page = ""
|
|
timings = []
|
|
for l in subprocess.check_output(cmd, text=True):
|
|
if l.startswith("\f"):
|
|
t = parse_timing(page)
|
|
if t is not None:
|
|
timings.append(t)
|
|
page = l[1:]
|
|
else:
|
|
page += l
|
|
|
|
with open(out_path, "w+") as f:
|
|
f.write("/* DO NOT EDIT! This file has been generated by gen-dmt.py from {}. */\n\n".format(in_basename))
|
|
f.write('#include "dmt.h"\n\n')
|
|
f.write("const struct di_dmt_timing _di_dmt_timings[] = {\n")
|
|
for t in timings:
|
|
f.write("\t{\n")
|
|
for k, v in t.items():
|
|
f.write("\t\t.{} = {},\n".format(k, v))
|
|
f.write("\t},\n")
|
|
f.write("};\n\n")
|
|
f.write("const size_t _di_dmt_timings_len = {};\n".format(len(timings)))
|