1#!/usr/bin/python
2#
3# Comedy python script to generate cdecl to stdcall wrappers for GL functions
4#
5# This is designed to operate on OpenGL spec files from
6# http://www.opengl.org/registry/api/
7#
8#
9# Copyright (c) Jon TURNEY 2009
10#
11# Permission is hereby granted, free of charge, to any person obtaining a
12# copy of this software and associated documentation files (the "Software"),
13# to deal in the Software without restriction, including without limitation
14# the rights to use, copy, modify, merge, publish, distribute, sublicense,
15# and/or sell copies of the Software, and to permit persons to whom the
16# Software is furnished to do so, subject to the following conditions:
17#
18# The above copyright notice and this permission notice shall be included in
19# all copies or substantial portions of the Software.
20#
21# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24# THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
25# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
26# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27# DEALINGS IN THE SOFTWARE.
28#
29# Except as contained in this notice, the name(s) of the above copyright
30# holders shall not be used in advertising or otherwise to promote the sale,
31# use or other dealings in this Software without prior written authorization.
32#
33
34import sys
35import re
36import getopt
37
38dispatchheader = ''
39prefix = 'gl'
40preresolve = False
41staticwrappers = False
42
43opts, args = getopt.getopt(sys.argv[1:], "", ['spec=', 'typemap=', 'dispatch-header=', 'prefix=', 'preresolve', 'staticwrappers' ])
44
45for o,a in opts:
46        if o == '--typemap' :
47                typemapfile = a
48        elif o == '--dispatch-header' :
49                dispatchheader = a
50        elif o == '--spec' :
51                specfile = a
52        elif o == '--prefix' :
53                prefix = a
54        elif o == '--preresolve' :
55                preresolve = True
56        elif o == '--staticwrappers' :
57                staticwrappers = True
58
59#
60# look for all the SET_ macros in dispatch.h, this is the set of functions
61# we need to generate
62#
63
64dispatch = {}
65
66if dispatchheader :
67        fh = open(dispatchheader)
68        dispatchh = fh.readlines()
69
70        dispatch_regex = re.compile(r'#define\sSET_(\S*)\(')
71
72        for line in dispatchh :
73                line = line.strip()
74                m1 = dispatch_regex.search(line)
75
76                if m1 :
77                        dispatch[m1.group(1)] = 1
78
79        del dispatch['by_offset']
80
81#
82# read the typemap .tm file
83#
84
85typemap = {}
86
87fh = open(typemapfile)
88tm = fh.readlines()
89
90typemap_regex = re.compile(r'#define\sSET_(\S*)\(')
91
92for line in tm :
93        # ignore everything after a '#' as a comment
94        hash = line.find('#')
95        if hash != -1 :
96                line = line[:hash-1]
97
98        # ignore blank lines
99        if line.startswith('#') or len(line) == 0 :
100                continue
101
102        l = line.split(',')
103        typemap[l[0]] = l[3].strip()
104
105# interestingly, * is not a C type
106if typemap['void'] == '*' :
107        typemap['void'] = 'void'
108
109#
110# crudely parse the .spec file
111#
112
113r1 = re.compile(r'\t(\S*)\s+(\S*.*)')
114r2 = re.compile(r'(.*)\((.*)\)')
115r3 = re.compile(r'glWindowPos.*MESA')
116r4 = re.compile(r'gl.*Program(s|)NV')
117r5 = re.compile(r'glGetVertexAttribfvNV')
118
119wrappers = {}
120
121fh = open(specfile)
122glspec = fh.readlines()
123param_count = 0
124
125for line in glspec :
126        line = line.rstrip()
127
128        # ignore everything after a '#' as a comment
129        hash = line.find('#')
130        if hash != -1 :
131                line = line[:hash-1]
132
133        # ignore blank lines
134        if line.startswith('#') or len(line) == 0 :
135                continue
136
137        # lines containing ':' aren't intersting to us
138        if line.count(':') != 0 :
139                continue
140
141        # attributes of each function follow the name, indented by a tab
142        if not line.startswith('\t') :
143                m1 = r2.search(line)
144                if m1 :
145                        function = m1.group(1)
146                        arglist_use = m1.group(2)
147                        wrappers[function] = {}
148
149                        # near and far might be reserved words or macros so can't be used as formal parameter names
150                        arglist_use = arglist_use.replace('near','zNear')
151                        arglist_use = arglist_use.replace('far','zFar')
152
153                        wrappers[function]['arglist_use'] = arglist_use
154                        param_count = 0
155        else :
156                m1 = r1.search(line)
157                if m1 :
158                        attribute = m1.group(1)
159                        value = m1.group(2)
160
161                        # make param attributes unique and ordered
162                        if attribute == 'param' :
163                                attribute = 'param' + '%02d' % param_count
164                                param_count += 1
165
166                        wrappers[function][attribute] = value
167
168#
169# now emit code
170#
171
172print '/* Automatically generated by ' + sys.argv[0] + ' DO NOT EDIT */'
173print '/* from ' + specfile + ' and typemap ' + typemapfile + ' */'
174print ''
175
176#
177# if required, emit code for non-lazy function resolving
178#
179
180if preresolve :
181        for w in sorted(wrappers.keys()) :
182                funcname = prefix + w
183                print 'RESOLVE_DECL(PFN' + funcname.upper() + 'PROC);'
184
185        print ''
186        print 'void ' + prefix + 'ResolveExtensionProcs(void)'
187        print '{'
188
189        for w in sorted(wrappers.keys()) :
190                funcname = prefix + w
191                print '  PRERESOLVE(PFN' + funcname.upper() + 'PROC, "' + funcname + '");'
192
193        print '}\n'
194
195#
196# now emit the wrappers
197# for GL 1.0 and 1.1 functions, generate stdcall wrappers which call the function directly
198# for GL 1.2+ functions, generate wrappers which use wglGetProcAddress()
199#
200
201for w in sorted(wrappers.keys()) :
202
203        funcname = prefix + w
204        returntype = wrappers[w]['return']
205        if returntype != 'void' :
206                returntype = typemap[returntype]
207
208        # Avoid generating wrappers which aren't referenced by the dispatch table
209        if dispatchheader and not dispatch.has_key(w) :
210                print '/* No wrapper for ' + funcname + ', not in dispatch table */'
211                continue
212
213        # manufacture arglist
214        # if no param attributes were found, it should be 'void'
215        al = []
216        for k in sorted(wrappers[w].keys()) :
217                if k.startswith('param') :
218                        l = wrappers[w][k].split()
219
220                        # near and far might be reserved words or macros so can't be used as formal parameter names
221                        l[0] = l[0].replace('near','zNear')
222                        l[0] = l[0].replace('far','zFar')
223
224                        if l[2] == 'in' :
225                                if l[3] == 'array' :
226                                        arg = 'const ' + typemap[l[1]] + ' *' + l[0]
227                                else :
228                                        arg = typemap[l[1]] + ' ' + l[0]
229                        elif l[2] == 'out' :
230                                arg = typemap[l[1]] + ' *' + l[0]
231
232                        al.append(arg)
233
234        if len(al) == 0 :
235                arglist = 'void'
236        else:
237                arglist  = ', '.join(al)
238
239        if wrappers[w]['category'].startswith('VERSION_1_0') or wrappers[w]['category'].startswith('VERSION_1_1') :
240                if staticwrappers :
241                        print 'static',
242                print returntype + ' ' + funcname + 'Wrapper(' + arglist + ')'
243                print '{'
244                print '  if (glxWinDebugSettings.enable' + prefix.upper() + 'callTrace) ErrorF("'+ funcname + '\\n");'
245                print '  glWinDirectProcCalls++;'
246                if returntype.lower() == 'void' :
247                        print '  ' +  funcname + '(',
248                else :
249                        print ' /* returntype was ' + returntype.lower() + '*/'
250                        print '  return ' +  funcname + '(',
251
252                if arglist != 'void' :
253                        print wrappers[w]['arglist_use'],
254
255                print ');'
256                print "}\n"
257        else:
258                if staticwrappers :
259                        print 'static',
260                print returntype + ' ' + funcname + 'Wrapper(' + arglist + ')'
261                print '{'
262
263                stringname = funcname
264
265#
266# special case: Windows OpenGL implementations are far more likely to have GL_ARB_window_pos than GL_MESA_window_pos,
267# so arrange for the wrapper to use the ARB strings to find functions...
268#
269
270                m2 = r3.search(funcname)
271                if m2 :
272                        stringname = stringname.replace('MESA','ARB')
273
274#
275# special case: likewise, implementations are more likely to have GL_ARB_vertex_program than GL_NV_vertex_program,
276# especially if they are not NV implementations, so arrange for the wrapper to use ARB strings to find functions
277#
278
279                m3 = r4.search(funcname)
280                if m3 :
281                        stringname = stringname.replace('NV','ARB')
282                m4 = r5.search(funcname)
283                if m4 :
284                        stringname = stringname.replace('NV','ARB')
285
286                pfntypename = 'PFN' + funcname.upper() + 'PROC'
287
288                if returntype.lower() == 'void' :
289                        print '  RESOLVE(' + pfntypename + ', "' + stringname + '");'
290                        print '  if (glxWinDebugSettings.enable' + prefix.upper() + 'callTrace) ErrorF("'+ funcname + '\\n");'
291                        print '  RESOLVED_PROC(' + pfntypename + ')(',
292                else :
293                        print '  RESOLVE_RET(' + pfntypename + ', "' + stringname + '", FALSE);'
294                        print '  if (glxWinDebugSettings.enable' + prefix.upper() + 'callTrace) ErrorF("'+ funcname + '\\n");'
295                        print '  return RESOLVED_PROC(' + pfntypename + ')(',
296
297                if arglist != 'void' :
298                        print wrappers[w]['arglist_use'],
299
300                print ');'
301                print "}\n"
302
303
304# generate function to setup the dispatch table, which sets each
305# dispatch table entry to point to it's wrapper function
306# (assuming we were able to make one)
307
308if dispatchheader :
309        print 'void glWinSetupDispatchTable(void)'
310        print '{'
311        print '  struct _glapi_table *disp = _glapi_get_dispatch();'
312
313        for d in sorted(dispatch.keys()) :
314                if wrappers.has_key(d) :
315                        print '  SET_'+ d + '(disp, ' + prefix + d + 'Wrapper);'
316                else :
317                        print '#warning  No wrapper for ' + prefix + d + ' !'
318
319        print '}'
320