1// Copyright 2015 Brendan Zabarauskas and the gl-rs developers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use registry::Registry;
16use std::io;
17
18#[allow(missing_copy_implementations)]
19pub struct GlobalGenerator;
20
21impl super::Generator for GlobalGenerator {
22 fn write<W>(&self, registry: &Registry, dest: &mut W) -> io::Result<()>
23 where
24 W: io::Write,
25 {
26 try!(write_header(dest));
27 try!(write_metaloadfn(dest));
28 try!(write_type_aliases(registry, dest));
29 try!(write_enums(registry, dest));
30 try!(write_fns(registry, dest));
31 try!(write_fnptr_struct_def(dest));
32 try!(write_ptrs(registry, dest));
33 try!(write_fn_mods(registry, dest));
34 try!(write_panicking_fns(registry, dest));
35 try!(write_load_fn(registry, dest));
36 Ok(())
37 }
38}
39
40/// Creates a `__gl_imports` module which contains all the external symbols that we need for the
41/// bindings.
42fn write_header<W>(dest: &mut W) -> io::Result<()>
43where
44 W: io::Write,
45{
46 writeln!(
47 dest,
48 r#"
49 mod __gl_imports {{
50 pub use std::mem;
51 pub use std::os::raw;
52 }}
53 "#
54 )
55}
56
57/// Creates the metaloadfn function for fallbacks
58fn write_metaloadfn<W>(dest: &mut W) -> io::Result<()>
59where
60 W: io::Write,
61{
62 writeln!(
63 dest,
64 r#"
65 #[inline(never)]
66 fn metaloadfn(loadfn: &mut dyn FnMut(&'static str) -> *const __gl_imports::raw::c_void,
67 symbol: &'static str,
68 fallbacks: &[&'static str]) -> *const __gl_imports::raw::c_void {{
69 let mut ptr = loadfn(symbol);
70 if ptr.is_null() {{
71 for &sym in fallbacks {{
72 ptr = loadfn(sym);
73 if !ptr.is_null() {{ break; }}
74 }}
75 }}
76 ptr
77 }}
78 "#
79 )
80}
81
82/// Creates a `types` module which contains all the type aliases.
83///
84/// See also `generators::gen_types`.
85fn write_type_aliases<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
86where
87 W: io::Write,
88{
89 try!(writeln!(
90 dest,
91 r#"
92 pub mod types {{
93 #![allow(non_camel_case_types, non_snake_case, dead_code, missing_copy_implementations)]
94 "#
95 ));
96
97 try!(super::gen_types(registry.api, dest));
98
99 writeln!(
100 dest,
101 "
102 }}
103 "
104 )
105}
106
107/// Creates all the `<enum>` elements at the root of the bindings.
108fn write_enums<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
109where
110 W: io::Write,
111{
112 for enm: &Enum in &registry.enums {
113 try!(super::gen_enum_item(enm, "types::", dest));
114 }
115
116 Ok(())
117}
118
119/// Creates the functions corresponding to the GL commands.
120///
121/// The function calls the corresponding function pointer stored in the `storage` module created
122/// by `write_ptrs`.
123fn write_fns<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
124where
125 W: io::Write,
126{
127 for cmd: &Cmd in &registry.cmds {
128 if let Some(v: &Vec) = registry.aliases.get(&cmd.proto.ident) {
129 try!(writeln!(dest, "/// Fallbacks: {}", v.join(", ")));
130 }
131
132 try!(writeln!(dest,
133 "#[allow(non_snake_case, unused_variables, dead_code)] #[inline]
134 pub unsafe fn {name}({params}) -> {return_suffix} {{ \
135 __gl_imports::mem::transmute::<_, extern \"system\" fn({typed_params}) -> {return_suffix}>\
136 (storage::{name}.f)({idents}) \
137 }}",
138 name = cmd.proto.ident,
139 params = super::gen_parameters(cmd, true, true).join(", "),
140 typed_params = super::gen_parameters(cmd, false, true).join(", "),
141 return_suffix = cmd.proto.ty,
142 idents = super::gen_parameters(cmd, true, false).join(", "),
143 ));
144 }
145
146 Ok(())
147}
148
149/// Creates a `FnPtr` structure which contains the store for a single binding.
150fn write_fnptr_struct_def<W>(dest: &mut W) -> io::Result<()>
151where
152 W: io::Write,
153{
154 writeln!(dest,
155 "
156 #[allow(missing_copy_implementations)]
157 pub struct FnPtr {{
158 /// The function pointer that will be used when calling the function.
159 f: *const __gl_imports::raw::c_void,
160 /// True if the pointer points to a real function, false if points to a `panic!` fn.
161 is_loaded: bool,
162 }}
163
164 impl FnPtr {{
165 /// Creates a `FnPtr` from a load attempt.
166 pub fn new(ptr: *const __gl_imports::raw::c_void) -> FnPtr {{
167 if ptr.is_null() {{
168 FnPtr {{ f: missing_fn_panic as *const __gl_imports::raw::c_void, is_loaded: false }}
169 }} else {{
170 FnPtr {{ f: ptr, is_loaded: true }}
171 }}
172 }}
173 }}
174 ")
175}
176
177/// Creates a `storage` module which contains a static `FnPtr` per GL command in the registry.
178fn write_ptrs<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
179where
180 W: io::Write,
181{
182 try!(writeln!(
183 dest,
184 "mod storage {{
185 #![allow(non_snake_case)]
186 #![allow(non_upper_case_globals)]
187 use super::__gl_imports::raw;
188 use super::FnPtr;"
189 ));
190
191 for c: &Cmd in &registry.cmds {
192 try!(writeln!(
193 dest,
194 "pub static mut {name}: FnPtr = FnPtr {{
195 f: super::missing_fn_panic as *const raw::c_void,
196 is_loaded: false
197 }};",
198 name = c.proto.ident
199 ));
200 }
201
202 writeln!(dest, "}}")
203}
204
205/// Creates one module for each GL command.
206///
207/// Each module contains `is_loaded` and `load_with` which interact with the `storage` module
208/// created by `write_ptrs`.
209fn write_fn_mods<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
210where
211 W: io::Write,
212{
213 for c in &registry.cmds {
214 let fallbacks = match registry.aliases.get(&c.proto.ident) {
215 Some(v) => {
216 let names = v
217 .iter()
218 .map(|name| format!("\"{}\"", super::gen_symbol_name(registry.api, &name[..])))
219 .collect::<Vec<_>>();
220 format!("&[{}]", names.join(", "))
221 },
222 None => "&[]".to_string(),
223 };
224 let fnname = &c.proto.ident[..];
225 let symbol = super::gen_symbol_name(registry.api, &c.proto.ident[..]);
226 let symbol = &symbol[..];
227
228 try!(writeln!(dest, r##"
229 #[allow(non_snake_case)]
230 pub mod {fnname} {{
231 use super::{{storage, metaloadfn}};
232 use super::__gl_imports::raw;
233 use super::FnPtr;
234
235 #[inline]
236 #[allow(dead_code)]
237 pub fn is_loaded() -> bool {{
238 unsafe {{ storage::{fnname}.is_loaded }}
239 }}
240
241 #[allow(dead_code)]
242 pub fn load_with<F>(mut loadfn: F) where F: FnMut(&'static str) -> *const raw::c_void {{
243 unsafe {{
244 storage::{fnname} = FnPtr::new(metaloadfn(&mut loadfn, "{symbol}", {fallbacks}))
245 }}
246 }}
247 }}
248 "##, fnname = fnname, fallbacks = fallbacks, symbol = symbol));
249 }
250
251 Ok(())
252}
253
254/// Creates a `missing_fn_panic` function.
255///
256/// This function is the mock that is called if the real function could not be called.
257fn write_panicking_fns<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
258where
259 W: io::Write,
260{
261 writeln!(
262 dest,
263 "#[inline(never)]
264 fn missing_fn_panic() -> ! {{
265 panic!(\"{api} function was not loaded\")
266 }}
267 ",
268 api = registry.api
269 )
270}
271
272/// Creates the `load_with` function.
273///
274/// The function calls `load_with` in each module created by `write_fn_mods`.
275fn write_load_fn<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
276where
277 W: io::Write,
278{
279 try!(writeln!(dest,
280 "
281 /// Load each OpenGL symbol using a custom load function. This allows for the
282 /// use of functions like `glfwGetProcAddress` or `SDL_GL_GetProcAddress`.
283 /// ~~~ignore
284 /// gl::load_with(|s| glfw.get_proc_address(s));
285 /// ~~~
286 #[allow(dead_code)]
287 pub fn load_with<F>(mut loadfn: F) where F: FnMut(&'static str) -> *const __gl_imports::raw::c_void {{
288 #[inline(never)]
289 fn inner(loadfn: &mut dyn FnMut(&'static str) -> *const __gl_imports::raw::c_void) {{
290 "));
291
292 for c in &registry.cmds {
293 try!(writeln!(
294 dest,
295 "{cmd_name}::load_with(&mut *loadfn);",
296 cmd_name = &c.proto.ident[..]
297 ));
298 }
299
300 writeln!(
301 dest,
302 "
303 }}
304
305 inner(&mut loadfn)
306 }}
307 "
308 )
309}
310