Skip to content

Commit 07b2540

Browse files
committed
Basic implementation.
Need to find a way to run the generator somehow, but in the meantime, just run gen_from_xml.rb yourself.
1 parent c3d1a9a commit 07b2540

File tree

7 files changed

+350
-8
lines changed

7 files changed

+350
-8
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ tmp
1616
.yardoc
1717
_yardoc
1818
doc/
19+
lib/opengl/gl_enums.rb
20+
lib/opengl/gl_commands.rb
21+
gl.xml

ext/opengl/extconf.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
require 'mkmf'
2+
3+
create_makefile('opengl/opengl_stub')

ext/opengl/opengl_stub.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include "ruby.h"
2+
3+
4+
static VALUE plat_is_apple(VALUE self)
5+
{
6+
#if defined(__APPLE__)
7+
return Qtrue;
8+
#else
9+
return Qfalse;
10+
#endif
11+
}
12+
13+
14+
static VALUE plat_is_windows(VALUE self)
15+
{
16+
#if defined(_WIN32) || defined(__MINGW32__) || defined(__CYGWIN__)
17+
return Qtrue;
18+
#else
19+
return Qfalse;
20+
#endif
21+
}
22+
23+
24+
static VALUE plat_is_unix(VALUE self)
25+
{
26+
#if defined(__unix) || defined(__unix__) || defined(unix) || defined(__APPLE__)
27+
return Qtrue;
28+
#else
29+
return Qfalse;
30+
#endif
31+
}
32+
33+
34+
static VALUE plat_is_linux(VALUE self)
35+
{
36+
#if defined(__linux__) || defined(linux) || defined(__linux)
37+
return Qtrue;
38+
#else
39+
return Qfalse;
40+
#endif
41+
}
42+
43+
44+
void Init_opengl_stub(void)
45+
{
46+
VALUE module = rb_define_module("GlSym");
47+
rb_define_singleton_method(module, "apple?", plat_is_apple, 0);
48+
rb_define_singleton_method(module, "windows?", plat_is_windows, 0);
49+
rb_define_singleton_method(module, "unix?", plat_is_unix, 0);
50+
rb_define_singleton_method(module, "linux?", plat_is_linux, 0);
51+
}

