use crate::entry_points::{gl_entry_points, GetProcAddress};
use crate::gl_consts;
use std::ffi::{c_char, CStr};
pub struct GlBasicInfoInterface {
basic: BasicInfoEntryPoints,
}
gl_entry_points! {
table BasicInfoEntryPoints for GlBasicInfoInterface.basic(get, info: ());
required unsafe pub fn get_integerv_native(name: i32, rv: *mut i32) = get("glGetIntegerv");
required unsafe pub fn get_floatv_native(name: i32, rv: *mut f32) = get("glGetFloatv");
required safe pub fn get_string_native(v: i32) -> *const c_char = get("glGetString");
required safe pub fn get_stringi_native(v: i32, v1: i32) -> *const c_char = get("glGetStringi");
required safe pub fn get_error() -> i32 = get("glGetError");
}
const SCRATCH_VALUES: usize = 256;
impl GlBasicInfoInterface {
pub unsafe fn new(get_proc_address: &GetProcAddress) -> Self {
Self { basic: BasicInfoEntryPoints::load(get_proc_address, &()) }
}
pub fn get_integerv(&self, name: i32) -> i32 {
let mut rv = [0i32; SCRATCH_VALUES];
unsafe { self.get_integerv_native(name, rv.as_mut_ptr()) };
rv[0]
}
pub fn get_floatv(&self, name: i32) -> f32 {
let mut rv = [0f32; SCRATCH_VALUES];
unsafe { self.get_floatv_native(name, rv.as_mut_ptr()) };
rv[0]
}
fn to_string(ptr: *const c_char) -> Option<String> {
if ptr.is_null() {
return None;
}
Some(unsafe { CStr::from_ptr(ptr) }.to_string_lossy().into_owned())
}
pub fn get_string(&self, v: i32) -> Option<String> {
Self::to_string(self.get_string_native(v))
}
pub fn get_string_indexed(&self, v: i32, index: i32) -> Option<String> {
Self::to_string(self.get_stringi_native(v, index))
}
pub fn get_extensions(&self) -> Vec<String> {
if let Some(sp) = self.get_string(gl_consts::GL_EXTENSIONS) {
return sp.split(' ').map(str::to_string).collect();
}
self.get_error();
let count = self.get_integerv(gl_consts::GL_NUM_EXTENSIONS);
let mut rv = Vec::with_capacity(count.max(0) as usize);
for c in 0..count {
if let Some(extension) = self.get_string_indexed(gl_consts::GL_EXTENSIONS, c) {
rv.push(extension);
}
}
rv
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::testing::FakeGl;
use crate::{GlProfileType, GlVersion};
#[test]
fn single_values_are_read_through_the_out_parameter() {
let fake = FakeGl::install(GlVersion::new(GlProfileType::OpenGLES, 3, 0));
fake.set_integer(gl_consts::GL_FRAMEBUFFER_BINDING, 7);
fake.set_float(0x0B21, 2.5);
let basic = unsafe { GlBasicInfoInterface::new(&fake.loader()) };
assert_eq!(7, basic.get_integerv(gl_consts::GL_FRAMEBUFFER_BINDING));
assert_eq!(2.5, basic.get_floatv(0x0B21));
}
#[test]
fn strings_are_copied_and_missing_ones_are_none() {
let fake = FakeGl::install(GlVersion::new(GlProfileType::OpenGLES, 3, 0));
fake.set_string(gl_consts::GL_VENDOR, Some("Fake Vendor"));
fake.set_string(gl_consts::GL_RENDERER, None);
let basic = unsafe { GlBasicInfoInterface::new(&fake.loader()) };
assert_eq!(Some("Fake Vendor".to_string()), basic.get_string(gl_consts::GL_VENDOR));
assert_eq!(None, basic.get_string(gl_consts::GL_RENDERER));
}
#[test]
fn extensions_come_from_the_space_separated_string_when_there_is_one() {
let fake = FakeGl::install(GlVersion::new(GlProfileType::OpenGLES, 2, 0));
fake.set_string(gl_consts::GL_EXTENSIONS, Some("GL_OES_vertex_array_object GL_OES_EGL_image"));
let basic = unsafe { GlBasicInfoInterface::new(&fake.loader()) };
assert_eq!(vec!["GL_OES_vertex_array_object", "GL_OES_EGL_image"], basic.get_extensions());
assert!(!fake.calls().iter().any(|call| call.starts_with("glGetStringi")));
}
#[test]
fn extensions_are_enumerated_when_the_string_is_not_available() {
let fake = FakeGl::install(GlVersion::new(GlProfileType::OpenGL, 4, 1));
fake.set_string(gl_consts::GL_EXTENSIONS, None);
fake.set_indexed_extensions(&["GL_ARB_one", "GL_ARB_two", "GL_ARB_three"]);
let basic = unsafe { GlBasicInfoInterface::new(&fake.loader()) };
assert_eq!(vec!["GL_ARB_one", "GL_ARB_two", "GL_ARB_three"], basic.get_extensions());
assert_eq!(gl_consts::GL_NO_ERROR, basic.get_error());
}
#[test]
#[should_panic(expected = "Unable to find an entry point named 'get_stringi_native'")]
fn a_missing_required_entry_point_fails_the_construction() {
let fake = FakeGl::install(GlVersion::new(GlProfileType::OpenGLES, 3, 0));
fake.remove("glGetStringi");
let _ = unsafe { GlBasicInfoInterface::new(&fake.loader()) };
}
}