1#!/usr/bin/python3
2#
3# python script to generate cdecl to stdcall wrappers for GL functions
4# adapted from genheaders.py
5#
6# Copyright (c) 2013 The Khronos Group Inc.
7#
8# Permission is hereby granted, free of charge, to any person obtaining a
9# copy of this software and/or associated documentation files (the
10# "Materials"), to deal in the Materials without restriction, including
11# without limitation the rights to use, copy, modify, merge, publish,
12# distribute, sublicense, and/or sell copies of the Materials, and to
13# permit persons to whom the Materials are furnished to do so, subject to
14# the following conditions:
15#
16# The above copyright notice and this permission notice shall be included
17# in all copies or substantial portions of the Materials.
18#
19# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25# MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
26
27import sys, time, pdb, string, cProfile
28from reg import *
29
30# Default input / log files
31errFilename = None
32diagFilename = 'diag.txt'
33regFilename = 'gl.xml'
34outFilename = 'gen_gl_wrappers.c'
35
36protect=True
37prefix="gl"
38preresolve=False
39wrapper=False
40shim=False
41thunk=False
42thunkdefs=False
43staticwrappers=False
44nodebug=False
45
46# list of WGL extension functions we use
47used_wgl_ext_fns = {key: 1 for key in [
48    "wglSwapIntervalEXT",
49    "wglGetExtensionsStringARB",
50    "wglDestroyPbufferARB",
51    "wglGetPbufferDCARB",
52    "wglReleasePbufferDCARB",
53    "wglCreatePbufferARB",
54    "wglMakeContextCurrentARB",
55    "wglChoosePixelFormatARB",
56    "wglGetPixelFormatAttribivARB",
57    "wglGetPixelFormatAttribivARB"
58]}
59
60if __name__ == '__main__':
61    i = 1
62    while (i < len(sys.argv)):
63        arg = sys.argv[i]
64        i = i + 1
65        if (arg == '-noprotect'):
66            print('Disabling inclusion protection in output headers', file=sys.stderr)
67            protect = False
68        elif (arg == '-registry'):
69            regFilename = sys.argv[i]
70            i = i+1
71            print('Using registry', regFilename, file=sys.stderr)
72        elif (arg == '-outfile'):
73            outFilename = sys.argv[i]
74            i = i+1
75        elif (arg == '-preresolve'):
76            preresolve=True
77        elif (arg == '-wrapper'):
78            wrapper=True
79        elif (arg == '-shim'):
80            shim=True
81        elif (arg == '-thunk'):
82            thunk=True
83        elif (arg == '-thunkdefs'):
84            thunkdefs=True
85        elif (arg == '-staticwrappers'):
86            staticwrappers=True
87        elif (arg == '-prefix'):
88            prefix = sys.argv[i]
89            i = i+1
90        elif (arg == '-nodebug'):
91            nodebug = True
92        elif (arg[0:1] == '-'):
93            print('Unrecognized argument:', arg, file=sys.stderr)
94            exit(1)
95
96print('Generating', outFilename, file=sys.stderr)
97
98# Load & parse registry
99reg = Registry()
100tree = etree.parse(regFilename)
101reg.loadElementTree(tree)
102
103if shim:
104    versions = '1\.[012]'
105else:
106    versions = '.*'
107
108genOpts = CGeneratorOptions(
109        apiname           = prefix,
110        profile           = 'compatibility',
111        versions          = versions,
112        emitversions      = versions,
113        defaultExtensions = prefix,                   # Default extensions for GL
114        protectFile       = protect,
115        protectFeature    = protect,
116        protectProto      = protect,
117        )
118
119# create error/warning & diagnostic files
120if (errFilename):
121    errWarn = open(errFilename,'w')
122else:
123    errWarn = sys.stderr
124diag = open(diagFilename, 'w')
125
126def ParseCmdRettype(cmd):
127    proto=noneStr(cmd.elem.find('proto'))
128    rettype=noneStr(proto.text)
129    if rettype.lower()!="void ":
130        plist = ([t for t in proto.itertext()])
131        rettype = ''.join(plist[:-1])
132    rettype=rettype.strip()
133    return rettype
134
135def ParseCmdParams(cmd):
136    params = cmd.elem.findall('param')
137    plist=[]
138    for param in params:
139        # construct the formal parameter definition from ptype and name
140        # elements, also using any text found around these in the
141        # param element, in the order it appears in the document
142        paramtype = ''
143        # also extract the formal parameter name from the name element
144        paramname = ''
145        for t in param.iter():
146            if t.tag == 'ptype' or t.tag == 'param':
147                paramtype = paramtype + noneStr(t.text)
148            if t.tag == 'name':
149                paramname = t.text + '_'
150                paramtype = paramtype + ' ' + paramname
151            if t.tail is not None:
152                paramtype = paramtype + t.tail.strip()
153        plist.append((paramtype, paramname))
154    return plist
155
156class PreResolveOutputGenerator(OutputGenerator):
157    def __init__(self,
158                 errFile = sys.stderr,
159                 warnFile = sys.stderr,
160                 diagFile = sys.stdout):
161        OutputGenerator.__init__(self, errFile, warnFile, diagFile)
162        self.wrappers={}
163    def beginFile(self, genOpts):
164        self.outFile.write('/* Automatically generated from %s - DO NOT EDIT */\n\n'%regFilename)
165    def endFile(self):
166        self.outFile.write('\nvoid ' + prefix + 'ResolveExtensionProcs(void)\n{\n')
167        for funcname in self.wrappers.keys():
168            self.outFile.write( '  PRERESOLVE(PFN' + funcname.upper() + 'PROC, "' + funcname + '");\n')
169        self.outFile.write('}\n\n')
170    def beginFeature(self, interface, emit):
171        OutputGenerator.beginFeature(self, interface, emit)
172    def endFeature(self):
173        OutputGenerator.endFeature(self)
174    def genType(self, typeinfo, name):
175        OutputGenerator.genType(self, typeinfo, name)
176    def genEnum(self, enuminfo, name):
177        OutputGenerator.genEnum(self, enuminfo, name)
178    def genCmd(self, cmd, name):
179        OutputGenerator.genCmd(self, cmd, name)
180
181        if prefix == 'wgl' and not name in used_wgl_ext_fns:
182            return
183
184        self.outFile.write('RESOLVE_DECL(PFN' + name.upper() + 'PROC);\n')
185        self.wrappers[name]=1
186
187class WrapperOutputGenerator(OutputGenerator):
188    def __init__(self,
189                 errFile = sys.stderr,
190                 warnFile = sys.stderr,
191                 diagFile = sys.stdout):
192        OutputGenerator.__init__(self, errFile, warnFile, diagFile)
193    def beginFile(self, genOpts):
194        self.outFile.write('/* Automatically generated from %s - DO NOT EDIT */\n\n'%regFilename)
195    def endFile(self):
196        pass
197    def beginFeature(self, interface, emit):
198        OutputGenerator.beginFeature(self, interface, emit)
199        self.OldVersion = self.featureName.startswith('GL_VERSION_1_0') or self.featureName.startswith('GL_VERSION_1_1')
200    def endFeature(self):
201        OutputGenerator.endFeature(self)
202    def genType(self, typeinfo, name):
203        OutputGenerator.genType(self, typeinfo, name)
204    def genEnum(self, enuminfo, name):
205        OutputGenerator.genEnum(self, enuminfo, name)
206    def genCmd(self, cmd, name):
207        OutputGenerator.genCmd(self, cmd, name)
208
209        if prefix == 'wgl' and not name in used_wgl_ext_fns:
210            return
211
212        rettype=ParseCmdRettype(cmd)
213
214        if staticwrappers: self.outFile.write("static ")
215        self.outFile.write("%s %sWrapper("%(rettype, name))
216        plist=ParseCmdParams(cmd)
217        Comma=""
218        if len(plist):
219            for ptype, pname in plist:
220                self.outFile.write("%s%s"%(Comma, ptype))
221                Comma=", "
222        else:
223            self.outFile.write("void")
224
225        self.outFile.write(")\n{\n")
226
227        # for GL 1.0 and 1.1 functions, generate stdcall wrappers which call the function directly
228        if self.OldVersion:
229            if not nodebug:
230                self.outFile.write('  if (glxWinDebugSettings.enable%scallTrace) ErrorF("%s\\n");\n'%(prefix.upper(), name))
231                self.outFile.write("  glWinDirectProcCalls++;\n")
232                self.outFile.write("\n")
233
234            if rettype.lower()=="void":
235                self.outFile.write("  %s( "%(name))
236            else:
237                self.outFile.write("  return %s( "%(name))
238
239            Comma=""
240            for ptype, pname in plist:
241                self.outFile.write("%s%s"%(Comma, pname))
242                Comma=", "
243
244        # for GL 1.2+ functions, generate stdcall wrappers which use wglGetProcAddress()
245        else:
246            if rettype.lower()=="void":
247                self.outFile.write('  RESOLVE(PFN%sPROC, "%s");\n'%(name.upper(), name))
248
249                if not nodebug:
250                    self.outFile.write("\n")
251                    self.outFile.write('  if (glxWinDebugSettings.enable%scallTrace) ErrorF("%s\\n");\n'%(prefix.upper(), name))
252                    self.outFile.write("\n")
253
254                self.outFile.write("  RESOLVED_PROC(PFN%sPROC)( """%(name.upper()))
255            else:
256                self.outFile.write('  RESOLVE_RET(PFN%sPROC, "%s", FALSE);\n'%(name.upper(), name))
257
258                if not nodebug:
259                    self.outFile.write("\n")
260                    self.outFile.write('  if (glxWinDebugSettings.enable%scallTrace) ErrorF("%s\\n");\n'%(prefix.upper(), name))
261                    self.outFile.write("\n")
262
263                self.outFile.write("  return RESOLVED_PROC(PFN%sPROC)("%(name.upper()))
264
265            Comma=""
266            for ptype, pname in plist:
267                self.outFile.write("%s%s"%(Comma, pname))
268                Comma=", "
269        self.outFile.write(" );\n}\n\n")
270
271class ThunkOutputGenerator(OutputGenerator):
272    def __init__(self,
273                 errFile = sys.stderr,
274                 warnFile = sys.stderr,
275                 diagFile = sys.stdout):
276        OutputGenerator.__init__(self, errFile, warnFile, diagFile)
277    def beginFile(self, genOpts):
278        self.outFile.write('/* Automatically generated from %s - DO NOT EDIT */\n\n'%regFilename)
279    def endFile(self):
280        pass
281    def beginFeature(self, interface, emit):
282        OutputGenerator.beginFeature(self, interface, emit)
283        self.OldVersion = (self.featureName in ['GL_VERSION_1_0', 'GL_VERSION_1_1'])
284    def endFeature(self):
285        OutputGenerator.endFeature(self)
286    def genType(self, typeinfo, name):
287        OutputGenerator.genType(self, typeinfo, name)
288    def genEnum(self, enuminfo, name):
289        OutputGenerator.genEnum(self, enuminfo, name)
290    def genCmd(self, cmd, name):
291        OutputGenerator.genCmd(self, cmd, name)
292
293        rettype=ParseCmdRettype(cmd)
294        self.outFile.write("%s %sWrapper("%(rettype, name))
295        plist=ParseCmdParams(cmd)
296
297        Comma=""
298        if len(plist):
299            for ptype, pname in plist:
300                self.outFile.write("%s%s"%(Comma, ptype))
301                Comma=", "
302        else:
303            self.outFile.write("void")
304
305        self.outFile.write(")\n{\n")
306
307        # for GL 1.0 and 1.1 functions, generate stdcall thunk wrappers which call the function directly
308        if self.OldVersion:
309            if rettype.lower()=="void":
310                self.outFile.write("  %s( "%(name))
311            else:
312                self.outFile.write("  return %s( "%(name))
313
314            Comma=""
315            for ptype, pname in plist:
316                self.outFile.write("%s%s"%(Comma, pname))
317                Comma=", "
318
319        # for GL 1.2+ functions, generate wrappers which use wglGetProcAddress()
320        else:
321            if rettype.lower()=="void":
322                self.outFile.write('  RESOLVE(PFN%sPROC, "%s");\n'%(name.upper(), name))
323                self.outFile.write("  RESOLVED_PROC(PFN%sPROC)( """%(name.upper()))
324            else:
325                self.outFile.write('  RESOLVE_RET(PFN%sPROC, "%s", FALSE);\n'%(name.upper(), name))
326                self.outFile.write("  return RESOLVED_PROC(PFN%sPROC)("%(name.upper()))
327
328            Comma=""
329            for ptype, pname in plist:
330                self.outFile.write("%s%s"%(Comma, pname))
331                Comma=", "
332        self.outFile.write(" );\n}\n\n")
333
334class ThunkDefsOutputGenerator(OutputGenerator):
335    def __init__(self,
336                 errFile = sys.stderr,
337                 warnFile = sys.stderr,
338                 diagFile = sys.stdout):
339        OutputGenerator.__init__(self, errFile, warnFile, diagFile)
340    def beginFile(self, genOpts):
341        self.outFile.write("EXPORTS\n"); # this must be the first line for libtool to realize this is a .def file
342        self.outFile.write('; Automatically generated from %s - DO NOT EDIT\n\n'%regFilename)
343    def endFile(self):
344        pass
345    def beginFeature(self, interface, emit):
346        OutputGenerator.beginFeature(self, interface, emit)
347    def endFeature(self):
348        OutputGenerator.endFeature(self)
349    def genType(self, typeinfo, name):
350        OutputGenerator.genType(self, typeinfo, name)
351    def genEnum(self, enuminfo, name):
352        OutputGenerator.genEnum(self, enuminfo, name)
353    def genCmd(self, cmd, name):
354        OutputGenerator.genCmd(self, cmd, name)
355
356        # export the wrapper function with the name of the function it wraps
357        self.outFile.write("%s = %sWrapper\n"%(name, name))
358
359class ShimOutputGenerator(OutputGenerator):
360    def __init__(self,
361                 errFile = sys.stderr,
362                 warnFile = sys.stderr,
363                 diagFile = sys.stdout):
364        OutputGenerator.__init__(self, errFile, warnFile, diagFile)
365    def beginFile(self, genOpts):
366        self.outFile.write('/* Automatically generated from %s - DO NOT EDIT */\n\n'%regFilename)
367    def endFile(self):
368        pass
369    def beginFeature(self, interface, emit):
370        OutputGenerator.beginFeature(self, interface, emit)
371        self.OldVersion = (self.featureName in ['GL_VERSION_1_0', 'GL_VERSION_1_1', 'GL_VERSION_1_2', 'GL_ARB_imaging', 'GL_ARB_multitexture', 'GL_ARB_texture_compression'])
372    def endFeature(self):
373        OutputGenerator.endFeature(self)
374    def genType(self, typeinfo, name):
375        OutputGenerator.genType(self, typeinfo, name)
376    def genEnum(self, enuminfo, name):
377        OutputGenerator.genEnum(self, enuminfo, name)
378    def genCmd(self, cmd, name):
379        OutputGenerator.genCmd(self, cmd, name)
380
381        if not self.OldVersion:
382            return
383
384        # for GL functions which are in the ABI, generate a shim which calls the function via GetProcAddress
385        rettype=ParseCmdRettype(cmd)
386        self.outFile.write("%s %s("%(rettype, name))
387        plist=ParseCmdParams(cmd)
388
389        Comma=""
390        if len(plist):
391            for ptype, pname in plist:
392                self.outFile.write("%s%s"%(Comma, ptype))
393                Comma=", "
394        else:
395            self.outFile.write("void")
396
397        self.outFile.write(")\n{\n")
398
399        self.outFile.write('  typedef %s (* PFN%sPROC)(' % (rettype, name.upper()))
400
401        if len(plist):
402            Comma=""
403            for ptype, pname in plist:
404                self.outFile.write("%s %s"%(Comma, ptype))
405                Comma=", "
406        else:
407            self.outFile.write("void")
408
409        self.outFile.write(');\n')
410
411        if rettype.lower()=="void":
412            self.outFile.write('  RESOLVE(PFN%sPROC, "%s");\n'%(name.upper(), name))
413            self.outFile.write('  RESOLVED_PROC(')
414        else:
415            self.outFile.write('  RESOLVE_RET(PFN%sPROC, "%s", 0);\n'%(name.upper(), name))
416            self.outFile.write('  return RESOLVED_PROC(')
417
418        Comma=""
419        for ptype, pname in plist:
420            self.outFile.write("%s%s"%(Comma, pname))
421            Comma=", "
422
423        self.outFile.write(" );\n}\n\n")
424
425def genHeaders():
426    outFile = open(outFilename,"w")
427
428    if preresolve:
429        gen = PreResolveOutputGenerator(errFile=errWarn,
430                                        warnFile=errWarn,
431                                        diagFile=diag)
432        gen.outFile=outFile
433        reg.setGenerator(gen)
434        reg.apiGen(genOpts)
435
436    if wrapper:
437        gen = WrapperOutputGenerator(errFile=errWarn,
438                                     warnFile=errWarn,
439                                     diagFile=diag)
440        gen.outFile=outFile
441        reg.setGenerator(gen)
442        reg.apiGen(genOpts)
443
444    if shim:
445        gen = ShimOutputGenerator(errFile=errWarn,
446                                  warnFile=errWarn,
447                                  diagFile=diag)
448        gen.outFile=outFile
449        reg.setGenerator(gen)
450        reg.apiGen(genOpts)
451
452    if thunk:
453        gen = ThunkOutputGenerator(errFile=errWarn,
454                                   warnFile=errWarn,
455                                   diagFile=diag)
456        gen.outFile=outFile
457        reg.setGenerator(gen)
458        reg.apiGen(genOpts)
459
460
461    if thunkdefs:
462        gen = ThunkDefsOutputGenerator(errFile=errWarn,
463                                       warnFile=errWarn,
464                                       diagFile=diag)
465        gen.outFile=outFile
466        reg.setGenerator(gen)
467        reg.apiGen(genOpts)
468
469    outFile.close()
470
471genHeaders()
472