8854f64bae
This prevents mismatches and missing formats between wl_shm.formats and drm_fourcc.h. The script collects DRM_FORMAT_* constants from drm_fourcc.h, compares the list with the current wayland.xml entries (checking for any mismatch) and then appends missing entries to wayland.xml. Enum values are obtained by executing a generated C file which prints the constants. There is no other reliable way to get these values as they are defined via various macros. There is no widespread Python library able to parse an XML file and format it with all whitespace preserved. For this reason, we don't use an XML library to create the new XML elements. Instead, we keep track of the line number of the last wl_shm.format enum entry and add new entries right after. To be able to read the line number of an element, we use lxml (the standard library doesn't retain line number information). Signed-off-by: Simon Ser <contact@emersion.fr>
154 lines
5.2 KiB
Python
Executable File
154 lines
5.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# This script synchronizes wayland.xml's wl_shm.format enum with drm_fourcc.h.
|
|
# Invoke it to update wayland.xml, then manually check the changes applied.
|
|
#
|
|
# Requires Python 3, python-lxml, a C compiler and pkg-config.
|
|
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
# We need lxml instead of the standard library because we want
|
|
# Element.sourceline
|
|
from lxml import etree as ElementTree
|
|
|
|
proto_dir = os.path.dirname(os.path.realpath(__file__))
|
|
wayland_proto = proto_dir + "/wayland.xml"
|
|
|
|
cc = os.getenv("CC", "cc")
|
|
pkg_config = os.getenv("PKG_CONFIG", "pkg-config")
|
|
|
|
# Find drm_fourcc.h
|
|
version = subprocess.check_output([pkg_config, "libdrm",
|
|
"--modversion"]).decode().strip()
|
|
cflags = subprocess.check_output([pkg_config, "libdrm",
|
|
"--cflags-only-I"]).decode().strip().split()
|
|
libdrm_include = None
|
|
for include_flag in cflags:
|
|
if not include_flag.startswith("-I"):
|
|
raise Exception("Expected one include dir for libdrm")
|
|
include_dir = include_flag[2:]
|
|
if include_dir.endswith("/libdrm"):
|
|
libdrm_include = include_dir
|
|
fourcc_include = libdrm_include + "/drm_fourcc.h"
|
|
if libdrm_include == None:
|
|
raise Exception("Failed to find libdrm include dir")
|
|
|
|
print("Using libdrm " + version, file=sys.stderr)
|
|
|
|
def drm_format_to_wl(ident):
|
|
return ident.replace("DRM_FORMAT_", "").lower()
|
|
|
|
# Collect DRM format constant names
|
|
ident_list = []
|
|
descriptions = {}
|
|
prev_comment = None
|
|
with open(fourcc_include) as input_file:
|
|
for l in input_file.readlines():
|
|
l = l.strip()
|
|
|
|
# Collect comments right before format definitions
|
|
if l.startswith("/*") and l.endswith("*/"):
|
|
prev_comment = l[2:-2]
|
|
continue
|
|
desc = prev_comment
|
|
prev_comment = None
|
|
|
|
# Recognize format definitions
|
|
parts = l.split()
|
|
if len(parts) < 3 or parts[0] != "#define":
|
|
continue
|
|
ident = parts[1]
|
|
if not ident.startswith("DRM_FORMAT_") or ident.startswith(
|
|
"DRM_FORMAT_MOD_"):
|
|
continue
|
|
|
|
ident_list.append(ident)
|
|
|
|
# Prefer in-line comments
|
|
if l.endswith("*/"):
|
|
desc = l[l.rfind("/*") + 2:-2]
|
|
if desc != None:
|
|
descriptions[drm_format_to_wl(ident)] = desc.strip()
|
|
|
|
# Collect DRM format values
|
|
idents = {}
|
|
with tempfile.TemporaryDirectory() as work_dir:
|
|
c_file_name = work_dir + "/print-formats.c"
|
|
exe_file_name = work_dir + "/print-formats"
|
|
|
|
with open(c_file_name, "w+") as c_file:
|
|
c_file.write('#include <inttypes.h>\n')
|
|
c_file.write('#include <stdint.h>\n')
|
|
c_file.write('#include <stdio.h>\n')
|
|
c_file.write('#include <drm_fourcc.h>\n')
|
|
c_file.write('\n')
|
|
c_file.write('int main(void) {\n')
|
|
for ident in ident_list:
|
|
c_file.write('printf("0x%" PRIX64 "\\n", (uint64_t)' + ident + ');\n')
|
|
c_file.write('}\n')
|
|
|
|
subprocess.check_call([cc, "-Wall", "-Wextra", "-o", exe_file_name,
|
|
c_file_name] + cflags)
|
|
output = subprocess.check_output([exe_file_name]).decode().strip()
|
|
for i, val in enumerate(output.splitlines()):
|
|
idents[ident_list[i]] = val
|
|
|
|
# We don't need those
|
|
del idents["DRM_FORMAT_BIG_ENDIAN"]
|
|
del idents["DRM_FORMAT_INVALID"]
|
|
del idents["DRM_FORMAT_RESERVED"]
|
|
|
|
# Convert from DRM constants to Wayland wl_shm.format entries
|
|
formats = {}
|
|
for ident, val in idents.items():
|
|
formats[drm_format_to_wl(ident)] = val.lower()
|
|
# Special case for ARGB8888 and XRGB8888
|
|
formats["argb8888"] = "0"
|
|
formats["xrgb8888"] = "1"
|
|
|
|
print("Loaded {} formats from drm_fourcc.h".format(len(formats)), file=sys.stderr)
|
|
|
|
tree = ElementTree.parse("wayland.xml")
|
|
root = tree.getroot()
|
|
wl_shm_format = root.find("./interface[@name='wl_shm']/enum[@name='format']")
|
|
if wl_shm_format == None:
|
|
raise Exception("wl_shm.format not found in wayland.xml")
|
|
|
|
# Remove formats we already know about
|
|
last_line = None
|
|
for node in wl_shm_format:
|
|
if node.tag != "entry":
|
|
continue
|
|
fmt = node.attrib["name"]
|
|
val = node.attrib["value"]
|
|
if fmt not in formats:
|
|
raise Exception("Format present in wl_shm.formats but not in "
|
|
"drm_fourcc.h: " + fmt)
|
|
if val != formats[fmt]:
|
|
raise Exception("Format value in wl_shm.formats ({}) differs "
|
|
"from value in drm_fourcc.h ({}) for format {}"
|
|
.format(val, formats[fmt], fmt))
|
|
del formats[fmt]
|
|
last_line = node.sourceline
|
|
if last_line == None:
|
|
raise Exception("Expected at least one existing wl_shm.format entry")
|
|
|
|
print("Adding {} formats to wayland.xml...".format(len(formats)), file=sys.stderr)
|
|
|
|
# Append new formats
|
|
new_wayland_proto = wayland_proto + ".new"
|
|
with open(new_wayland_proto, "w+") as output_file, \
|
|
open(wayland_proto) as input_file:
|
|
for i, l in enumerate(input_file.readlines()):
|
|
output_file.write(l)
|
|
if i + 1 == last_line:
|
|
for fmt, val in formats.items():
|
|
output_file.write(' <entry name="{}" value="{}"'
|
|
.format(fmt, val))
|
|
if fmt in descriptions:
|
|
output_file.write(' summary="{}"'.format(descriptions[fmt]))
|
|
output_file.write('/>\n')
|
|
os.rename(new_wayland_proto, wayland_proto)
|