#![cfg(any(target_os = "windows"))]
mod make_current_guard;
use crate::{
Api, ContextError, CreationError, GlAttributes, GlProfile, GlRequest,
PixelFormat, PixelFormatRequirements, ReleaseBehavior, Robustness,
};
use self::make_current_guard::CurrentContextGuard;
use glutin_wgl_sys as gl;
use winapi::shared::minwindef::HMODULE;
use winapi::shared::minwindef::*;
use winapi::shared::ntdef::LPCWSTR;
use winapi::shared::windef::{HDC, HGLRC, HWND};
use winapi::um::libloaderapi::*;
use winapi::um::wingdi::*;
use winapi::um::winuser::*;
use std::ffi::{CStr, CString, OsStr};
use std::os::raw;
use std::os::windows::ffi::OsStrExt;
#[derive(Debug)]
pub struct Context {
context: ContextWrapper,
hdc: HDC,
gl_library: HMODULE,
pixel_format: PixelFormat,
}
#[derive(Debug)]
struct WindowWrapper(HWND, HDC);
impl Drop for WindowWrapper {
#[inline]
fn drop(&mut self) {
unsafe {
DestroyWindow(self.0);
}
}
}
#[derive(Debug)]
struct ContextWrapper(HGLRC);
impl Drop for ContextWrapper {
#[inline]
fn drop(&mut self) {
unsafe {
gl::wgl::DeleteContext(self.0 as *const _);
}
}
}
impl Context {
#[inline]
pub unsafe fn new(
pf_reqs: &PixelFormatRequirements,
opengl: &GlAttributes<HGLRC>,
win: HWND,
) -> Result<Context, CreationError> {
let hdc = GetDC(win);
if hdc.is_null() {
let err = Err(CreationError::OsError(format!(
"GetDC function failed: {}",
std::io::Error::last_os_error()
)));
return err;
}
let extra_functions = load_extra_functions(win)?;
let extensions = if extra_functions.GetExtensionsStringARB.is_loaded() {
let data = extra_functions.GetExtensionsStringARB(hdc as *const _);
let data = CStr::from_ptr(data).to_bytes().to_vec();
String::from_utf8(data).unwrap()
} else if extra_functions.GetExtensionsStringEXT.is_loaded() {
let data = extra_functions.GetExtensionsStringEXT();
let data = CStr::from_ptr(data).to_bytes().to_vec();
String::from_utf8(data).unwrap()
} else {
format!("")
};
let use_arb_for_pixel_format = extensions
.split(' ')
.find(|&i| i == "WGL_ARB_pixel_format")
.is_some();
let mut pixel_format_id = GetPixelFormat(hdc);
if pixel_format_id == 0 {
let id = if use_arb_for_pixel_format {
choose_arb_pixel_format_id(
&extra_functions,
&extensions,
hdc,
pf_reqs,
)
.map_err(|_| CreationError::NoAvailablePixelFormat)?
} else {
choose_native_pixel_format_id(hdc, pf_reqs)
.map_err(|_| CreationError::NoAvailablePixelFormat)?
};
set_pixel_format(hdc, id)?;
pixel_format_id = id;
}
let pixel_format = if use_arb_for_pixel_format {
choose_arb_pixel_format(
&extra_functions,
&extensions,
hdc,
pixel_format_id,
)
.map_err(|_| CreationError::NoAvailablePixelFormat)?
} else {
choose_native_pixel_format(hdc, pf_reqs, pixel_format_id)
.map_err(|_| CreationError::NoAvailablePixelFormat)?
};
let context = create_context(
Some((&extra_functions, pf_reqs, opengl, &extensions)),
win,
hdc,
)?;
let gl_library = load_opengl32_dll()?;
if extensions
.split(' ')
.find(|&i| i == "WGL_EXT_swap_control")
.is_some()
{
let _guard = CurrentContextGuard::make_current(hdc, context.0)?;
if extra_functions.SwapIntervalEXT(if opengl.vsync { 1 } else { 0 })
== 0
{
return Err(CreationError::OsError(
"wglSwapIntervalEXT failed".to_string(),
));
}
}
Ok(Context {
context,
hdc,
gl_library,
pixel_format,
})
}
#[inline]
pub fn get_hglrc(&self) -> HGLRC {
self.context.0
}
#[inline]
pub unsafe fn make_current(&self) -> Result<(), ContextError> {
if gl::wgl::MakeCurrent(
self.hdc as *const _,
self.context.0 as *const _,
) != 0
{
Ok(())
} else {
Err(ContextError::IoError(std::io::Error::last_os_error()))
}
}
#[inline]
pub unsafe fn make_not_current(&self) -> Result<(), ContextError> {
if self.is_current()
&& gl::wgl::MakeCurrent(self.hdc as *const _, std::ptr::null()) != 0
{
Ok(())
} else {
Err(ContextError::IoError(std::io::Error::last_os_error()))
}
}
#[inline]
pub fn is_current(&self) -> bool {
unsafe {
gl::wgl::GetCurrentContext() == self.context.0 as *const raw::c_void
}
}
pub fn get_proc_address(&self, addr: &str) -> *const () {
let addr = CString::new(addr.as_bytes()).unwrap();
let addr = addr.as_ptr();
unsafe {
let p = gl::wgl::GetProcAddress(addr) as *const ();
if !p.is_null() {
return p;
}
GetProcAddress(self.gl_library, addr) as *const _
}
}
#[inline]
pub fn swap_buffers(&self) -> Result<(), ContextError> {
unsafe { SwapBuffers(self.hdc) };
Ok(())
}
#[inline]
pub fn get_api(&self) -> Api {
Api::OpenGl
}
#[inline]
pub fn get_pixel_format(&self) -> PixelFormat {
self.pixel_format.clone()
}
}
unsafe impl Send for Context {}
unsafe impl Sync for Context {}
unsafe fn create_context(
extra: Option<(
&gl::wgl_extra::Wgl,
&PixelFormatRequirements,
&GlAttributes<HGLRC>,
&str,
)>,
_: HWND,
hdc: HDC,
) -> Result<ContextWrapper, CreationError> {
let share;
if let Some((extra_functions, _pf_reqs, opengl, extensions)) = extra {
share = opengl.sharing.unwrap_or(std::ptr::null_mut());
if extensions
.split(' ')
.find(|&i| i == "WGL_ARB_create_context")
.is_some()
{
let mut attributes = Vec::new();
match opengl.version {
GlRequest::Latest => {}
GlRequest::Specific(Api::OpenGl, (major, minor)) => {
attributes.push(
gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as raw::c_int,
);
attributes.push(major as raw::c_int);
attributes.push(
gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as raw::c_int,
);
attributes.push(minor as raw::c_int);
}
GlRequest::Specific(Api::OpenGlEs, (major, minor)) => {
if extensions
.split(' ')
.find(|&i| i == "WGL_EXT_create_context_es2_profile")
.is_some()
{
attributes.push(
gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB
as raw::c_int,
);
attributes.push(
gl::wgl_extra::CONTEXT_ES2_PROFILE_BIT_EXT
as raw::c_int,
);
} else {
return Err(CreationError::OpenGlVersionNotSupported);
}
attributes.push(
gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as raw::c_int,
);
attributes.push(major as raw::c_int);
attributes.push(
gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as raw::c_int,
);
attributes.push(minor as raw::c_int);
}
GlRequest::Specific(_, _) => {
return Err(CreationError::OpenGlVersionNotSupported);
}
GlRequest::GlThenGles {
opengl_version: (major, minor),
..
} => {
attributes.push(
gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as raw::c_int,
);
attributes.push(major as raw::c_int);
attributes.push(
gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as raw::c_int,
);
attributes.push(minor as raw::c_int);
}
}
if let Some(profile) = opengl.profile {
if extensions
.split(' ')
.find(|&i| i == "WGL_ARB_create_context_profile")
.is_some()
{
let flag = match profile {
GlProfile::Compatibility => {
gl::wgl_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB
}
GlProfile::Core => {
gl::wgl_extra::CONTEXT_CORE_PROFILE_BIT_ARB
}
};
attributes.push(
gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB as raw::c_int,
);
attributes.push(flag as raw::c_int);
} else {
return Err(CreationError::NotSupported(
"required extension \"WGL_ARB_create_context_profile\" not found".to_string(),
));
}
}
let flags = {
let mut flags = 0;
if extensions
.split(' ')
.find(|&i| i == "WGL_ARB_create_context_robustness")
.is_some()
{
match opengl.robustness {
Robustness::RobustNoResetNotification
| Robustness::TryRobustNoResetNotification => {
attributes.push(
gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as raw::c_int,
);
attributes.push(
gl::wgl_extra::NO_RESET_NOTIFICATION_ARB
as raw::c_int,
);
flags = flags
| gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB
as raw::c_int;
}
Robustness::RobustLoseContextOnReset
| Robustness::TryRobustLoseContextOnReset => {
attributes.push(
gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as raw::c_int,
);
attributes.push(
gl::wgl_extra::LOSE_CONTEXT_ON_RESET_ARB
as raw::c_int,
);
flags = flags
| gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB
as raw::c_int;
}
Robustness::NotRobust => (),
Robustness::NoError => (),
}
} else {
match opengl.robustness {
Robustness::RobustNoResetNotification
| Robustness::RobustLoseContextOnReset => {
return Err(CreationError::RobustnessNotSupported);
}
_ => (),
}
}
if opengl.debug {
flags = flags
| gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as raw::c_int;
}
flags
};
attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as raw::c_int);
attributes.push(flags);
attributes.push(0);
let ctx = extra_functions.CreateContextAttribsARB(
hdc as *const raw::c_void,
share as *const raw::c_void,
attributes.as_ptr(),
);
if ctx.is_null() {
return Err(CreationError::OsError(format!(
"wglCreateContextAttribsARB failed: {}",
std::io::Error::last_os_error()
)));
} else {
return Ok(ContextWrapper(ctx as HGLRC));
}
}
} else {
share = std::ptr::null_mut();
}
let ctx = gl::wgl::CreateContext(hdc as *const raw::c_void);
if ctx.is_null() {
return Err(CreationError::OsError(format!(
"wglCreateContext failed: {}",
std::io::Error::last_os_error()
)));
}
if !share.is_null() {
if gl::wgl::ShareLists(share as *const raw::c_void, ctx) == 0 {
return Err(CreationError::OsError(format!(
"wglShareLists failed: {}",
std::io::Error::last_os_error()
)));
}
};
Ok(ContextWrapper(ctx as HGLRC))
}
unsafe fn choose_native_pixel_format_id(
hdc: HDC,
pf_reqs: &PixelFormatRequirements,
) -> Result<raw::c_int, ()> {
if pf_reqs.float_color_buffer {
return Err(());
}
match pf_reqs.multisampling {
Some(0) => (),
None => (),
Some(_) => return Err(()),
};
if pf_reqs.stereoscopy {
return Err(());
}
if pf_reqs.srgb {
return Err(());
}
if pf_reqs.release_behavior != ReleaseBehavior::Flush {
return Err(());
}
let descriptor = PIXELFORMATDESCRIPTOR {
nSize: std::mem::size_of::<PIXELFORMATDESCRIPTOR>() as u16,
nVersion: 1,
dwFlags: {
let f1 = match pf_reqs.double_buffer {
None => PFD_DOUBLEBUFFER,
Some(true) => PFD_DOUBLEBUFFER,
Some(false) => 0,
};
let f2 = if pf_reqs.stereoscopy { PFD_STEREO } else { 0 };
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | f1 | f2
},
iPixelType: PFD_TYPE_RGBA,
cColorBits: pf_reqs.color_bits.unwrap_or(0),
cRedBits: 0,
cRedShift: 0,
cGreenBits: 0,
cGreenShift: 0,
cBlueBits: 0,
cBlueShift: 0,
cAlphaBits: pf_reqs.alpha_bits.unwrap_or(0),
cAlphaShift: 0,
cAccumBits: 0,
cAccumRedBits: 0,
cAccumGreenBits: 0,
cAccumBlueBits: 0,
cAccumAlphaBits: 0,
cDepthBits: pf_reqs.depth_bits.unwrap_or(0),
cStencilBits: pf_reqs.stencil_bits.unwrap_or(0),
cAuxBuffers: 0,
iLayerType: PFD_MAIN_PLANE,
bReserved: 0,
dwLayerMask: 0,
dwVisibleMask: 0,
dwDamageMask: 0,
};
let pf_id = ChoosePixelFormat(hdc, &descriptor);
if pf_id == 0 {
return Err(());
}
Ok(pf_id)
}
unsafe fn choose_native_pixel_format(
hdc: HDC,
pf_reqs: &PixelFormatRequirements,
pf_id: raw::c_int,
) -> Result<PixelFormat, ()> {
let mut output: PIXELFORMATDESCRIPTOR = std::mem::zeroed();
if DescribePixelFormat(
hdc,
pf_id,
std::mem::size_of::<PIXELFORMATDESCRIPTOR>() as u32,
&mut output,
) == 0
{
return Err(());
}
if (output.dwFlags & PFD_DRAW_TO_WINDOW) == 0 {
return Err(());
}
if (output.dwFlags & PFD_SUPPORT_OPENGL) == 0 {
return Err(());
}
if output.iPixelType != PFD_TYPE_RGBA {
return Err(());
}
let pf_desc = PixelFormat {
hardware_accelerated: (output.dwFlags & PFD_GENERIC_FORMAT) == 0,
color_bits: output.cRedBits + output.cGreenBits + output.cBlueBits,
alpha_bits: output.cAlphaBits,
depth_bits: output.cDepthBits,
stencil_bits: output.cStencilBits,
stereoscopy: (output.dwFlags & PFD_STEREO) != 0,
double_buffer: (output.dwFlags & PFD_DOUBLEBUFFER) != 0,
multisampling: None,
srgb: false,
};
if pf_desc.alpha_bits < pf_reqs.alpha_bits.unwrap_or(0) {
return Err(());
}
if pf_desc.depth_bits < pf_reqs.depth_bits.unwrap_or(0) {
return Err(());
}
if pf_desc.stencil_bits < pf_reqs.stencil_bits.unwrap_or(0) {
return Err(());
}
if pf_desc.color_bits < pf_reqs.color_bits.unwrap_or(0) {
return Err(());
}
if let Some(req) = pf_reqs.hardware_accelerated {
if pf_desc.hardware_accelerated != req {
return Err(());
}
}
if let Some(req) = pf_reqs.double_buffer {
if pf_desc.double_buffer != req {
return Err(());
}
}
Ok(pf_desc)
}
unsafe fn choose_arb_pixel_format_id(
extra: &gl::wgl_extra::Wgl,
extensions: &str,
hdc: HDC,
pf_reqs: &PixelFormatRequirements,
) -> Result<raw::c_int, ()> {
let descriptor = {
let mut out: Vec<raw::c_int> = Vec::with_capacity(37);
out.push(gl::wgl_extra::DRAW_TO_WINDOW_ARB as raw::c_int);
out.push(1);
out.push(gl::wgl_extra::SUPPORT_OPENGL_ARB as raw::c_int);
out.push(1);
out.push(gl::wgl_extra::PIXEL_TYPE_ARB as raw::c_int);
if pf_reqs.float_color_buffer {
if extensions
.split(' ')
.find(|&i| i == "WGL_ARB_pixel_format_float")
.is_some()
{
out.push(gl::wgl_extra::TYPE_RGBA_FLOAT_ARB as raw::c_int);
} else {
return Err(());
}
} else {
out.push(gl::wgl_extra::TYPE_RGBA_ARB as raw::c_int);
}
if let Some(hardware_accelerated) = pf_reqs.hardware_accelerated {
out.push(gl::wgl_extra::ACCELERATION_ARB as raw::c_int);
out.push(if hardware_accelerated {
gl::wgl_extra::FULL_ACCELERATION_ARB as raw::c_int
} else {
gl::wgl_extra::NO_ACCELERATION_ARB as raw::c_int
});
}
if let Some(color) = pf_reqs.color_bits {
out.push(gl::wgl_extra::COLOR_BITS_ARB as raw::c_int);
out.push(color as raw::c_int);
}
if let Some(alpha) = pf_reqs.alpha_bits {
out.push(gl::wgl_extra::ALPHA_BITS_ARB as raw::c_int);
out.push(alpha as raw::c_int);
}
if let Some(depth) = pf_reqs.depth_bits {
out.push(gl::wgl_extra::DEPTH_BITS_ARB as raw::c_int);
out.push(depth as raw::c_int);
}
if let Some(stencil) = pf_reqs.stencil_bits {
out.push(gl::wgl_extra::STENCIL_BITS_ARB as raw::c_int);
out.push(stencil as raw::c_int);
}
let double_buffer = pf_reqs.double_buffer.unwrap_or(true);
out.push(gl::wgl_extra::DOUBLE_BUFFER_ARB as raw::c_int);
out.push(if double_buffer { 1 } else { 0 });
if let Some(multisampling) = pf_reqs.multisampling {
if extensions
.split(' ')
.find(|&i| i == "WGL_ARB_multisample")
.is_some()
{
out.push(gl::wgl_extra::SAMPLE_BUFFERS_ARB as raw::c_int);
out.push(if multisampling == 0 { 0 } else { 1 });
out.push(gl::wgl_extra::SAMPLES_ARB as raw::c_int);
out.push(multisampling as raw::c_int);
} else {
return Err(());
}
}
out.push(gl::wgl_extra::STEREO_ARB as raw::c_int);
out.push(if pf_reqs.stereoscopy { 1 } else { 0 });
if pf_reqs.srgb {
if extensions
.split(' ')
.find(|&i| i == "WGL_ARB_framebuffer_sRGB")
.is_some()
{
out.push(
gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as raw::c_int,
);
out.push(1);
} else if extensions
.split(' ')
.find(|&i| i == "WGL_EXT_framebuffer_sRGB")
.is_some()
{
out.push(
gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as raw::c_int,
);
out.push(1);
} else {
return Err(());
}
}
match pf_reqs.release_behavior {
ReleaseBehavior::Flush => (),
ReleaseBehavior::None => {
if extensions
.split(' ')
.find(|&i| i == "WGL_ARB_context_flush_control")
.is_some()
{
out.push(
gl::wgl_extra::CONTEXT_RELEASE_BEHAVIOR_ARB
as raw::c_int,
);
out.push(
gl::wgl_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB
as raw::c_int,
);
}
}
}
out.push(0);
out
};
let mut format_id = std::mem::uninitialized();
let mut num_formats = std::mem::uninitialized();
if extra.ChoosePixelFormatARB(
hdc as *const _,
descriptor.as_ptr(),
std::ptr::null(),
1,
&mut format_id,
&mut num_formats,
) == 0
{
return Err(());
}
if num_formats == 0 {
return Err(());
}
Ok(format_id)
}
unsafe fn choose_arb_pixel_format(
extra: &gl::wgl_extra::Wgl,
extensions: &str,
hdc: HDC,
format_id: raw::c_int,
) -> Result<PixelFormat, ()> {
let get_info = |attrib: u32| {
let mut value = std::mem::uninitialized();
extra.GetPixelFormatAttribivARB(
hdc as *const _,
format_id as raw::c_int,
0,
1,
[attrib as raw::c_int].as_ptr(),
&mut value,
);
value as u32
};
let pf_desc = PixelFormat {
hardware_accelerated: get_info(gl::wgl_extra::ACCELERATION_ARB)
!= gl::wgl_extra::NO_ACCELERATION_ARB,
color_bits: get_info(gl::wgl_extra::RED_BITS_ARB) as u8
+ get_info(gl::wgl_extra::GREEN_BITS_ARB) as u8
+ get_info(gl::wgl_extra::BLUE_BITS_ARB) as u8,
alpha_bits: get_info(gl::wgl_extra::ALPHA_BITS_ARB) as u8,
depth_bits: get_info(gl::wgl_extra::DEPTH_BITS_ARB) as u8,
stencil_bits: get_info(gl::wgl_extra::STENCIL_BITS_ARB) as u8,
stereoscopy: get_info(gl::wgl_extra::STEREO_ARB) != 0,
double_buffer: get_info(gl::wgl_extra::DOUBLE_BUFFER_ARB) != 0,
multisampling: {
if extensions
.split(' ')
.find(|&i| i == "WGL_ARB_multisample")
.is_some()
{
match get_info(gl::wgl_extra::SAMPLES_ARB) {
0 => None,
a => Some(a as u16),
}
} else {
None
}
},
srgb: if extensions
.split(' ')
.find(|&i| i == "WGL_ARB_framebuffer_sRGB")
.is_some()
{
get_info(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB) != 0
} else if extensions
.split(' ')
.find(|&i| i == "WGL_EXT_framebuffer_sRGB")
.is_some()
{
get_info(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT) != 0
} else {
false
},
};
Ok(pf_desc)
}
unsafe fn set_pixel_format(
hdc: HDC,
id: raw::c_int,
) -> Result<(), CreationError> {
let mut output: PIXELFORMATDESCRIPTOR = std::mem::zeroed();
if DescribePixelFormat(
hdc,
id,
std::mem::size_of::<PIXELFORMATDESCRIPTOR>() as UINT,
&mut output,
) == 0
{
return Err(CreationError::OsError(format!(
"DescribePixelFormat function failed: {}",
std::io::Error::last_os_error()
)));
}
if SetPixelFormat(hdc, id, &output) == 0 {
return Err(CreationError::OsError(format!(
"SetPixelFormat function failed: {}",
std::io::Error::last_os_error()
)));
}
Ok(())
}
unsafe fn load_opengl32_dll() -> Result<HMODULE, CreationError> {
let name = OsStr::new("opengl32.dll")
.encode_wide()
.chain(Some(0).into_iter())
.collect::<Vec<_>>();
let lib = LoadLibraryW(name.as_ptr());
if lib.is_null() {
return Err(CreationError::OsError(format!(
"LoadLibrary function failed: {}",
std::io::Error::last_os_error()
)));
}
Ok(lib)
}
unsafe fn load_extra_functions(
win: HWND,
) -> Result<gl::wgl_extra::Wgl, CreationError> {
let (ex_style, style) = (
WS_EX_APPWINDOW,
WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
);
let dummy_win = {
let rect = {
let mut placement: WINDOWPLACEMENT = std::mem::zeroed();
placement.length = std::mem::size_of::<WINDOWPLACEMENT>() as UINT;
if GetWindowPlacement(win, &mut placement) == 0 {
panic!();
}
placement.rcNormalPosition
};
let mut class_name = [0u16; 128];
if GetClassNameW(win, class_name.as_mut_ptr(), 128) == 0 {
return Err(CreationError::OsError(format!(
"GetClassNameW function failed: {}",
std::io::Error::last_os_error()
)));
}
let instance = GetModuleHandleW(std::ptr::null());
let mut class: WNDCLASSEXW = std::mem::zeroed();
if GetClassInfoExW(instance, class_name.as_ptr(), &mut class) == 0 {
return Err(CreationError::OsError(format!(
"GetClassInfoExW function failed: {}",
std::io::Error::last_os_error()
)));
}
let class_name = OsStr::new("WglDummy Class")
.encode_wide()
.chain(Some(0).into_iter())
.collect::<Vec<_>>();
class.cbSize = std::mem::size_of::<WNDCLASSEXW>() as UINT;
class.lpszClassName = class_name.as_ptr();
class.lpfnWndProc = Some(DefWindowProcW);
RegisterClassExW(&class);
let title = OsStr::new("dummy window")
.encode_wide()
.chain(Some(0).into_iter())
.collect::<Vec<_>>();
let win = CreateWindowExW(
ex_style,
class_name.as_ptr(),
title.as_ptr() as LPCWSTR,
style,
CW_USEDEFAULT,
CW_USEDEFAULT,
rect.right - rect.left,
rect.bottom - rect.top,
std::ptr::null_mut(),
std::ptr::null_mut(),
GetModuleHandleW(std::ptr::null()),
std::ptr::null_mut(),
);
if win.is_null() {
return Err(CreationError::OsError(format!(
"CreateWindowEx function failed: {}",
std::io::Error::last_os_error()
)));
}
let hdc = GetDC(win);
if hdc.is_null() {
let err = Err(CreationError::OsError(format!(
"GetDC function failed: {}",
std::io::Error::last_os_error()
)));
return err;
}
WindowWrapper(win, hdc)
};
{
let id = choose_dummy_pixel_format(dummy_win.1)?;
set_pixel_format(dummy_win.1, id)?;
}
let dummy_ctx = create_context(None, dummy_win.0, dummy_win.1)?;
let _current_context =
CurrentContextGuard::make_current(dummy_win.1, dummy_ctx.0)?;
Ok(gl::wgl_extra::Wgl::load_with(|addr| {
let addr = CString::new(addr.as_bytes()).unwrap();
let addr = addr.as_ptr();
gl::wgl::GetProcAddress(addr) as *const raw::c_void
}))
}
fn choose_dummy_pixel_format(hdc: HDC) -> Result<raw::c_int, CreationError> {
let descriptor = PIXELFORMATDESCRIPTOR {
nSize: std::mem::size_of::<PIXELFORMATDESCRIPTOR>() as u16,
nVersion: 1,
dwFlags: PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
iPixelType: PFD_TYPE_RGBA,
cColorBits: 24,
cRedBits: 0,
cRedShift: 0,
cGreenBits: 0,
cGreenShift: 0,
cBlueBits: 0,
cBlueShift: 0,
cAlphaBits: 8,
cAlphaShift: 0,
cAccumBits: 0,
cAccumRedBits: 0,
cAccumGreenBits: 0,
cAccumBlueBits: 0,
cAccumAlphaBits: 0,
cDepthBits: 24,
cStencilBits: 8,
cAuxBuffers: 0,
iLayerType: PFD_MAIN_PLANE,
bReserved: 0,
dwLayerMask: 0,
dwVisibleMask: 0,
dwDamageMask: 0,
};
let pf_id = unsafe { ChoosePixelFormat(hdc, &descriptor) };
if pf_id == 0 {
return Err(CreationError::OsError(
"No available pixel format".to_owned(),
));
}
Ok(pf_id)
}