1#!/usr/bin/python2
2
3# (C) Copyright Zack Rusin 2005. All Rights Reserved.
4# Copyright (C) 2015 Intel Corporation
5# Copyright (C) 2015 Broadcom Corporation
6#
7# Permission is hereby granted, free of charge, to any person obtaining a
8# copy of this software and associated documentation files (the "Software"),
9# to deal in the Software without restriction, including without limitation
10# on the rights to use, copy, modify, merge, publish, distribute, sub
11# license, and/or sell copies of the Software, and to permit persons to whom
12# the Software is furnished to do so, subject to the following conditions:
13#
14# The above copyright notice and this permission notice (including the next
15# paragraph) shall be included in all copies or substantial portions of the
16# Software.
17#
18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
21# IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24# IN THE SOFTWARE.
25#
26# Authors:
27#    Zack Rusin <zack@kde.org>
28
29import argparse
30
31import license
32import gl_XML
33import xml.etree.ElementTree as ET
34import sys, getopt
35import re
36
37class PrintGlEnums(gl_XML.gl_print_base):
38
39    def __init__(self):
40        gl_XML.gl_print_base.__init__(self)
41
42        self.name = "gl_enums.py (from Mesa)"
43        self.license = license.bsd_license_template % ( \
44"""Copyright (C) 1999-2005 Brian Paul All Rights Reserved.""", "BRIAN PAUL")
45        # Mapping from enum value to (name, priority) tuples.
46        self.enum_table = {}
47        # Mapping from enum name to value
48        self.string_to_int = {}
49
50
51    def printRealHeader(self):
52        print '#include "main/glheader.h"'
53        print '#include "main/enums.h"'
54        print '#include "main/imports.h"'
55        print '#include "main/mtypes.h"'
56        print ''
57        print 'typedef struct PACKED {'
58        print '   uint32_t offset;'
59        print '   int n;'
60        print '} enum_elt;'
61        print ''
62        return
63
64    def print_code(self):
65        print """
66typedef int (*cfunc)(const void *, const void *);
67
68/**
69 * Compare a key enum value to an element in the \c enum_string_table_offsets array.
70 *
71 * \c bsearch always passes the key as the first parameter and the pointer
72 * to the array element as the second parameter.  We can elimiate some
73 * extra work by taking advantage of that fact.
74 *
75 * \param a  Pointer to the desired enum name.
76 * \param b  Pointer into the \c enum_string_table_offsets array.
77 */
78static int compar_nr( const int *a, enum_elt *b )
79{
80   return a[0] - b->n;
81}
82
83
84static char token_tmp[20];
85
86/**
87 * This function always returns a string. If the number is a valid enum, it
88 * returns the enum name. Otherwise, it returns a numeric string.
89 */
90const char *
91_mesa_enum_to_string(int nr)
92{
93   enum_elt *elt;
94
95   elt = bsearch(& nr, enum_string_table_offsets,
96                 ARRAY_SIZE(enum_string_table_offsets),
97                 sizeof(enum_string_table_offsets[0]),
98                 (cfunc) compar_nr);
99
100   if (elt != NULL) {
101      return &enum_string_table[elt->offset];
102   }
103   else {
104      /* this is not re-entrant safe, no big deal here */
105      _mesa_snprintf(token_tmp, sizeof(token_tmp) - 1, "0x%x", nr);
106      token_tmp[sizeof(token_tmp) - 1] = '\\0';
107      return token_tmp;
108   }
109}
110
111/**
112 * Primitive names
113 */
114static const char *prim_names[PRIM_MAX+3] = {
115   "GL_POINTS",
116   "GL_LINES",
117   "GL_LINE_LOOP",
118   "GL_LINE_STRIP",
119   "GL_TRIANGLES",
120   "GL_TRIANGLE_STRIP",
121   "GL_TRIANGLE_FAN",
122   "GL_QUADS",
123   "GL_QUAD_STRIP",
124   "GL_POLYGON",
125   "GL_LINES_ADJACENCY",
126   "GL_LINE_STRIP_ADJACENCY",
127   "GL_TRIANGLES_ADJACENCY",
128   "GL_TRIANGLE_STRIP_ADJACENCY",
129   "GL_PATCHES",
130   "outside begin/end",
131   "unknown state"
132};
133
134
135/* Get the name of an enum given that it is a primitive type.  Avoids
136 * GL_FALSE/GL_POINTS ambiguity and others.
137 */
138const char *
139_mesa_lookup_prim_by_nr(GLuint nr)
140{
141   if (nr < ARRAY_SIZE(prim_names))
142      return prim_names[nr];
143   else
144      return "invalid mode";
145}
146
147
148"""
149        return
150
151
152    def printBody(self, xml):
153        self.process_enums(xml)
154
155        sorted_enum_values = sorted(self.enum_table.keys())
156        string_offsets = {}
157        i = 0;
158        print '#if defined(__GNUC__)'
159        print '# define LONGSTRING __extension__'
160        print '#else'
161        print '# define LONGSTRING'
162        print '#endif'
163        print ''
164        print 'LONGSTRING static const char enum_string_table[] = {'
165        # We express the very long concatenation of enum strings as an array
166        # of characters rather than as a string literal to work-around MSVC's
167        # 65535 character limit.
168        for enum in sorted_enum_values:
169            (name, pri) = self.enum_table[enum]
170            print "  ",
171            for ch in name:
172                print "'%c'," % ch,
173            print "'\\0',"
174
175            string_offsets[ enum ] = i
176            i += len(name) + 1
177
178        print '};'
179        print ''
180
181
182        print 'static const enum_elt enum_string_table_offsets[%u] =' % (len(self.enum_table))
183        print '{'
184        for enum in sorted_enum_values:
185            (name, pri) = self.enum_table[enum]
186            print '   { %5u, 0x%08X }, /* %s */' % (string_offsets[enum], enum, name)
187        print '};'
188        print ''
189
190        self.print_code()
191        return
192
193    def add_enum_provider(self, name, priority):
194        value = self.string_to_int[name]
195
196        # We don't want the weird GL_SKIP_COMPONENTS1_NV enums.
197        if value < 0:
198            return
199        # We don't want the 64-bit GL_TIMEOUT_IGNORED "enums"
200        if value > 0xffffffff:
201            return
202
203        # We don't want bitfields in the enum-to-string table --
204        # individual bits have so many names, it's pointless.  Note
205        # that we check for power-of-two, since some getters have
206        # "_BITS" in their name, but none have a power-of-two enum
207        # number.
208        if not (value & (value - 1)) and '_BIT' in name:
209            return
210
211        # Also drop the GL_*_ATTRIB_BITS bitmasks.
212        if value == 0xffffffff:
213                return
214
215        if value in self.enum_table:
216            (n, p) = self.enum_table[value]
217            if priority < p:
218                self.enum_table[value] = (name, priority)
219        else:
220            self.enum_table[value] = (name, priority)
221
222    def process_extension(self, extension):
223        if extension.get('name').startswith('GL_ARB_'):
224            extension_prio = 400
225        elif extension.get('name').startswith('GL_EXT_'):
226            extension_prio = 600
227        else:
228            extension_prio = 800
229
230        for enum in extension.findall('require/enum'):
231            self.add_enum_provider(enum.get('name'), extension_prio)
232
233    def process_enums(self, xml):
234        # First, process the XML entries that define the hex values
235        # for all of the enum names.
236        for enum in xml.findall('enums/enum'):
237            name = enum.get('name')
238            value = int(enum.get('value'), base=16)
239
240            # If the same name ever maps to multiple values, that can
241            # confuse us.  GL_ACTIVE_PROGRAM_EXT is OK to lose because
242            # we choose GL_ACTIVE PROGRAM instead.
243            if name in self.string_to_int and name != "GL_ACTIVE_PROGRAM_EXT":
244                print "#error Renumbering {0} from {1} to {2}".format(name, self.string_to_int[name], value)
245
246            self.string_to_int[name] = value
247
248        # Now, process all of the API versions and extensions that
249        # provide enums, so we can decide what name to call any hex
250        # value.
251        for feature in xml.findall('feature'):
252            feature_name = feature.get('name')
253
254            # When an enum gets renamed in a newer version (generally
255            # because of some generalization of the functionality),
256            # prefer the newer name.  Also, prefer desktop GL names to
257            # ES.
258            m = re.match('GL_VERSION_([0-9])_([0-9])', feature_name)
259            if m:
260                feature_prio = 100 - int(m.group(1) + m.group(2))
261            else:
262                m = re.match('GL_ES_VERSION_([0-9])_([0-9])', feature_name)
263                if m:
264                    feature_prio = 200 - int(m.group(1) + m.group(2))
265                else:
266                    feature_prio = 200
267
268            for enum in feature.findall('require/enum'):
269                self.add_enum_provider(enum.get('name'), feature_prio)
270
271        for extension in xml.findall('extensions/extension'):
272            self.process_extension(extension)
273
274
275def _parser():
276    parser = argparse.ArgumentParser()
277    parser.add_argument('-f', '--input_file',
278                        required=True,
279                        help="Choose an xml file to parse.")
280    return parser.parse_args()
281
282
283def main():
284    args = _parser()
285    xml = ET.parse(args.input_file)
286
287    printer = PrintGlEnums()
288    printer.Print(xml)
289
290
291if __name__ == '__main__':
292    main()
293