1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
//! See [`gl-headless` docs] instead.
//!
//! [`gl-headless` docs]: https://docs.rs/gl-headless/*/gl_headless/
#![forbid(unsafe_code)]
#![forbid(elided_lifetimes_in_paths)]
use proc_macro::TokenStream;
use quote::quote_spanned;
use syn::spanned::Spanned;
use syn::{parse_macro_input, ItemFn, LitStr};
/// Creates a headless OpenGL context, that is valid throughout
/// the scope of the function.
///
/// See examples in the [crate root].
///
/// # Attributes
///
/// - `version = "3.3"`: Specify the OpenGL version, e.g.:
/// `#[gl_headless(version = "3.3")]`
///
/// # Example
///
/// ```toml
/// [dependencies]
/// gl = "0.14"
/// gl-headless = "0.2"
/// ```
///
/// ```rust
/// use gl_headless::gl_headless;
///
/// #[gl_headless]
/// unsafe fn main() {
/// let (mut major, mut minor) = (0, 0);
/// gl::GetIntegerv(gl::MAJOR_VERSION, &mut major);
/// gl::GetIntegerv(gl::MINOR_VERSION, &mut minor);
/// println!("OpenGL {major}.{minor}");
/// }
/// ```
///
/// [crate root]: https://docs.rs/gl-headless/*/gl_headless/
#[proc_macro_attribute]
pub fn gl_headless(args: TokenStream, item: TokenStream) -> TokenStream {
let mut version: Option<LitStr> = None;
let args_parser = syn::meta::parser(|meta| {
if meta.path.is_ident("version") {
version = Some(meta.value()?.parse()?);
Ok(())
} else {
Err(meta.error("unsupported attribute"))
}
});
parse_macro_input!(args with args_parser);
let item_fn: ItemFn = parse_macro_input!(item);
let attrs = &item_fn.attrs;
let vis = &item_fn.vis;
let sig = &item_fn.sig;
let ident = &sig.ident;
let mut new_sig = sig.clone();
let call_wrap_unsafe = sig.unsafety.is_some() && (ident.to_string() == "main");
let call = if call_wrap_unsafe {
new_sig.unsafety = None;
quote_spanned! { sig.span() =>
unsafe {
#ident()
}
}
} else {
quote_spanned! { sig.span() =>
#ident()
}
};
let set_version_str = version.map(|version| {
quote_spanned! { version.span() =>
builder.set_version_str(#version);
}
});
quote_spanned! { sig.span() =>
#(#attrs)*
#vis #new_sig {
let _ctx = {
#[allow(unused_mut)]
let mut builder = ::gl_headless::_internals::GLContextBuilder::new();
#set_version_str
builder.build().unwrap()
};
#item_fn
#call
}
}
.into()
}