summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Ser <contact@emersion.fr>2019-09-02 20:51:02 +0300
committerPekka Paalanen <pq@iki.fi>2019-09-06 12:09:22 +0000
commit8854f64baeec50ebb0fe5af748574c290cce0682 (patch)
tree100846cc1aa2be5d05aa8d65cb7191bc46c542ad
parentcd9b3ef0cd2d6b6c5a07e07759699956af77555d (diff)
Add an automated script to update wl_shm.format
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>
-rwxr-xr-xprotocol/generate-shm-formats.py153
1 files changed, 153 insertions, 0 deletions
diff --git a/protocol/generate-shm-formats.py b/protocol/generate-shm-formats.py
new file mode 100755
index 0000000..c56642e
--- /dev/null
+++ b/protocol/generate-shm-formats.py
@@ -0,0 +1,153 @@
+#!/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)