gen_from_xml.rb

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
#!/usr/bin/env ruby -w
2+
3+
require 'nokogiri'
4+
require 'open-uri'
5+
require 'fiddle'
6+
7+
KHRONOS_GL_XML = 'https://cvs.khronos.org/svn/repos/ogl/trunk/doc/registry/public/api/gl.xml'
8+
9+
if !File.exist?('gl.xml')
10+
puts "gl.xml doesn't exist: downloading <#{KHRONOS_GL_XML}>..."
11+
curl_pid = Process.spawn("curl --output 'gl.xml' '#{KHRONOS_GL_XML}'")
12+
Process.wait(curl_pid)
13+
if !$?.exited? || !$?.success?
14+
puts "Download failed. Try again."
15+
exit 1
16+
end
17+
puts "Download complete."
18+
end
19+
20+
# name => String
21+
# value => String
22+
# alias => nil | String
23+
GLEnum = Struct.new(:name, :value, :alias)
24+
25+
# name => String
26+
# type => String
27+
# core => String (substring of type or nil)
28+
GLParam = Struct.new(:name, :type, :core)
29+
30+
# name => String
31+
# type => GLParam (name -> nil)
32+
# parameters => [GLParam]
33+
GLCommand = Struct.new(:name, :type, :parameters)
34+
35+
# command_suffix => String
36+
# enum_suffix => String
37+
GLVendor = Struct.new(:command_suffix, :enum_suffix)
38+
39+
# Set to false to generate bindings for non-core profile functions/enums
40+
GEN_GL3_AND_UP = true
41+
42+
TYPE_MAPPINGS = {
43+
"void" => 'Fiddle::TYPE_VOID',
44+
'GLvoid' => 'Fiddle::TYPE_VOID',
45+
'GLenum' => 'Fiddle::TYPE_INT',
46+
'GLboolean' => 'Fiddle::TYPE_CHAR',
47+
'GLbitfield' => 'Fiddle::TYPE_INT',
48+
'GLbyte' => 'Fiddle::TYPE_CHAR',
49+
'GLshort' => 'Fiddle::TYPE_SHORT',
50+
'GLint' => 'Fiddle::TYPE_INT',
51+
'GLclampx' => 'Fiddle::TYPE_INT',
52+
'GLubyte' => 'Fiddle::TYPE_CHAR',
53+
'GLushort' => 'Fiddle::TYPE_SHORT',
54+
'GLuint' => 'Fiddle::TYPE_INT',
55+
'GLsizei' => 'Fiddle::TYPE_INT',
56+
'GLfloat' => 'Fiddle::TYPE_FLOAT',
57+
'GLclampf' => 'Fiddle::TYPE_FLOAT',
58+
'GLdouble' => 'Fiddle::TYPE_DOUBLE',
59+
'GLclampd' => 'Fiddle::TYPE_DOUBLE',
60+
'GLchar' => 'Fiddle::TYPE_CHAR',
61+
'GLcharARB' => 'Fiddle::TYPE_CHAR',
62+
'GLhandleARB' => 'Fiddle::TYPE_UINTPTR_T',
63+
'GLhalfARB' => 'Fiddle::TYPE_SHORT',
64+
'GLhalf' => 'Fiddle::TYPE_SHORT',
65+
'GLfixed' => 'Fiddle::TYPE_INT',
66+
'GLintptr' => 'Fiddle::TYPE_PTRDIFF_T',
67+
'GLsizeiptr' => 'Fiddle::TYPE_PTRDIFF_T',
68+
'GLint64' => 'Fiddle::TYPE_LONG_LONG',
69+
'GLuint64' => 'Fiddle::TYPE_LONG_LONG',
70+
'GLintptrARB' => 'Fiddle::TYPE_PTRDIFF_T',
71+
'GLsizeiptrARB' => 'Fiddle::TYPE_PTRDIFF_T',
72+
'GLint64EXT' => 'Fiddle::TYPE_LONG_LONG',
73+
'GLuint64EXT' => 'Fiddle::TYPE_LONG_LONG',
74+
'GLsync' => 'Fiddle::TYPE_VOIDP',
75+
'GLhalfNV' => 'Fiddle::TYPE_SHORT',
76+
'GLvdpauSurfaceNV' => 'Fiddle::TYPE_PTRDIFF_T',
77+
'GLDEBUGPROC' => 'Fiddle::TYPE_VOIDP',
78+
'GLDEBUGPROCARB' => 'Fiddle::TYPE_VOIDP',
79+
'GLDEBUGPROCKHR' => 'Fiddle::TYPE_VOIDP',
80+
'GLDEBUGPROCAMD' => 'Fiddle::TYPE_VOIDP'
81+
}
82+
83+
SELECT_ENUMS_XPATH = 'registry/enums/enum[@name and @value and (not(@api) or not(starts-with(@api, "gles")))]'
84+
SELECT_COMMANDS_XPATH = 'registry/commands/command'
85+
SELECT_FEATURES_XPATH = 'registry/*[(self::feature or self::extension) and (not(starts-with(@api,"gles")) or not(starts-with(@supported, "gles")))]/require/*[self::command|self::enum]'
86+
SELECT_REMOVES_XPATH = 'registry/*[(self::feature or self::extension) and (not(starts-with(@api,"gles")) or not(starts-with(@supported, "gles")))]/remove[@profile="core"]/*[self::command|self::enum]'
87+
SELECT_TYPEDEFS_XPATH = 'registry/types/type[(not(@api) or not(starts-with(@api, "gles"))) and (not(@name) or not(starts-with(@name, "khr")))]'
88+
SELECT_TYPE_XPATH = 'ptype/text()|text()'
89+
90+
def fiddle_type(param)
91+
if param.type.end_with? '*'
92+
'Fiddle::VOIDP'
93+
elsif TYPE_MAPPINGS.include?(param.core)
94+
TYPE_MAPPINGS[param.core]
95+
else
96+
raise "Unrecognized type: #{param.core}"
97+
end
98+
end
99+
100+
def get_type(from)
101+
from.inner_text.chomp(from.at('name').text)
102+
end
103+
104+
def get_enums(document)
105+
enums = {}
106+
document.xpath(SELECT_ENUMS_XPATH).each {
107+
|gl_enum|
108+
109+
enum_name = gl_enum['name']
110+
enum_value = gl_enum['value']
111+
enum_alias = gl_enum['alias']
112+
113+
enums[enum_name] = GLEnum.new(enum_name, enum_value, enum_alias)
114+
}
115+
enums
116+
end
117+
118+
def get_commands(document)
119+
commands = {}
120+
document.xpath(SELECT_COMMANDS_XPATH).each {
121+
|gl_command|
122+
123+
proto = gl_command.at('proto')
124+
name = proto.at('name').text
125+
params = []
126+
gl_command.xpath('param').each {
127+
|gl_param|
128+
ptype = gl_param.at('ptype')
129+
full_type = get_type(gl_param)
130+
params << GLParam.new(gl_param.at('name').text, full_type, ptype ? ptype.text : full_type.strip)
131+
}
132+
133+
ptype = proto.at('ptype')
134+
full_return_type = get_type(proto)
135+
return_type = GLParam.new(nil, full_return_type, ptype ? ptype.text : full_return_type.strip)
136+
137+
commands[name] = GLCommand.new(name, return_type, params)
138+
}
139+
commands
140+
end
141+
142+
def generate_binding_impl(document)
143+
filtered_commands = {}
144+
filtered_enums = {}
145+
146+
begin
147+
gl_commands = get_commands(document)
148+
gl_enums = get_enums(document)
149+
150+
document.xpath(SELECT_FEATURES_XPATH).each {
151+
|feature|
152+
feature_name = feature['name']
153+
feature_kind = feature.name
154+
case feature_kind
155+
when 'enum'
156+
filtered_enums[feature_name] = gl_enums[feature_name]
157+
158+
when 'command'
159+
filtered_commands[feature_name] = gl_commands[feature_name]
160+
161+
else
162+
Raise "Unrecognized feature kind"
163+
end
164+
}
165+
166+
if GEN_GL3_AND_UP
167+
document.xpath(SELECT_REMOVES_XPATH).each {
168+
|feature|
169+
feature_name = feature['name']
170+
feature_kind = feature.name
171+
case feature_kind
172+
when 'enum'
173+
filtered_enums.delete feature_name
174+
175+
when 'command'
176+
filtered_commands.delete feature_name
177+
178+
else
179+
Raise "Unrecognized feature kind"
180+
end
181+
}
182+
end
183+
end
184+
185+
enum_name_length = filtered_enums.map { |k, enum| enum.name.length }.max
186+
prefix = document.url.chomp(File.extname(document.url))
187+
188+
File.open("lib/opengl/#{prefix}_enums.rb", 'w') {
189+
|io|
190+
191+
io.puts <<-EOS
192+
module GlEnums
193+
EOS
194+
195+
filtered_enums.select { |name, enum| enum.alias.nil? }.each {
196+
|name, enum|
197+
io.puts " #{name.ljust(enum_name_length)} = #{enum.value}"
198+
}
199+
200+
filtered_enums.select { |name, enum| !enum.alias.nil? }.each {
201+
|name, enum|
202+
if !filtered_enums[enum.alias]
203+
prefixed_alias = "GL_#{enum.alias}"
204+
if filtered_enums[prefixed_alias]
205+
enum.alias = prefixed_alias
206+
else
207+
next if enum.value.nil?
208+
enum.alias = enum.value
209+
end
210+
end
211+
212+
213+
io.puts " #{name.ljust(enum_name_length)} = #{enum.alias}"
214+
}
215+
216+
io.puts 'end # module GlEnums'
217+
}
218+
219+
File.open("lib/opengl/#{prefix}_commands.rb", 'w') {
220+
|io|
221+
222+
io.puts <<-EOS
223+
require 'fiddle'
224+
require 'opengl/gl_sym'
225+
226+
module GlCommands
227+
EOS
228+
229+
filtered_commands.each {
230+
|name, cmd|
231+
io.puts " @@#{name} = nil"
232+
}
233+
234+
filtered_commands.each {
235+
|name, cmd|
236+
# Put a _ on the end of each argument to avoid conflicts with reserved words
237+
param_string = cmd.parameters.map { |p| "#{p.name}_" }.join(', ')
238+
io.puts <<-EOS
239+
240+
def #{name}(#{param_string})
241+
if @@#{name}.nil?
242+
sym = load_gl_sym__('#{name}')
243+
@@#{name} = Fiddle::Function.new(sym, [#{
244+
cmd.parameters.map { |p| fiddle_type(p) }.join(', ') }], [#{ fiddle_type(cmd.type) }])
245+
end
246+
@@#{name}.call(#{param_string})
247+
end
248+
EOS
249+
}
250+
251+
io.puts 'end # module GlCommands'
252+
}
253+
end
254+
255+
256+
257+
# Read gl.xml
258+
259+
document_paths = [ 'gl.xml' ]
260+
261+
document_paths.each {
262+
|path|
263+
document = Nokogiri.XML(File.open(path, 'r'), path)
264+
generate_binding_impl(document)
265+
}

