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