rubygl 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d0db02206c2e8a176817a841bc3ced8cfe60b4af
4
+ data.tar.gz: cc0aee2bc3df4f86b43b151e666b967ad9153b03
5
+ SHA512:
6
+ metadata.gz: c37b401ada50218e49983b17a3e7d8f9486fe1a05f1b5bd99ac1ec67fd83cea4a6254c9febf8fa903a21b1f9364bed9bb8c1f8399a862e7eb7e71c3f220d5911
7
+ data.tar.gz: 7148dd01e893ed00fcfccb884b34d05e24a946ee01d668b637b3defc22a77b0f378784b225512394e6728a2f8210a266e2b247e3c36b3b05204623c3d33d7a86
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Andrew
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,83 @@
1
+ require 'rake/task'
2
+ require 'rake/testtask'
3
+ require 'tmpdir'
4
+
5
+ SDL_INCLUDE = 'C:\Users\GG\Desktop\SDL2-2.0.3\i686-w64-mingw32\include\SDL2'
6
+ SDL_LIB = 'C:\Users\GG\Desktop\SDL2-2.0.3\i686-w64-mingw32\lib'
7
+ SDL_BIN = 'C:\Users\GG\Desktop\SDL2-2.0.3\i686-w64-mingw32\bin'
8
+
9
+ MINGW_LIB = ''
10
+
11
+ PROJ_INCLUDE = 'lib/RubyGL/Native/include'
12
+ PROJ_SRC = 'lib/RubyGL/Native/src/'
13
+
14
+ # OS Detection
15
+ def os
16
+ @os ||= (
17
+ host_os = RbConfig::CONFIG['host_os']
18
+
19
+ case host_os
20
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
21
+ :windows
22
+ when /darwin|mac os/
23
+ :macosx
24
+ when /linux/
25
+ :linux
26
+ when /solaris|bsd/
27
+ :unix
28
+ else
29
+ raise Error::WebDriverError, "unknown os: #{host_os.inspect}"
30
+ end
31
+ )
32
+ end
33
+
34
+ /---------------------------------RAKE JOBS-----------------------------------/
35
+
36
+ task :default do
37
+ files = ['Audio', 'Window', 'GLContext', 'Input', 'OpenGL']
38
+
39
+ # Build Object Files
40
+ obj = "gcc -I#{SDL_INCLUDE} -I#{PROJ_INCLUDE} -c -fPIC REPLACE.c -o REPLACE.o"
41
+ files.each { |file|
42
+ sh (obj.gsub(/REPLACE/, PROJ_SRC + file))
43
+ file = file.prepend PROJ_SRC
44
+ }
45
+
46
+ # Build Shared Object File
47
+ bin = "gcc -L#{SDL_LIB} -L#{MINGW_LIB} #{files.join('.o ') << '.o'} \
48
+ -lmingw32 -lSDL2main -lSDL2 -lopengl32 -shared -o \
49
+ ext/#{os.to_s}/RubyGL.so"
50
+ sh bin
51
+ end
52
+
53
+ task :clean do
54
+ # Clean Out Object Files
55
+ FileUtils.rm Dir.glob(PROJ_SRC + '/*.o')
56
+ end
57
+
58
+ task :bindings do
59
+ shared_lib = "RubyGL.so"
60
+
61
+ Dir.mktmpdir { |dir|
62
+ # Generates Header And Source C Files
63
+ sh "ruby bin/gl_code_gen.rb gl 4.5 OpenGL #{dir}"
64
+
65
+ # Run Preprocessor On Source File
66
+ sh "gcc -E -I#{SDL_INCLUDE} -o #{dir}/OpenGL.o #{dir}/OpenGL.c"
67
+
68
+ # Pipe Preprocessor(ed) File And Header File To Generate Bindings
69
+ sh "ruby bin/ffi_code_gen.rb #{dir}/OpenGL.o #{dir}/OpenGL.h \
70
+ #{dir}/opengl.rb #{shared_lib}"
71
+
72
+ # Move Files To Correct Folders
73
+ mv "#{dir}/OpenGL.h", PROJ_INCLUDE
74
+ mv "#{dir}/OpenGL.c", PROJ_SRC
75
+ mv "#{dir}/opengl.rb", "lib/RubyGL/Native/"
76
+ }
77
+ end
78
+
79
+ Rake::TestTask.new do |t|
80
+ t.libs << "tests"
81
+ t.test_files = FileList['tests/test*.rb']
82
+ t.verbose = true
83
+ end
data/RubyGL.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'rubygl'
5
+ spec.version = '0.1.0'
6
+ spec.authors = ["Andrew Miller"]
7
+ spec.email = ["millera9@seattleu.edu"]
8
+ spec.summary = %q{A Complete Solution For Graphics Programming In Ruby}
9
+ spec.description = %q{This library provides you with all of the essentials for
10
+ doing graphics programming in Ruby including windowing, context management,
11
+ 1:1 mapping of OpenGL calls, and input handling. The backend of this library
12
+ consists of FFI functions with 1:1 mapping to the C code that makes all of this
13
+ possible. The frontend of this library provides abstractions for interacting with
14
+ the backend functionality so that you do not have to deal with the manual memory
15
+ management from within Ruby that the backend requires.}
16
+ spec.homepage = "https://github.com/GGist/RubyGL"
17
+ spec.license = "MIT"
18
+
19
+ spec.files = `git ls-files`.split($/)
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency 'ffi'
23
+ end
@@ -0,0 +1,167 @@
1
+ require 'fileutils'
2
+
3
+ def resolve_typedef(type, type_defs)
4
+ while (type_defs[type]) do
5
+ type = type_defs[type]
6
+ end
7
+
8
+ return type
9
+ end
10
+
11
+ def get_type(type, hash)
12
+ hash[type]
13
+ end
14
+
15
+ def match_type(type, regex_hash)
16
+ regex_hash.select { |key,val|
17
+ type =~ key
18
+ }.values[0]
19
+ end
20
+
21
+ def write_file(name, contents, mode = 'w+')
22
+ File.open(name, mode) { |file|
23
+ file << contents
24
+ }
25
+ end
26
+
27
+ /--------------------------------START SCRIPT---------------------------------/
28
+ # ARGV: [Object File] [Header File] [Ruby Source Name] [Library Name]
29
+
30
+ SOURCE_INDENTS = 4
31
+ FFI_TYPES = { 'char' => 'char', 'signed char' => 'char',
32
+ 'unsigned char' => 'uchar', 'int8_t' => 'int8', 'uint8_t' => 'uint8',
33
+ 'short' => 'short', 'unsigned short' => 'ushort', 'int16_t' => 'int16',
34
+ 'uint16_t' => 'uint16', 'int' => 'int', 'unsigned int' => 'uint',
35
+ 'int32_t' => 'int32', 'uint32_t' => 'uint32', 'long int' => 'long',
36
+ 'unsigned long int' => 'ulong', 'int64_t' => 'int64', 'uint64_t' => 'uint64',
37
+ 'long long int' => 'long_long', 'unsigned long long int' => 'ulong_long',
38
+ 'float' => 'float', 'double' => 'double' }
39
+ FFI_PTR_TYPES = { /\Aconst +(?:unsigned +)?char *\*\Z/ => 'string', /\*/ => 'pointer' }
40
+
41
+ # Open Pre-Processor(ed) Source File
42
+ source_code = ''
43
+ File.open(ARGV[0]) { |file|
44
+ source_code = file.read
45
+ }
46
+
47
+ # Associate Type Definitions With Their Actual Types
48
+ typedef_hash = {}
49
+ source_code.scan(/^typedef (.*?;)$/).flatten.each { |segment|
50
+ segment.gsub!(/\*/, '* ')
51
+
52
+ if (segment =~ /\(.*?\*.*?\)/) then # Function Pointer
53
+ key = segment[/\* *(.*?) *\)/, 1]
54
+
55
+ typedef_hash[key] = segment[0..-2] # Remove Semi-Colon
56
+ else # Other Type
57
+ key = segment[/([^ ]+);/, 1]
58
+
59
+ typedef_hash[key] = segment[/(.*?) +[^ ]+;/, 1]
60
+ end
61
+ }
62
+
63
+ # Open Header File
64
+ source_code = ''
65
+ File.open(ARGV[1]) { |file|
66
+ source_code = file.read
67
+ }
68
+
69
+ # Associate Macros With Their Literal Types (OpenGL Enumerations)
70
+ macro_tuples = []
71
+ source_code.scan(/^#define GL_.+?$/i).each { |macro|
72
+ token = macro[/^.+ +(.+) +.+$/, 1]
73
+ value = macro[/^.+ +.+ +(.+)$/, 1]
74
+ macro_tuples.push([token, value])
75
+ }
76
+
77
+ # Pull Out OpenGL Function Names From Header
78
+ gl_functions = source_code.scan(/^.+?gl.+?\(.*?\);$/)
79
+
80
+ ffi_template = "require 'ffi'
81
+
82
+ module RubyGL::Native
83
+ FUNCTIONS_GO_HERE
84
+
85
+ CONSTANTS_GO_HERE
86
+ end
87
+ "
88
+
89
+ # Append Functions (Param Type -> Typedefs -> Ruby Type)
90
+ buffer = ''
91
+ gl_functions.each { |signature|
92
+ buffer << ' ' * SOURCE_INDENTS + 'attach_function :' +
93
+ signature[/(gl.*?)\(.*?\);/, 1] + ', ['
94
+
95
+ # Get Type List
96
+ param_type_list = signature[/\((.*?)\);/, 1].split(', ').map { |full_param|
97
+ full_param[/(.+) [[:alnum:]]+/, 1]
98
+ }
99
+
100
+ # Type Definition -> Primitive (Actual) Type
101
+ param_type_list.map! { |param_type|
102
+ # Parameter Type Could Use Multiple Type Definitions
103
+
104
+ simple_param_types = param_type.gsub(/(?:\*|const)/, ' ').split(' ')
105
+ # Translate Each Type And Put Back Into param_type
106
+ simple_param_types.each { |simple_param_type|
107
+ primitive_type = resolve_typedef(simple_param_type, typedef_hash)
108
+
109
+ param_type.sub!(/#{simple_param_type}/, primitive_type)
110
+ }
111
+
112
+ param_type
113
+ }
114
+
115
+ # Primitive Type -> Ruby FFI Type
116
+ param_type_list.each { |param_type|
117
+ mutable_type = param_type.gsub(/const/, '').strip
118
+
119
+ if (get_type(mutable_type, FFI_TYPES)) then
120
+ param_type = get_type(mutable_type, FFI_TYPES)
121
+ else
122
+ param_type = match_type(param_type, FFI_PTR_TYPES)
123
+ end
124
+
125
+ # Write Out FFI Type
126
+ buffer << ':' + param_type + ', '
127
+ }
128
+ if (buffer[-1] == '[') then # No Parameters
129
+ buffer << '],'
130
+ else # At Least One Parameter
131
+ buffer[-2..-1] = '],'
132
+ end
133
+
134
+ # Get Return Type
135
+ # We Do Not Care About const If We Can Match Our Return Type With A
136
+ # Primitive Type. Otherwise, We Might Be Able To Match Against A
137
+ # const char* Or const unsigned char* In Which Case We Prefer To Use A
138
+ # string Type.
139
+ return_type = signature[/(.*?) gl[^ ]+\(/, 1]
140
+ mutable_return_type = return_type.gsub(/ *const */, '')
141
+
142
+ mutable_return_type.gsub(/\*/, ' ').split(' ').each { |type|
143
+ resolved_type = resolve_typedef(type, typedef_hash)
144
+
145
+ mutable_return_type.sub!(/#{type}/, resolved_type)
146
+ return_type.sub!(/#{type}/, resolved_type)
147
+ }
148
+
149
+ if (get_type(mutable_return_type, FFI_TYPES)) then
150
+ return_type = get_type(mutable_return_type, FFI_TYPES)
151
+ elsif (match_type(return_type, FFI_PTR_TYPES))
152
+ return_type = match_type(return_type, FFI_PTR_TYPES)
153
+ end
154
+
155
+ buffer << ' :' + return_type + "\n"
156
+ }
157
+ ffi_template.sub!(/FUNCTIONS_GO_HERE/, buffer)
158
+
159
+ # Append OpenGL Enumerations (Macros)
160
+ buffer = ''
161
+ macro_tuples.each { |name,val|
162
+ buffer << ' ' * SOURCE_INDENTS + "#{name} = #{val}\n"
163
+ }
164
+ ffi_template.sub!(/CONSTANTS_GO_HERE/, buffer)
165
+
166
+ write_file(ARGV[2], ffi_template)
167
+ /---------------------------------END SCRIPT----------------------------------/
@@ -0,0 +1,459 @@
1
+ require 'net/http'
2
+ require 'openssl'
3
+ require 'rexml/document'
4
+ require 'fileutils'
5
+
6
+ # Decorate String Class With Convenience Methods For Parsing
7
+ class String
8
+ def to_bool
9
+ return true if self == true || self =~ (/^(?:true|t|yes|y|1)$/i)
10
+ return false if self == false || self.blank? || self =~ (/^(?:false|f|no|n|0)$/i)
11
+ raise ArgumentError.new("Invalid Boolean: \"#{self}\"")
12
+ end
13
+ def to_bool?
14
+ begin
15
+ self.to_bool
16
+ rescue
17
+ return false
18
+ end
19
+ return true
20
+ end
21
+ def is_i?
22
+ !!(self =~ /\A[-+]?[0-9]+\Z/)
23
+ end
24
+ def replace_tags(sub)
25
+ self.gsub(/\<.*?\>/, sub)
26
+ end
27
+ end
28
+
29
+ # Returns The Page Source Of The url And Aborts If Anything Other Than A 200
30
+ # Is Received.
31
+ def get_page_source(url)
32
+ uri = URI.parse url
33
+
34
+ http = Net::HTTP.new(uri.host, uri.port)
35
+ if (uri.kind_of? URI::HTTPS) then
36
+ http.use_ssl = true
37
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
38
+ end
39
+
40
+ response = http.get(uri.path)
41
+ if (response.code != '200') then
42
+ abort("\nWas not able to pull xml file from #{uri}" +
43
+ "Response Code: #{response.code}")
44
+ end
45
+
46
+ return response.body
47
+ end
48
+
49
+ # Returns An API List In The Form Of [[API, Version], ...]
50
+ def get_api_list(root)
51
+ api_list = []
52
+
53
+ root.each_element('feature') { |element|
54
+ api = element.attributes['api']
55
+ version = element.attributes['number']
56
+
57
+ api_list.push [api, version]
58
+ }
59
+
60
+ return api_list
61
+ end
62
+
63
+ # Builds An ASCII Grid View From The Tuple List Supplied. The Cell Sizes Will
64
+ # Dynamically Scale Based On The Biggest Cell So That Columns Are Aligned.
65
+ def get_grid_view(console_width, tuple_list)
66
+ formatting_chars = 6
67
+
68
+ # Find Size Of Longest String Element (With Formatting)
69
+ max_length = tuple_list.sort { |a,b|
70
+ string_one = a[0] + a[1]
71
+ string_two = b[0] + b[1]
72
+
73
+ string_one.size <=> string_two.size
74
+ }.last.join('').size + tuple_list.size.to_s.size + formatting_chars
75
+
76
+ options_per_row = console_width / max_length
77
+
78
+ # Build 1-D Array Of Concatenated Strings
79
+ curr_iteraion = -1
80
+ concat_list = tuple_list.collect { |a,b|
81
+ curr_iteraion += 1
82
+ ' ' + curr_iteraion.to_s + ': ' + a + ' ' + b + ' '
83
+ }
84
+ formatting_chars -= 5
85
+
86
+ # Expand Concatenated Strings Into Result
87
+ result = '-' * console_width
88
+ curr_column = 1
89
+ concat_list.each { |concat_item|
90
+ num_padding = max_length - concat_item.size - formatting_chars
91
+
92
+ result << concat_item << ' ' * num_padding
93
+
94
+ if (curr_column == options_per_row) then
95
+ result << "\n" + ('-' * console_width)
96
+ curr_column = 1
97
+ else
98
+ result << '|'
99
+ curr_column += 1
100
+ end
101
+ }
102
+
103
+ result << "\n" + ('-' * console_width) if result[-1] != '-'
104
+
105
+ return result
106
+ end
107
+
108
+ # Pads The String With pad_char So That It Fills columns Completely
109
+ def get_pretty(columns, pad_char, string)
110
+ padding = pad_char * ((columns - string.size) / 2)
111
+
112
+ result = (padding + string + padding)
113
+ result << pad_char if result.size < columns
114
+
115
+ return result
116
+ end
117
+
118
+ # Writes contents Out To path/name Using mode
119
+ def write_file(path, name, contents, mode = 'w+')
120
+ FileUtils.mkdir_p(path)
121
+
122
+ File.open(path + '/' + name, mode) { |file|
123
+ file << contents
124
+ }
125
+ end
126
+
127
+ # Verify The Supplied api And version Numbers Against The api_list
128
+ def valid_api_values?(api_list, api, version)
129
+ api_list.find_index { |api_tuple|
130
+ api_tuple[0].casecmp(api) && api_tuple[1].to_f == version
131
+ } != nil
132
+ end
133
+
134
+ # Verify The Supplied "api_index" To Make Sure It Is Valid
135
+ def valid_api_index?(api_list, api_index)
136
+ api_index.is_i? && (0...api_list.size).include?(api_index.to_i)
137
+ end
138
+
139
+ # Prompt User With An api_list In A Grid View For An api_index Selection
140
+ def prompt_user(api_list)
141
+ # Prompt User
142
+ print get_pretty(COLUMN_WIDTH, ' ', GRID_TITLE)
143
+ print get_grid_view(COLUMN_WIDTH, api_list)
144
+ puts "Note: OpenGL 3.2 introduced the notion of a 'core' profile which
145
+ essentially means that it is not completely backwards compatible with
146
+ earlier versions. To retain this backwards compatibility (not recommended)
147
+ append a 'C' to the index number of the API you want.".gsub(/\s/, ' ')
148
+
149
+ # User Input
150
+ print "\nEnter The Index Number Next To The API You Want: "
151
+ api_index = gets.chomp
152
+ compatibility_mode = false
153
+
154
+ if (api_index[-1].upcase == 'C') then
155
+ compatibility_mode = true
156
+ api_index = api_index[0..-2]
157
+ end
158
+
159
+ while (!valid_api_index?(api_list, api_index)) do
160
+ print "Please Enter A Valid Input: "
161
+ api_index = gets.chomp
162
+
163
+ if (api_index[-1].upcase == 'C') then
164
+ compatibility_mode = true
165
+ api_index = api_index[0..-2]
166
+ else
167
+ compatibility_mode = false
168
+ end
169
+ end
170
+ api_index = api_index.to_i
171
+
172
+ return [api_list[api_index][0], api_list[api_index][1].to_f,
173
+ compatibility_mode]
174
+ end
175
+
176
+ /--------------------------------START SCRIPT---------------------------------/
177
+
178
+ # ARGV: (GL_API GL_VERSION) NAME FOLDER
179
+ # All Optional, If GL_API Is Specified Then GL_VERSION Must Be As Well
180
+
181
+ # Constants
182
+ COLUMN_WIDTH = 80
183
+ GRID_TITLE = "OpenGL API Versions"
184
+ GL_URL = 'https://cvs.khronos.org/svn/repos/ogl/trunk/doc/registry/public/api'
185
+ GL_PATH = '/gl.xml'
186
+ EGL_URL = 'http://www.khronos.org/registry/egl/api'
187
+ EGL_KHR_PATH = '/KHR/khrplatform.h'
188
+ SOURCE_INDENTS = 2
189
+
190
+ # Execution Variables
191
+ gl_header = 'OpenGL.h'
192
+ gl_source = 'OpenGL.c'
193
+ directory = './'
194
+ api, version, compatibility_mode = nil, nil, false
195
+
196
+ # Check Command Line Arguments
197
+ if (ARGV[0]) then
198
+ api = ARGV[0]
199
+ version = ARGV[1].to_f
200
+ compatibility_mode = ARGV[1][-1].casecmp('C') == 0
201
+ end
202
+ if (ARGV[2]) then
203
+ gl_header = ARGV[2] + '.h'
204
+ gl_source = ARGV[2] + '.c'
205
+ end
206
+ if (ARGV[3]) then
207
+ directory = ARGV[3]
208
+ end
209
+
210
+ print "\n" + get_pretty(COLUMN_WIDTH, '-', "Pulling Data From Web")
211
+ puts "- #{GL_URL + GL_PATH}"
212
+ source = get_page_source(GL_URL + GL_PATH)
213
+ puts get_pretty(COLUMN_WIDTH, '-', "Finished Pulling Data")
214
+
215
+ print get_pretty(COLUMN_WIDTH, '-', "Building XML Structure")
216
+ puts "Bytes: #{source.size}"
217
+ root = REXML::Document.new(source).root
218
+ puts get_pretty(COLUMN_WIDTH, '-', "Finished Building XML Structure")
219
+
220
+ # Extract API And Version Information
221
+ api_list = get_api_list(root) # [[API, Version], ...]
222
+
223
+ if (!api || !version) then # Prompt User
224
+ api_tuple = prompt_user(api_list)
225
+
226
+ api = api_tuple[0]
227
+ version = api_tuple[1]
228
+ compatibility_mode = api_tuple[3]
229
+ else # Verify User Parameters
230
+ if (!valid_api_values?(api_list, api, version)) then
231
+ abort("Invalid API Or Version Parameters Passed To Script")
232
+ end
233
+ end
234
+
235
+ puts get_pretty(COLUMN_WIDTH, '-', "API/Version/Profile - #{api}/#{version}/" +
236
+ (compatibility_mode ? "Compatibility" : "Core"))
237
+
238
+ # Parse All Enumerations And Commands
239
+ all_enums, all_commands = {}, {}
240
+ print get_pretty(COLUMN_WIDTH, '-', "Crawling XML Structure")
241
+
242
+ puts "Parsing Enumerations..."
243
+ root.each_element('enums/enum') { |element|
244
+ all_enums[element.attributes['name']] = element.attributes['value']
245
+ }
246
+ puts "Parsed All Enumerations\n\n"
247
+
248
+ puts "Parsing Commands..."
249
+ root.each_element('commands/command') { |element|
250
+ comm_name = element.elements['proto/name'].text
251
+
252
+ command = element.elements['proto'].to_s.replace_tags(' ').squeeze(' ').strip
253
+ command << '('
254
+
255
+ element.each_element('param') { |innerElement|
256
+ command << innerElement.to_s.replace_tags(' ').squeeze(' ').strip + ', '
257
+ }
258
+ command[-2..-1] = '' if command.end_with? ', '
259
+
260
+ all_commands[comm_name] = command + ');'
261
+ }
262
+ puts "Parsed All Commands"
263
+
264
+ puts get_pretty(COLUMN_WIDTH, '-', "Finished Crawling XML Structure")
265
+
266
+ #-----------------------------Build Header File-------------------------------
267
+ used_enums, used_commands = {}, {}
268
+
269
+ header_template = "#ifndef #{gl_header.upcase.sub(/\./, '_')}
270
+ #define #{gl_header.upcase.sub(/\./, '_')}
271
+
272
+ #ifdef __cplusplus
273
+ extern \"C\" {
274
+ #endif
275
+
276
+ #if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
277
+ #ifndef WIN32_LEAN_AND_MEAN
278
+ #define WIN32_LEAN_AND_MEAN 1
279
+ #endif
280
+ #include <windows.h>
281
+ #undef near // Used As An Identifier In OpenGL
282
+ #undef far // Used As An Identifier In OpenGL
283
+ #endif
284
+
285
+ #ifndef APIENTRY
286
+ #define APIENTRY
287
+ #endif
288
+
289
+ COMMENTS_GO_HERE
290
+
291
+ TYPEDEFS_GO_HERE
292
+
293
+ DEFINES_GO_HERE
294
+
295
+ FUNCTIONS_GO_HERE
296
+
297
+ #ifdef __cplusplus
298
+ }
299
+ #endif
300
+
301
+ #endif"
302
+
303
+ print get_pretty(COLUMN_WIDTH, '-', "Generating File #{gl_header}")
304
+
305
+ puts "Appending Comments To #{gl_header}..."
306
+ buffer = "/*\n"
307
+ root.elements['comment'].text.each_line { |line|
308
+ buffer << '** ' << line
309
+ }
310
+ buffer << "\n*/"
311
+ header_template.sub!(/COMMENTS_GO_HERE/, buffer)
312
+ puts "Comments Have Been Appended\n\n"
313
+
314
+ # Pull Out All Possible Include Dependencies
315
+ puts "Appending Type Definitions To #{gl_header}..."
316
+ possible_includes = {}
317
+ root.each_element('types/type[@name]') { |element|
318
+ possible_includes[element.attributes['name']] = element.text
319
+ }
320
+
321
+ # Pull Out All Possible Types And Check If They Have Include Dependencies
322
+ actual_includes, buffer = {}, ''
323
+ attrib_filter = api == 'gl' ? 'not(@api)' : "@api='#{api}'"
324
+ root.each_element("types/type[#{attrib_filter} and not(@name)]") { |element|
325
+ content = element.to_s.replace_tags(' ').squeeze(' ').strip
326
+ content.gsub!(/ +;/, ';')
327
+ buffer << content << "\n"
328
+
329
+ requires = element.attributes['requires']
330
+ if (requires && !actual_includes[requires]) then
331
+ actual_includes[requires] = possible_includes[requires]
332
+ end
333
+ }
334
+
335
+ # Any Header Dependencies Found
336
+ actual_includes.each { |require,include|
337
+ if (require == 'khrplatform') then
338
+ print get_pretty(COLUMN_WIDTH, '-', "Pulling Data From Web")
339
+ puts "- #{EGL_URL + EGL_KHR_PATH}"
340
+
341
+ source = get_page_source(EGL_URL + EGL_KHR_PATH)
342
+ write_file("#{directory}/KHR", 'khrplatform.h', source)
343
+
344
+ print get_pretty(COLUMN_WIDTH, '-', "Finished Pulling Data")
345
+ end
346
+
347
+ # Ugh, Khronos Re-Used The Require Attribute For Non-Header Dependencies!!!
348
+ if (include) then
349
+ buffer.prepend(include + "\n")
350
+ end
351
+ }
352
+ header_template.sub!(/TYPEDEFS_GO_HERE/, buffer)
353
+ puts "Appended Type Definitions\n\n"
354
+
355
+ puts "Filtering Macros And Commands..."
356
+ root.each_element('feature') { |element|
357
+ if (element.attributes['api'] == api && element.attributes['number'].to_f <= version) then
358
+ element.each_element('require/enum') { |inner_element|
359
+ enum_name = inner_element.attributes['name']
360
+ used_enums[enum_name] = all_enums[enum_name]
361
+ }
362
+
363
+ element.each_element('require/command') { |inner_element|
364
+ comm_name = inner_element.attributes['name']
365
+ used_commands[comm_name] = all_commands[comm_name]
366
+ }
367
+ if (!compatibility_mode) then
368
+ element.each_element('remove/enum') { |inner_element|
369
+ enum_name = inner_element.attributes['name']
370
+ used_enums.delete(enum_name)
371
+ }
372
+ element.each_element('remove/command') { |inner_element|
373
+ comm_name = inner_element.attributes['name']
374
+ used_commands.delete(comm_name)
375
+ }
376
+ end
377
+ end
378
+ }
379
+ puts "Filtered Macros And Commands\n\n"
380
+
381
+ puts "Appending Macro To #{gl_header}..."
382
+ used_enums, buffer = used_enums.to_a.sort, ''
383
+ used_enums.each { |name,value|
384
+ buffer << '#define ' << name << ' ' << value << "\n"
385
+ }
386
+ header_template.sub!(/DEFINES_GO_HERE/, buffer)
387
+ puts "Appended Macros\n\n"
388
+
389
+ puts "Appending Commands To #{gl_header}..."
390
+ used_commands, buffer = used_commands.to_a.sort, ''
391
+ used_commands.each { |name,signature|
392
+ buffer << signature << "\n"
393
+ }
394
+ header_template.sub!(/FUNCTIONS_GO_HERE/, buffer)
395
+ puts "Appended Commands"
396
+
397
+ write_file(directory, gl_header, header_template)
398
+ puts get_pretty(COLUMN_WIDTH, '-', "Finished Generating #{gl_header}")
399
+
400
+ #-----------------------------Build Source File-------------------------------
401
+ print get_pretty(COLUMN_WIDTH, '-', "Generating File #{gl_source}")
402
+
403
+ buffer = "#include \"#{gl_header}\"\n#include \"SDL.h\"\n\n"
404
+
405
+ puts "Appending Command Definitions To #{gl_source}..."
406
+ used_commands.each { |name,signature|
407
+ buffer << signature[0..-2] + " {\n"
408
+
409
+ # Break Function Down
410
+ func_return_type = signature[/\A(.*?)gl/, 1].strip
411
+ typedef_name = 'GL_' + name[2..-1] + '_Func'
412
+ func_ptr_name = name + '_ptr'
413
+ param_type_list = []
414
+ unless (signature =~ /\(\);/) then # Has At Least One Parameter
415
+ param_type_list = signature[/\((.*?);/, 1].split(/ *\w+[,\)] */)
416
+ end
417
+ param_name_list = signature.scan(/(\w+)[,\)]/).flatten
418
+
419
+ # Append Typedef
420
+ buffer << ' ' * SOURCE_INDENTS + 'typedef ' + func_return_type +
421
+ " (APIENTRY * #{typedef_name})("
422
+ param_type_list.each { |param_type|
423
+ buffer << param_type + ', '
424
+ }
425
+ if (buffer[-2..-1] == ', ') then
426
+ buffer[-2..-1] = ');'
427
+ else
428
+ buffer << ');'
429
+ end
430
+ buffer << "\n"
431
+
432
+ # Append Pointer Initialization
433
+ buffer << ' ' * SOURCE_INDENTS + 'static ' + typedef_name + ' ' +
434
+ func_ptr_name + " = NULL;\n\n"
435
+
436
+ buffer << ' ' * SOURCE_INDENTS + "if (#{func_ptr_name} == NULL) {\n" +
437
+ ' ' * (SOURCE_INDENTS * 2) + func_ptr_name +
438
+ " = (#{typedef_name})SDL_GL_GetProcAddress(__func__);\n" +
439
+ ' ' * SOURCE_INDENTS + "}\n\n"
440
+
441
+ # Append Function Pointer Call
442
+ buffer << ' ' * SOURCE_INDENTS + func_ptr_name + '('
443
+ param_name_list.each { |param_name|
444
+ buffer << param_name + ', '
445
+ }
446
+ if (buffer[-2..-1] == ', ') then
447
+ buffer[-2..-1] = ');'
448
+ else
449
+ buffer << ');'
450
+ end
451
+
452
+ buffer << "\n}\n\n"
453
+ }
454
+ write_file(directory, gl_source, buffer)
455
+ puts "Finished Appending Command Definitions"
456
+
457
+ print get_pretty(COLUMN_WIDTH, '-', "Finished Generating #{gl_source}")
458
+
459
+ /---------------------------------END SCRIPT----------------------------------/