#![allow(non_snake_case)]
use crate::gl::{GlConfig, Profile};
use crate::warn;
use crate::wrappers::win32::window::OwnDeviceContext;
use std::ffi::c_void;
use std::fmt::{Display, Formatter};
use std::mem::transmute;
use std::num::NonZeroI32;
use std::ptr::{null_mut, NonNull};
use windows_core::Error;
use windows_sys::s;
use windows_sys::Win32::Foundation::PROC;
use windows_sys::Win32::Graphics::Gdi::HDC;
use windows_sys::Win32::Graphics::OpenGL::{
wglDeleteContext, wglGetCurrentContext, wglGetProcAddress, wglMakeCurrent, HGLRC,
};
const WGL_CONTEXT_MAJOR_VERSION_ARB: i32 = 0x2091;
const WGL_CONTEXT_MINOR_VERSION_ARB: i32 = 0x2092;
const WGL_CONTEXT_PROFILE_MASK_ARB: i32 = 0x9126;
const WGL_CONTEXT_CORE_PROFILE_BIT_ARB: i32 = 0x00000001;
const WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB: i32 = 0x00000002;
pub struct WglContext {
pub(super) inner: NonNull<c_void>,
}
impl WglContext {
pub unsafe fn make_current(&self, dc: &OwnDeviceContext) -> windows_core::Result<()> {
let result = unsafe { wglMakeCurrent(dc.as_raw(), self.inner.as_ptr()) };
if result == 0 {
return Err(Error::from_thread());
}
Ok(())
}
pub unsafe fn make_not_current(&self) -> windows_core::Result<()> {
let current = unsafe { wglGetCurrentContext() };
if current.is_null() {
return Ok(());
};
if current != self.inner.as_ptr() {
return Ok(());
};
let result = unsafe { wglMakeCurrent(null_mut(), null_mut()) };
if result == 0 {
return Err(Error::from_thread());
}
Ok(())
}
pub fn with_current<T>(
&self, dc: &OwnDeviceContext, f: impl FnOnce() -> T,
) -> windows_core::Result<T> {
struct Guard<'a>(&'a WglContext);
impl<'a> Drop for Guard<'a> {
fn drop(&mut self) {
let _ = unsafe { self.0.make_not_current() };
}
}
unsafe { self.make_current(dc)? };
let _guard = Guard(self);
let result = f();
drop(_guard);
Ok(result)
}
}
impl Drop for WglContext {
fn drop(&mut self) {
if let Err(e) = unsafe { self.make_not_current() } {
warn!("Could not unset current GL context {}", e);
}
let result = unsafe { wglDeleteContext(self.inner.as_ptr()) };
if result == 0 {
warn!("Could not delete GL context: {}", Error::from_thread());
}
}
}
type WglSwapIntervalEXT = unsafe extern "system" fn(i32) -> i32;
type WglChoosePixelFormatARB =
unsafe extern "system" fn(HDC, *const i32, *const f32, u32, *mut i32, *mut u32) -> i32;
type WglCreateContextAttribsARB = unsafe extern "system" fn(HDC, HGLRC, *const i32) -> HGLRC;
pub struct WglExtra {
wglCreateContextAttribsARB: Option<WglCreateContextAttribsARB>,
wglChoosePixelFormatARB: Option<WglChoosePixelFormatARB>,
wglSwapIntervalEXT: Option<WglSwapIntervalEXT>,
}
impl WglExtra {
pub fn load() -> Self {
unsafe {
Self {
wglCreateContextAttribsARB: transmute::<PROC, Option<WglCreateContextAttribsARB>>(
wglGetProcAddress(s!("wglCreateContextAttribsARB")),
),
wglChoosePixelFormatARB: transmute::<PROC, Option<WglChoosePixelFormatARB>>(
wglGetProcAddress(s!("wglChoosePixelFormatARB")),
),
wglSwapIntervalEXT: transmute::<PROC, Option<WglSwapIntervalEXT>>(
wglGetProcAddress(s!("wglSwapIntervalEXT")),
),
}
}
}
pub fn create_context_for_config(
&self, dc: &OwnDeviceContext, config: &GlConfig,
) -> Result<WglContext, CreateContextError> {
let Some(wglCreateContextAttribsARB) = self.wglCreateContextAttribsARB else {
return Err(CreateContextError::Unavailable);
};
let profile_mask = match config.profile {
Profile::Core => WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
Profile::Compatibility => WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
};
#[rustfmt::skip]
let ctx_attribs = [
WGL_CONTEXT_MAJOR_VERSION_ARB, config.version.0 as i32,
WGL_CONTEXT_MINOR_VERSION_ARB, config.version.1 as i32,
WGL_CONTEXT_PROFILE_MASK_ARB, profile_mask,
0
];
let ctx =
unsafe { wglCreateContextAttribsARB(dc.as_raw(), null_mut(), ctx_attribs.as_ptr()) };
let ctx =
NonNull::new(ctx).ok_or_else(|| CreateContextError::Win32(Error::from_thread()))?;
Ok(WglContext { inner: ctx })
}
pub fn set_vsync(&self, vsync: bool) -> windows_core::Result<()> {
let Some(wglSwapIntervalEXT) = self.wglSwapIntervalEXT else {
warn!("Could not set vsync: wglSwapIntervalEXT is not available");
return Ok(());
};
let result = unsafe { wglSwapIntervalEXT(vsync.into()) };
if result == 0 {
return Err(Error::from_thread());
}
Ok(())
}
pub fn choose_pixel_format_from_attribs(
&self, attribs: &PixelFormatAttribs, dc: &OwnDeviceContext,
) -> Result<Option<NonZeroI32>, ChoosePixelFormatFromAttribsError> {
let Some(wglChoosePixelFormatARB) = self.wglChoosePixelFormatARB else {
return Err(ChoosePixelFormatFromAttribsError::Unavailable);
};
let mut pixel_formats = [0];
let mut num_formats = 0;
let result = unsafe {
wglChoosePixelFormatARB(
dc.as_raw(),
attribs.inner.as_ptr(),
std::ptr::null(),
1,
pixel_formats.as_mut_ptr(),
&mut num_formats,
)
};
if result == 0 {
return Err(ChoosePixelFormatFromAttribsError::Win32(Error::from_thread()));
}
if num_formats == 0 {
return Ok(None);
}
Ok(NonZeroI32::new(pixel_formats[0]))
}
}
pub enum CreateContextError {
Win32(Error),
Unavailable,
}
impl Display for CreateContextError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
CreateContextError::Win32(err) => Display::fmt(err, f),
CreateContextError::Unavailable => {
f.write_str("wglCreateContextAttribsARB is not available")
}
}
}
}
pub enum ChoosePixelFormatFromAttribsError {
Win32(Error),
Unavailable,
}
impl Display for ChoosePixelFormatFromAttribsError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ChoosePixelFormatFromAttribsError::Win32(e) => Display::fmt(e, f),
ChoosePixelFormatFromAttribsError::Unavailable => {
f.write_str("wglChoosePixelFormatARB is not available")
}
}
}
}
const WGL_DRAW_TO_WINDOW_ARB: i32 = 0x2001;
const WGL_ACCELERATION_ARB: i32 = 0x2003;
const WGL_SUPPORT_OPENGL_ARB: i32 = 0x2010;
const WGL_DOUBLE_BUFFER_ARB: i32 = 0x2011;
const WGL_PIXEL_TYPE_ARB: i32 = 0x2013;
const WGL_RED_BITS_ARB: i32 = 0x2015;
const WGL_GREEN_BITS_ARB: i32 = 0x2017;
const WGL_BLUE_BITS_ARB: i32 = 0x2019;
const WGL_ALPHA_BITS_ARB: i32 = 0x201B;
const WGL_DEPTH_BITS_ARB: i32 = 0x2022;
const WGL_STENCIL_BITS_ARB: i32 = 0x2023;
const WGL_FULL_ACCELERATION_ARB: i32 = 0x2027;
const WGL_TYPE_RGBA_ARB: i32 = 0x202B;
const WGL_SAMPLE_BUFFERS_ARB: i32 = 0x2041;
const WGL_SAMPLES_ARB: i32 = 0x2042;
const WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB: i32 = 0x20A9;
pub struct PixelFormatAttribs {
inner: [i32; 29],
}
impl PixelFormatAttribs {
#[rustfmt::skip]
pub fn from_config(config: &GlConfig) -> Self {
Self {
inner: [
WGL_DRAW_TO_WINDOW_ARB, 1,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_SUPPORT_OPENGL_ARB, 1,
WGL_DOUBLE_BUFFER_ARB, config.double_buffer as i32,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_RED_BITS_ARB, config.red_bits as i32,
WGL_GREEN_BITS_ARB, config.green_bits as i32,
WGL_BLUE_BITS_ARB, config.blue_bits as i32,
WGL_ALPHA_BITS_ARB, config.alpha_bits as i32,
WGL_DEPTH_BITS_ARB, config.depth_bits as i32,
WGL_STENCIL_BITS_ARB, config.stencil_bits as i32,
WGL_SAMPLE_BUFFERS_ARB, config.samples.is_some() as i32,
WGL_SAMPLES_ARB, config.samples.unwrap_or(0) as i32,
WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, config.srgb as i32,
0,
]
}
}
pub fn set_without_srgb_ext(&mut self) {
self.inner[26] = 0;
self.inner[27] = 0;
}
}