Skip to content

Commit 0a4c86a

Browse files
MortimerGoroemilio
authored andcommitted
Implement WGL (#64)
* Implement WGL * Minor fixes and improve rust formatting * Add attributions to the glutin code and format functions * Check for null HDC value * Use a dummy context for wglGetProcAddress when needed instead of loading gl symbols after a context creation: wglGetProcAddress only works in the presence of a valid GL context, so we use a dummy ctx when the caller calls wglGetProcAddress without a valid GL context bound * Remove unneeded wglMakeCurrent & minor fixes * Improve disposed winapi & WGL resources in error paths
1 parent 68447da commit 0a4c86a

File tree

8 files changed

+1041
-2
lines changed

8 files changed

+1041
-2
lines changed

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,10 @@ cgl = "0.1"
3232
[target.'cfg(target_os = "linux")'.dependencies.x11]
3333
version = "2.3.0"
3434
features = ["xlib"]
35+
36+
[target.'cfg(target_os = "windows")'.dependencies]
37+
winapi = "0.2"
38+
gdi32-sys = "0.2"
39+
user32-sys = "0.2"
40+
kernel32-sys = "0.2"
41+
lazy_static = "0.2"

build.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,31 @@ fn main() {
2121
.write_bindings(gl_generator::StaticGenerator, &mut file).unwrap();
2222
println!("cargo:rustc-link-lib=EGL");
2323
}
24+
25+
if target.contains("windows") {
26+
let mut file = File::create(&dest.join("wgl_bindings.rs")).unwrap();
27+
Registry::new(Api::Wgl, (1, 0), Profile::Core, Fallbacks::All, [])
28+
.write_bindings(gl_generator::StaticGenerator, &mut file)
29+
.unwrap();
30+
31+
let mut file = File::create(&dest.join("wgl_extra_bindings.rs")).unwrap();
32+
Registry::new(Api::Wgl, (1, 0), Profile::Core, Fallbacks::All, [
33+
"WGL_ARB_create_context",
34+
"WGL_ARB_create_context_profile",
35+
"WGL_ARB_create_context_robustness",
36+
"WGL_ARB_context_flush_control",
37+
"WGL_ARB_extensions_string",
38+
"WGL_ARB_framebuffer_sRGB",
39+
"WGL_ARB_multisample",
40+
"WGL_ARB_pixel_format",
41+
"WGL_ARB_pixel_format_float",
42+
"WGL_EXT_create_context_es2_profile",
43+
"WGL_EXT_extensions_string",
44+
"WGL_EXT_framebuffer_sRGB",
45+
"WGL_EXT_swap_control",
46+
])
47+
.write_bindings(gl_generator::StructGenerator, &mut file).unwrap();
48+
49+
50+
}
2451
}

src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,17 @@ extern crate cgl;
1515
extern crate core_foundation;
1616
#[cfg(feature="osmesa")]
1717
extern crate osmesa_sys;
18+
#[cfg(target_os = "windows")]
19+
extern crate winapi;
20+
#[cfg(target_os = "windows")]
21+
extern crate kernel32;
22+
#[cfg(target_os = "windows")]
23+
extern crate gdi32;
24+
#[cfg(target_os = "windows")]
25+
extern crate user32;
26+
#[cfg(target_os = "windows")]
27+
#[macro_use]
28+
extern crate lazy_static;
1829

1930
mod platform;
2031
pub use platform::{NativeGLContext, NativeGLContextMethods, NativeGLContextHandle};

src/platform/mod.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ pub mod with_cgl;
3939
#[cfg(target_os="macos")]
4040
pub use self::with_cgl::{NativeGLContext, NativeGLContextHandle};
4141

