1#!/usr/bin/env python3 2 3# This script synchronizes wayland.xml's wl_shm.format enum with drm_fourcc.h. 4# Invoke it to update wayland.xml, then manually check the changes applied. 5# 6# Requires Python 3, python-lxml, a C compiler and pkg-config. 7 8import os 9import subprocess 10import sys 11import tempfile 12# We need lxml instead of the standard library because we want 13# Element.sourceline 14from lxml import etree as ElementTree 15 16proto_dir = os.path.dirname(os.path.realpath(__file__)) 17wayland_proto = proto_dir + "/wayland.xml" 18 19cc = os.getenv("CC", "cc") 20pkg_config = os.getenv("PKG_CONFIG", "pkg-config") 21 22# Find drm_fourcc.h 23version = subprocess.check_output([pkg_config, "libdrm", 24 "--modversion"]).decode().strip() 25cflags = subprocess.check_output([pkg_config, "libdrm", 26 "--cflags-only-I"]).decode().strip().split() 27libdrm_include = None 28for include_flag in cflags: 29 if not include_flag.startswith("-I"): 30 raise Exception("Expected one include dir for libdrm") 31 include_dir = include_flag[2:] 32 if include_dir.endswith("/libdrm"): 33 libdrm_include = include_dir 34 fourcc_include = libdrm_include + "/drm_fourcc.h" 35if libdrm_include == None: 36 raise Exception("Failed to find libdrm include dir") 37 38print("Using libdrm " + version, file=sys.stderr) 39 40def drm_format_to_wl(ident): 41 return ident.replace("DRM_FORMAT_", "").lower() 42 43# Collect DRM format constant names 44ident_list = [] 45descriptions = {} 46prev_comment = None 47with open(fourcc_include) as input_file: 48 for l in input_file.readlines(): 49 l = l.strip() 50 51 # Collect comments right before format definitions 52 if l.startswith("/*") and l.endswith("*/"): 53 prev_comment = l[2:-2] 54 continue 55 desc = prev_comment 56 prev_comment = None 57 58 # Recognize format definitions 59 parts = l.split() 60 if len(parts) < 3 or parts[0] != "#define": 61 continue 62 ident = parts[1] 63 if not ident.startswith("DRM_FORMAT_") or ident.startswith( 64 "DRM_FORMAT_MOD_"): 65 continue 66 67 ident_list.append(ident) 68 69 # Prefer in-line comments 70 if l.endswith("*/"): 71 desc = l[l.rfind("/*") + 2:-2] 72 if desc != None: 73 descriptions[drm_format_to_wl(ident)] = desc.strip() 74 75# Collect DRM format values 76idents = {} 77with tempfile.TemporaryDirectory() as work_dir: 78 c_file_name = work_dir + "/print-formats.c" 79 exe_file_name = work_dir + "/print-formats" 80 81 with open(c_file_name, "w+") as c_file: 82 c_file.write('#include <inttypes.h>\n') 83 c_file.write('#include <stdint.h>\n') 84 c_file.write('#include <stdio.h>\n') 85 c_file.write('#include <drm_fourcc.h>\n') 86 c_file.write('\n') 87 c_file.write('int main(void) {\n') 88 for ident in ident_list: 89 c_file.write('printf("0x%" PRIX64 "\\n", (uint64_t)' + ident + ');\n') 90 c_file.write('}\n') 91 92 subprocess.check_call([cc, "-Wall", "-Wextra", "-o", exe_file_name, 93 c_file_name] + cflags) 94 output = subprocess.check_output([exe_file_name]).decode().strip() 95 for i, val in enumerate(output.splitlines()): 96 idents[ident_list[i]] = val 97 98# We don't need those 99del idents["DRM_FORMAT_BIG_ENDIAN"] 100del idents["DRM_FORMAT_INVALID"] 101del idents["DRM_FORMAT_RESERVED"] 102 103# Convert from DRM constants to Wayland wl_shm.format entries 104formats = {} 105for ident, val in idents.items(): 106 formats[drm_format_to_wl(ident)] = val.lower() 107# Special case for ARGB8888 and XRGB8888 108formats["argb8888"] = "0" 109formats["xrgb8888"] = "1" 110 111print("Loaded {} formats from drm_fourcc.h".format(len(formats)), file=sys.stderr) 112 113tree = ElementTree.parse("wayland.xml") 114root = tree.getroot() 115wl_shm_format = root.find("./interface[@name='wl_shm']/enum[@name='format']") 116if wl_shm_format == None: 117 raise Exception("wl_shm.format not found in wayland.xml") 118 119# Remove formats we already know about 120last_line = None 121for node in wl_shm_format: 122 if node.tag != "entry": 123 continue 124 fmt = node.attrib["name"] 125 val = node.attrib["value"] 126 if fmt not in formats: 127 raise Exception("Format present in wl_shm.formats but not in " 128 "drm_fourcc.h: " + fmt) 129 if val != formats[fmt]: 130 raise Exception("Format value in wl_shm.formats ({}) differs " 131 "from value in drm_fourcc.h ({}) for format {}" 132 .format(val, formats[fmt], fmt)) 133 del formats[fmt] 134 last_line = node.sourceline 135if last_line == None: 136 raise Exception("Expected at least one existing wl_shm.format entry") 137 138print("Adding {} formats to wayland.xml...".format(len(formats)), file=sys.stderr) 139 140# Append new formats 141new_wayland_proto = wayland_proto + ".new" 142with open(new_wayland_proto, "w+") as output_file, \ 143 open(wayland_proto) as input_file: 144 for i, l in enumerate(input_file.readlines()): 145 output_file.write(l) 146 if i + 1 == last_line: 147 for fmt, val in formats.items(): 148 output_file.write(' <entry name="{}" value="{}"' 149 .format(fmt, val)) 150 if fmt in descriptions: 151 output_file.write(' summary="{}"'.format(descriptions[fmt])) 152 output_file.write('/>\n') 153os.rename(new_wayland_proto, wayland_proto) 154