use crate::platform::NativeGLContextMethods;
use crate::gl_context::GLContextDispatcher;
use crate::GLVersion;
use sparkle::gl;
use std::ffi::CString;
use std::os::raw::c_void;
use std::ptr;
use std::sync::mpsc;
use winapi::shared::windef::{HDC, HGLRC};
use winapi::shared::minwindef::HMODULE;
use winapi::um::libloaderapi::{LoadLibraryA, GetProcAddress};
use winapi::um::winuser::{WindowFromDC, ReleaseDC, DestroyWindow};
use super::wgl;
use super::wgl_attributes::*;
use super::utils;
struct HMODULEWrapper(HMODULE);
unsafe impl Sync for HMODULEWrapper {}
lazy_static! {
static ref GL_LIB: Option<HMODULEWrapper> = {
let p = unsafe { LoadLibraryA(b"opengl32.dll\0".as_ptr() as *const _) };
if p.is_null() {
error!("WGL: opengl32.dll not found!");
None
} else {
debug!("WGL: opengl32.dll loaded!");
Some(HMODULEWrapper(p))
}
};
static ref PROC_ADDR_CTX: Option<NativeGLContext> = {
match unsafe { utils::create_offscreen(ptr::null_mut(), &WGLAttributes::default()) } {
Ok(ref res) => {
let ctx = NativeGLContext {
render_ctx: res.0,
device_ctx: res.1,
weak: false,
};
Some(ctx)
}
Err(s) => {
error!("Error creating GetProcAddress helper context: {}", s);
None
}
}
};
}
pub struct NativeGLContext {
render_ctx: HGLRC,
device_ctx: HDC,
weak: bool,
}
impl Drop for NativeGLContext {
fn drop(&mut self) {
unsafe {
if !self.weak {
self.unbind().unwrap();
wgl::DeleteContext(self.render_ctx as *const _);
let window = WindowFromDC(self.device_ctx);
debug_assert!(!window.is_null());
ReleaseDC(window, self.device_ctx);
DestroyWindow(window);
}
}
}
}
unsafe impl Send for NativeGLContext {}
unsafe impl Sync for NativeGLContext {}
pub struct NativeGLContextHandle(HGLRC, HDC);
unsafe impl Send for NativeGLContextHandle {}
unsafe impl Sync for NativeGLContextHandle {}
impl NativeGLContextMethods for NativeGLContext {
type Handle = NativeGLContextHandle;
fn get_proc_address(addr: &str) -> *const () {
let addr = CString::new(addr.as_bytes()).unwrap();
let addr = addr.as_ptr();
unsafe {
if wgl::GetCurrentContext().is_null() {
if let Some(ref ctx) = *PROC_ADDR_CTX {
if ctx.make_current().is_err() {
return ptr::null_mut();
}
} else {
return ptr::null_mut();
}
}
let p = wgl::GetProcAddress(addr) as *const ();
if !p.is_null() {
return p;
}
match *GL_LIB {
Some(ref lib) => GetProcAddress(lib.0, addr) as *const _,
None => ptr::null_mut(),
}
}
}
fn create_shared(with: Option<&Self::Handle>,
api_type: &gl::GlType,
api_version: GLVersion) -> Result<Self, &'static str> {
Self::create_shared_with_dispatcher(with, api_type, api_version, None)
}
fn create_shared_with_dispatcher(with: Option<&Self::Handle>,
api_type: &gl::GlType,
api_version: GLVersion,
dispatcher: Option<Box<GLContextDispatcher>>)
-> Result<NativeGLContext, &'static str> {
let (render_ctx, device_ctx) = match with {
Some(ref handle) => (handle.0, handle.1),
None => (ptr::null_mut(), ptr::null_mut())
};
if let Some(ref dispatcher) = dispatcher {
let (tx, rx) = mpsc::channel();
dispatcher.dispatch(Box::new(move || {
let result = unsafe {
if wgl::MakeCurrent(ptr::null_mut(), ptr::null_mut()) == 0 {
Err(())
} else {
Ok(())
}
};
tx.send(result).unwrap();
}));
if rx.recv().unwrap().is_err() {
return Err("Error creating WGL context: WGL::MakeCurrent failed in shared context")
}
}
let mut attributes = WGLAttributes::default();
attributes.opengl_es = match *api_type {
gl::GlType::Gles => true,
_ => false,
};
match api_version {
GLVersion::Major(major) => {
attributes.major_version = major as u32;
attributes.minor_version = if attributes.opengl_es {
0
} else {
1
};
},
GLVersion::MajorMinor(major, minor) => {
attributes.major_version = major as u32;
attributes.minor_version = minor as u32;
}
}
let result = match unsafe { utils::create_offscreen(render_ctx, &attributes) } {
Ok(ref res) => {
let ctx = NativeGLContext {
render_ctx: res.0,
device_ctx: res.1,
weak: false,
};
Ok(ctx)
}
Err(s) => {
error!("WGL: {}", s);
Err("Error creating WGL context")
}
};
if let Some(ref dispatcher) = dispatcher {
let (tx, rx) = mpsc::channel();
let handle = NativeGLContextHandle(render_ctx, device_ctx);
dispatcher.dispatch(Box::new(move || {
unsafe {
if wgl::MakeCurrent(handle.1 as *const _, handle.0 as *const _) == 0 {
error!("Error restoring WGL shared context: WGL MakeCurrent failed");
}
};
tx.send(()).unwrap();
}));
rx.recv().unwrap();
}
result
}
fn is_current(&self) -> bool {
unsafe { wgl::GetCurrentContext() == self.render_ctx as *const c_void }
}
fn current() -> Option<Self> {
if let Some(handle) = Self::current_handle() {
Some(NativeGLContext {
render_ctx: handle.0,
device_ctx: handle.1,
weak: true,
})
} else {
None
}
}
fn current_handle() -> Option<Self::Handle> {
let ctx = unsafe { wgl::GetCurrentContext() };
let hdc = unsafe { wgl::GetCurrentDC() };
if ctx.is_null() || hdc.is_null() {
None
} else {
Some(NativeGLContextHandle(ctx as HGLRC, hdc as HDC))
}
}
fn make_current(&self) -> Result<(), &'static str> {
unsafe {
if wgl::MakeCurrent(self.device_ctx as *const _, self.render_ctx as *const _) != 0 {
Ok(())
} else {
Err("WGL::makeCurrent failed")
}
}
}
fn unbind(&self) -> Result<(), &'static str> {
unsafe {
if self.is_current() && wgl::MakeCurrent(ptr::null_mut(), ptr::null_mut()) == 0 {
Err("WGL::MakeCurrent (on unbind)")
} else {
Ok(())
}
}
}
fn handle(&self) -> Self::Handle {
NativeGLContextHandle(self.render_ctx, self.device_ctx)
}
}