42-
#[cfg(not(any(target_os="linux", target_os="android", target_os="macos")))]
42+
#[cfg(target_os="windows")]
43+
pub mod with_wgl;
44+
#[cfg(target_os="windows")]
45+
pub use self::with_wgl::{NativeGLContext, NativeGLContextHandle};
46+
47+
#[cfg(not(any(target_os="linux", target_os="android", target_os="macos", target_os="windows")))]
4348
pub mod not_implemented;
44-
#[cfg(not(any(target_os="linux", target_os="android", target_os="macos")))]
49+
#[cfg(not(any(target_os="linux", target_os="android", target_os="macos", target_os="windows")))]
4550
pub use self::not_implemented::{NativeGLContext, NativeGLContextHandle};

src/platform/with_wgl/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/// WGL bindings
2+
pub mod wgl {
3+
include!(concat!(env!("OUT_DIR"), "/wgl_bindings.rs"));
4+
}
5+
6+
/// Functions that are not necessarly always available
7+
pub mod wgl_ext {
8+
include!(concat!(env!("OUT_DIR"), "/wgl_extra_bindings.rs"));
9+
}
10+
11+
mod wgl_attributes;
12+
mod native_gl_context;
13+
mod utils;
14+
pub use self::native_gl_context::NativeGLContext;
15+
pub use self::native_gl_context::NativeGLContextHandle;
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
use platform::NativeGLContextMethods;
2+
use std::ffi::CString;
3+
use std::os::raw::c_void;
4+
use std::ptr;
5+
6+
use winapi;
7+
use user32;
8+
use kernel32;
9+
use super::wgl;
10+
use super::wgl_attributes::*;
11+
use super::utils;
12+
13+
// Wrappers to satisfy `Sync`.
14+
struct HMODULEWrapper(winapi::HMODULE);
15+
unsafe impl Sync for HMODULEWrapper {}
16+
17+
lazy_static! {
18+
static ref GL_LIB: Option<HMODULEWrapper> = {
19+
let p = unsafe { kernel32::LoadLibraryA(b"opengl32.dll\0".as_ptr() as *const _) };
20+
if p.is_null() {
21+
error!("WGL: opengl32.dll not found!");
22+
None
23+
} else {
24+
debug!("WGL: opengl32.dll loaded!");
25+
Some(HMODULEWrapper(p))
26+
}
27+
};
28+
29+
static ref PROC_ADDR_CTX: Option<NativeGLContext> = {
30+
match unsafe { utils::create_offscreen(ptr::null_mut(), &WGLAttributes::default()) } {
31+
Ok(ref res) => {
32+
let ctx = NativeGLContext {
33+
render_ctx: res.0,
34+
device_ctx: res.1,
35+
weak: false,
36+
};
37+
Some(ctx)
38+
}
39+
Err(s) => {
40+
error!("Error creating GetProcAddress helper context: {}", s);
41+
None
42+
}
43+
}
44+
};
45+
}
46+
47+
pub struct NativeGLContext {
48+
render_ctx: winapi::HGLRC,
49+
device_ctx: winapi::HDC,
50+
weak: bool,
51+
}
52+
53+
impl Drop for NativeGLContext {
54+
fn drop(&mut self) {
55+
unsafe {
56+
if !self.weak {
57+
// the context to be deleted needs to be unbound
58+
self.unbind().unwrap();
59+
wgl::DeleteContext(self.render_ctx as *const _);
60+
let window = user32::WindowFromDC(self.device_ctx);
61+
debug_assert!(!window.is_null());
62+
user32::ReleaseDC(window, self.device_ctx);
63+
user32::DestroyWindow(window);
64+
}
65+
}
66+
}
67+
}
68+
69+
unsafe impl Send for NativeGLContext {}
70+
unsafe impl Sync for NativeGLContext {}
71+
72+
pub struct NativeGLContextHandle(winapi::HGLRC, winapi::HDC);
73+
unsafe impl Send for NativeGLContextHandle {}
74+
unsafe impl Sync for NativeGLContextHandle {}
75+
76+
impl NativeGLContextMethods for NativeGLContext {
77+
type Handle = NativeGLContextHandle;
78+
79+
fn get_proc_address(addr: &str) -> *const () {
80+
let addr = CString::new(addr.as_bytes()).unwrap();
81+
let addr = addr.as_ptr();
82+
unsafe {
83+
if wgl::GetCurrentContext().is_null() {
84+
// wglGetProcAddress only works in the presence of a valid GL context
85+
// We use a dummy ctx when the caller calls this function without a valid GL context
86+
if let Some(ref ctx) = *PROC_ADDR_CTX {
87+
if ctx.make_current().is_err() {
88+
return ptr::null_mut();
89+
}
90+
} else {
91+
return ptr::null_mut();
92+
}
93+
}
94+
95+
let p = wgl::GetProcAddress(addr) as *const _;
96+
if !p.is_null() {
97+
return p;
98+
}
99+
// wglGetProcAddress​ doesn't return function pointers for some legacy functions,
100+
// (the ones coming from OpenGL 1.1)
101+
// These functions are exported by the opengl32.dll itself,
102+
// so we have to fallback to kernel32 getProcAddress if wglGetProcAddress​ return null
103+
match *GL_LIB {
104+
Some(ref lib) => kernel32::GetProcAddress(lib.0, addr) as *const _,
105+
None => ptr::null_mut(),
106+
}
107+
}
108+
}
109+
110+
fn create_shared(with: Option<&Self::Handle>) -> Result<NativeGLContext, &'static str> {
111+
let render_ctx = match with {
112+
Some(ref handle) => handle.0,
113+
None => ptr::null_mut(),
114+
};
115+
match unsafe { utils::create_offscreen(render_ctx, &WGLAttributes::default()) } {
116+
Ok(ref res) => {
117+
let ctx = NativeGLContext {
118+
render_ctx: res.0,
119+
device_ctx: res.1,
120+
weak: false,
121+
};
122+
Ok(ctx)
123+
}
124+
Err(s) => {
125+
error!("WGL: {}", s);
126+
Err("Error creating WGL context")
127+
}
128+
}
129+
}
130+
131+
fn is_current(&self) -> bool {
132+
unsafe { wgl::GetCurrentContext() == self.render_ctx as *const c_void }
133+
}
134+
135+
fn current() -> Option<Self> {
136+
if let Some(handle) = Self::current_handle() {
137+
Some(NativeGLContext {
138+
render_ctx: handle.0,
139+
device_ctx: handle.1,
140+
weak: true,
141+
})
142+
} else {
143+
None
144+
}
145+
}
146+
147+
fn current_handle() -> Option<Self::Handle> {
148+
let ctx = unsafe { wgl::GetCurrentContext() };
149+
let hdc = unsafe { wgl::GetCurrentDC() };
150+
if ctx.is_null() || hdc.is_null() {
151+
None
152+
} else {
153+
Some(NativeGLContextHandle(ctx as winapi::HGLRC, hdc as winapi::HDC))
154+
}
155+
}
156+
157+
fn make_current(&self) -> Result<(), &'static str> {
158+
unsafe {
159+
if wgl::MakeCurrent(self.device_ctx as *const _, self.render_ctx as *const _) != 0 {
160+
Ok(())
161+
} else {
162+
Err("WGL::makeCurrent failed")
163+
}
164+
}
165+
}
166+
167+
fn unbind(&self) -> Result<(), &'static str> {
168+
unsafe {
169+
if self.is_current() && wgl::MakeCurrent(ptr::null_mut(), ptr::null_mut()) == 0 {
170+
Err("WGL::MakeCurrent (on unbind)")
171+
} else {
172+
Ok(())
173+
}
174+
}
175+
}
176+
177+
fn handle(&self) -> Self::Handle {
178+
NativeGLContextHandle(self.render_ctx, self.device_ctx)
179+
}
180+
}

0 commit comments

Comments
 (0)