lib/opengl.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
require 'opengl/opengl_stub'
2+
require 'opengl/gl_sym'
3+
require 'opengl/gl_enums'
4+
require 'opengl/gl_commands'
5+
6+
module Gl
7+
include GlEnums
8+
include GlCommands
9+
end

lib/opengl/gl_sym.rb

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
1+
require 'fiddle'
2+
require 'opengl/opengl_stub'
23

34
module GlSym
45

@@ -7,11 +8,19 @@ module GlSym
78
def self.load_gl_sym__(name)
89
if @@opengl_lib.nil?
910
lib_path = case
10-
when macos? then nil
11-
when unix? then nil
11+
when apple?
12+
'/System/Library/Frameworks/OpenGL.framework/OpenGL'
13+
when unix? || linux?
14+
'libGL.so.1'
15+
when windows?
16+
'opengl32.dll'
17+
else
18+
raise 'Unrecognized platform'
1219
end
13-
puts lib_path
20+
@@opengl_lib = Fiddle.dlopen(lib_path)
1421
end
22+
23+
@@opengl_lib[name]
1524
end
1625

1726
end # module GlSym

opengl.gemspec

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ Gem::Specification.new { |s|
88
s.date = '2013-06-20'
99
s.summary = 'OpenGL'
1010
s.description = 'OpenGL bindings for Ruby 2.x'
11-
s.authors = ['Noel Raymond Cower']
11+
s.authors = [ 'Noel Raymond Cower' ]
1212
s.email = 'ncower@gmail.com'
13-
s.files = [
14-
'lib/opengl.rb'
15-
]
13+
s.files = Dir.glob('lib/**/*.rb') +
14+
Dir.glob('ext/**/*.{c,rb}')
15+
s.extensions << 'ext/opengl/extconf.rb'
1616
s.homepage = 'https://github.com/nilium/ruby-opengl'
1717
s.license = 'Simplified BSD'
18+
19+
s.add_development_dependency 'nokogiri'
1820
}

0 commit comments

Comments
 (0)