use crate::registry::{Api, Registry};
use std::io;
#[derive(Debug)]
pub struct GlobalGenerator {
pub trace: bool,
}
impl super::Generator for GlobalGenerator {
fn write<W>(&self, registry: &Registry, dest: &mut W) -> io::Result<()>
where
W: io::Write,
{
write_header(registry, dest, self)?;
write_meta_loader(dest)?;
write_type_aliases(registry, dest)?;
write_enums(registry, dest)?;
write_fns(registry, dest, self.trace)?;
write_ptrs(registry, dest)?;
write_fn_mods(registry, dest)?;
write_load_fn(registry, dest)?;
Ok(())
}
}
fn write_header<W>(
registry: &Registry,
dest: &mut W,
gen: &GlobalGenerator,
) -> io::Result<()>
where
W: io::Write,
{
writeln!(dest, "#![allow(bad_style)]")?;
writeln!(dest, "#![allow(clippy::unreadable_literal)]")?;
writeln!(dest, "#![allow(clippy::missing_safety_doc)]")?;
writeln!(dest, "#![allow(clippy::too_many_arguments)]")?;
writeln!(dest)?;
writeln!(dest, "//! `phosphorus` auto-generated GL bindings file.")?;
writeln!(dest, "//! * API: {}", registry.api)?;
writeln!(dest, "//! * Version: {:?}", registry.version)?;
writeln!(dest, "//! * Fallbacks: {:?}", registry.fallbacks)?;
writeln!(dest, "//! * Generator Style: {:?}", gen)?;
writeln!(dest)?;
writeln!(dest, "use core::ffi::c_void;")?;
writeln!(dest, "use core::ptr::NonNull;")?;
writeln!(dest, "use core::mem::transmute;")?;
writeln!(dest, "#[cfg(not(windows))] pub use libc::{{
c_char, c_int, c_long, c_longlong, c_short, c_uint, c_ulong, c_ulonglong, c_ushort, c_float, c_double,
}};")?;
writeln!(
dest,
"#[cfg(windows)] pub type c_char = i8;
#[cfg(windows)] pub type c_schar = i8;
#[cfg(windows)] pub type c_uchar = u8;
#[cfg(windows)] pub type c_short = i16;
#[cfg(windows)] pub type c_ushort = u16;
#[cfg(windows)] pub type c_int = i32;
#[cfg(windows)] pub type c_uint = u32;
#[cfg(windows)] pub type c_long = i32;
#[cfg(windows)] pub type c_ulong = u32;
#[cfg(windows)] pub type c_longlong = i64;
#[cfg(windows)] pub type c_ulonglong = u64;
#[cfg(windows)] pub type c_float = f32;
#[cfg(windows)] pub type c_double = f64;"
)?;
writeln!(dest, "type OptVoidPtr = Option<NonNull<c_void>>;")?;
Ok(())
}
fn write_meta_loader<W>(dest: &mut W) -> io::Result<()>
where
W: io::Write,
{
writeln!(
dest,
r#"#[inline(never)]
unsafe fn meta_loader(
loader: &mut dyn FnMut(*const c_char) -> *const c_void,
names: &[&[u8]]
) -> OptVoidPtr {{
fn ptr_ok(p: *const c_void) -> bool {{
!(p.is_null() || p as usize % core::mem::align_of::<usize>() == 0)
}}
for name in names.iter() {{
let p = loader(name.as_ptr() as *const c_char);
if ptr_ok(p) {{
return NonNull::new(p as *mut c_void);
}}
}}
None
}}
"#
)
}
fn write_type_aliases<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
W: io::Write,
{
writeln!(
dest,
r#"pub use types::*;
pub mod types {{
use super::*;"#
)?;
super::gen_types(registry.api, dest)?;
writeln!(dest, "}}")
}
fn write_enums<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
W: io::Write,
{
writeln!(
dest,
r#"pub use consts::*;
pub mod consts {{
use super::*;"#
)?;
for enm in ®istry.enums {
super::gen_enum_item(enm, "", dest)?;
}
writeln!(dest, "}}")
}
fn write_fns<W>(
registry: &Registry,
dest: &mut W,
trace: bool,
) -> io::Result<()>
where
W: io::Write,
{
writeln!(
dest,
r#"pub use functions::*;
pub mod functions {{
use super::*;"#
)?;
for cmd in ®istry.cmds {
match registry.api {
Api::Gl => {
writeln!(dest, "/// See [gl{name}](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/gl{name}.xhtml)",
name = cmd.proto.ident,
)?;
}
Api::Gles2 => {
if registry.version == (2, 0) {
writeln!(dest, "/// See [gl{name}](https://www.khronos.org/registry/OpenGL-Refpages/es2.0/html/gl{name}.xhtml)",
name = cmd.proto.ident,
)?;
} else if registry.version == (3, 0) {
writeln!(dest, "/// See [gl{name}](https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/gl{name}.xhtml)",
name = cmd.proto.ident,
)?;
} else if registry.version == (3, 1) {
writeln!(dest, "/// See [gl{name}](https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/gl{name}.xhtml)",
name = cmd.proto.ident,
)?;
} else if registry.version == (3, 2) {
writeln!(dest, "/// See [gl{name}](https://www.khronos.org/registry/OpenGL-Refpages/es3/html/gl{name}.xhtml)",
name = cmd.proto.ident,
)?;
}
}
_ => (),
}
if let Some(v) = registry.aliases.get(&cmd.proto.ident) {
writeln!(dest, "/// ")?;
writeln!(dest, "/// Fallbacks: {}", v.join(", "))?;
}
writeln!(
dest,
r#"#[inline]
pub unsafe fn {name}({params}) {return_suffix} {{
{trace_msg}transmute::<
Option<NonNull<c_void>>,
extern "system" fn({typed_params}) {return_suffix}
>(storage::{name})({idents})
}}"#,
name = cmd.proto.ident,
params = super::gen_parameters(cmd, true, true).join(", "),
typed_params = super::gen_parameters(cmd, false, true).join(", "),
return_suffix = if cmd.proto.ty != "()" {
format!("-> {}", cmd.proto.ty)
} else {
String::new()
},
idents = super::gen_parameters(cmd, true, false).join(", "),
trace_msg = if trace {
format!(
r#"trace!("{}");
"#,
cmd.proto.ident
)
} else {
String::new()
}
)?;
}
writeln!(dest, "}}")
}
fn write_ptrs<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
W: io::Write,
{
writeln!(
dest,
"mod storage {{
use super::*;"
)?;
for c in ®istry.cmds {
writeln!(
dest,
"pub static mut {name}: OptVoidPtr = None;",
name = c.proto.ident
)?;
}
writeln!(dest, "}}")
}
fn write_fn_mods<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
W: io::Write,
{
for c in ®istry.cmds {
let fallbacks = match registry.aliases.get(&c.proto.ident) {
Some(v) => {
let names = v
.iter()
.map(|name| {
format!(
r#",b"{}\0""#,
super::gen_symbol_name(registry.api, &name[..])
)
})
.collect::<Vec<_>>();
names.join(" ")
}
None => "".to_string(),
};
let fnname = &c.proto.ident[..];
let symbol = super::gen_symbol_name(registry.api, &c.proto.ident[..]);
let symbol = &symbol[..];
writeln!(
dest,
r#"
pub mod {fnname} {{
use super::*;
#[inline]
pub fn is_loaded() -> bool {{
unsafe {{ storage::{fnname}.is_some() }}
}}
/// Load {fnname} using the provided loader.
///
/// ## Safety
///
/// * This writes to a static global. If you're accessing GL from more than one thread you can cause a data race.
/// * This mostly trusts whatever the loader function says, so the loader must provide the correct pointer or a null pointer.
pub unsafe fn load_with<F>(mut load_fn: F)
where
F: FnMut(*const c_char) -> *const c_void {{
storage::{fnname} = meta_loader(&mut load_fn, &[b"{symbol}\0" {fallbacks}]);
}}
}}
"#,
fnname = fnname,
fallbacks = fallbacks,
symbol = symbol
)?;
}
Ok(())
}
fn write_load_fn<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
W: io::Write,
{
writeln!(dest,
"/// Load each OpenGL symbol using a provided loader function.
///
/// This allows for the use of functions like `glfwGetProcAddress` or `SDL_GL_GetProcAddress`.
///
/// ```ignore
/// gl::load_with(|ptr| SDL_GL_GetProcAddress(ptr));
/// ```
///
/// ## Safety
///
/// * This writes to the static global function pointers. If you're accessing GL from more than one thread you can cause a data race.
/// * This mostly trusts whatever the loader function says, so the loader must provide the correct pointer or a null pointer for each request.
pub unsafe fn load_with<F>(mut load_fn: F)
where
F: FnMut(*const c_char) -> *const c_void {{
#[inline(never)]
unsafe fn inner(load_fn: &mut dyn FnMut(*const c_char) -> *const c_void) {{"
)?;
for c in ®istry.cmds {
writeln!(
dest,
" {cmd_name}::load_with(&mut *load_fn);",
cmd_name = &c.proto.ident[..]
)?;
}
writeln!(
dest,
" }}
inner(&mut load_fn)
}}
"
)
}