Add the entire tree from raw-gl-handle
Including @prokopyl's PR that adds more X11 error handling: https://github.com/glowcoil/raw-gl-context/pull/15 Commit: prokopyl@raw-gl-context@98cd3cf1104ee254a67e3fed30e4e6b2ae2b6821 (with `cargo fmt`) Branch base: RustAudio/baseview@b68a05b4dc
This commit is contained in:
parent
d76b02df44
commit
45e29b4891
100
src/gl/lib.rs
Normal file
100
src/gl/lib.rs
Normal file
|
@ -0,0 +1,100 @@
|
|||
use raw_window_handle::HasRawWindowHandle;
|
||||
|
||||
use std::ffi::c_void;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
mod win;
|
||||
#[cfg(target_os = "windows")]
|
||||
use win as platform;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod x11;
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::x11 as platform;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
#[cfg(target_os = "macos")]
|
||||
use macos as platform;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GlConfig {
|
||||
pub version: (u8, u8),
|
||||
pub profile: Profile,
|
||||
pub red_bits: u8,
|
||||
pub blue_bits: u8,
|
||||
pub green_bits: u8,
|
||||
pub alpha_bits: u8,
|
||||
pub depth_bits: u8,
|
||||
pub stencil_bits: u8,
|
||||
pub samples: Option<u8>,
|
||||
pub srgb: bool,
|
||||
pub double_buffer: bool,
|
||||
pub vsync: bool,
|
||||
}
|
||||
|
||||
impl Default for GlConfig {
|
||||
fn default() -> Self {
|
||||
GlConfig {
|
||||
version: (3, 2),
|
||||
profile: Profile::Core,
|
||||
red_bits: 8,
|
||||
blue_bits: 8,
|
||||
green_bits: 8,
|
||||
alpha_bits: 8,
|
||||
depth_bits: 24,
|
||||
stencil_bits: 8,
|
||||
samples: None,
|
||||
srgb: true,
|
||||
double_buffer: true,
|
||||
vsync: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Profile {
|
||||
Compatibility,
|
||||
Core,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GlError {
|
||||
InvalidWindowHandle,
|
||||
VersionNotSupported,
|
||||
CreationFailed(platform::CreationFailedError),
|
||||
}
|
||||
|
||||
pub struct GlContext {
|
||||
context: platform::GlContext,
|
||||
phantom: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
impl GlContext {
|
||||
pub unsafe fn create(
|
||||
parent: &impl HasRawWindowHandle,
|
||||
config: GlConfig,
|
||||
) -> Result<GlContext, GlError> {
|
||||
platform::GlContext::create(parent, config).map(|context| GlContext {
|
||||
context,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn make_current(&self) {
|
||||
self.context.make_current();
|
||||
}
|
||||
|
||||
pub unsafe fn make_not_current(&self) {
|
||||
self.context.make_not_current();
|
||||
}
|
||||
|
||||
pub fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
||||
self.context.get_proc_address(symbol)
|
||||
}
|
||||
|
||||
pub fn swap_buffers(&self) {
|
||||
self.context.swap_buffers();
|
||||
}
|
||||
}
|
147
src/gl/macos.rs
Normal file
147
src/gl/macos.rs
Normal file
|
@ -0,0 +1,147 @@
|
|||
use std::ffi::c_void;
|
||||
use std::str::FromStr;
|
||||
|
||||
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
|
||||
|
||||
use cocoa::appkit::{
|
||||
NSOpenGLContext, NSOpenGLContextParameter, NSOpenGLPFAAccelerated, NSOpenGLPFAAlphaSize,
|
||||
NSOpenGLPFAColorSize, NSOpenGLPFADepthSize, NSOpenGLPFADoubleBuffer, NSOpenGLPFAMultisample,
|
||||
NSOpenGLPFAOpenGLProfile, NSOpenGLPFASampleBuffers, NSOpenGLPFASamples, NSOpenGLPFAStencilSize,
|
||||
NSOpenGLPixelFormat, NSOpenGLProfileVersion3_2Core, NSOpenGLProfileVersion4_1Core,
|
||||
NSOpenGLProfileVersionLegacy, NSOpenGLView, NSView,
|
||||
};
|
||||
use cocoa::base::{id, nil, YES};
|
||||
|
||||
use core_foundation::base::TCFType;
|
||||
use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName};
|
||||
use core_foundation::string::CFString;
|
||||
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
|
||||
use crate::{GlConfig, GlError, Profile};
|
||||
|
||||
pub type CreationFailedError = ();
|
||||
pub struct GlContext {
|
||||
view: id,
|
||||
context: id,
|
||||
}
|
||||
|
||||
impl GlContext {
|
||||
pub unsafe fn create(
|
||||
parent: &impl HasRawWindowHandle,
|
||||
config: GlConfig,
|
||||
) -> Result<GlContext, GlError> {
|
||||
let handle = if let RawWindowHandle::MacOS(handle) = parent.raw_window_handle() {
|
||||
handle
|
||||
} else {
|
||||
return Err(GlError::InvalidWindowHandle);
|
||||
};
|
||||
|
||||
if handle.ns_view.is_null() {
|
||||
return Err(GlError::InvalidWindowHandle);
|
||||
}
|
||||
|
||||
let parent_view = handle.ns_view as id;
|
||||
|
||||
let version = if config.version < (3, 2) && config.profile == Profile::Compatibility {
|
||||
NSOpenGLProfileVersionLegacy
|
||||
} else if config.version == (3, 2) && config.profile == Profile::Core {
|
||||
NSOpenGLProfileVersion3_2Core
|
||||
} else if config.version > (3, 2) && config.profile == Profile::Core {
|
||||
NSOpenGLProfileVersion4_1Core
|
||||
} else {
|
||||
return Err(GlError::VersionNotSupported);
|
||||
};
|
||||
|
||||
#[rustfmt::skip]
|
||||
let mut attrs = vec![
|
||||
NSOpenGLPFAOpenGLProfile as u32, version as u32,
|
||||
NSOpenGLPFAColorSize as u32, (config.red_bits + config.blue_bits + config.green_bits) as u32,
|
||||
NSOpenGLPFAAlphaSize as u32, config.alpha_bits as u32,
|
||||
NSOpenGLPFADepthSize as u32, config.depth_bits as u32,
|
||||
NSOpenGLPFAStencilSize as u32, config.stencil_bits as u32,
|
||||
NSOpenGLPFAAccelerated as u32,
|
||||
];
|
||||
|
||||
if config.samples.is_some() {
|
||||
#[rustfmt::skip]
|
||||
attrs.extend_from_slice(&[
|
||||
NSOpenGLPFAMultisample as u32,
|
||||
NSOpenGLPFASampleBuffers as u32, 1,
|
||||
NSOpenGLPFASamples as u32, config.samples.unwrap() as u32,
|
||||
]);
|
||||
}
|
||||
|
||||
if config.double_buffer {
|
||||
attrs.push(NSOpenGLPFADoubleBuffer as u32);
|
||||
}
|
||||
|
||||
attrs.push(0);
|
||||
|
||||
let pixel_format = NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attrs);
|
||||
|
||||
if pixel_format == nil {
|
||||
return Err(GlError::CreationFailed(()));
|
||||
}
|
||||
|
||||
let view =
|
||||
NSOpenGLView::alloc(nil).initWithFrame_pixelFormat_(parent_view.frame(), pixel_format);
|
||||
|
||||
if view == nil {
|
||||
return Err(GlError::CreationFailed(()));
|
||||
}
|
||||
|
||||
view.setWantsBestResolutionOpenGLSurface_(YES);
|
||||
|
||||
let () = msg_send![view, retain];
|
||||
NSOpenGLView::display_(view);
|
||||
parent_view.addSubview_(view);
|
||||
|
||||
let context: id = msg_send![view, openGLContext];
|
||||
let () = msg_send![context, retain];
|
||||
|
||||
context.setValues_forParameter_(
|
||||
&(config.vsync as i32),
|
||||
NSOpenGLContextParameter::NSOpenGLCPSwapInterval,
|
||||
);
|
||||
|
||||
let () = msg_send![pixel_format, release];
|
||||
|
||||
Ok(GlContext { view, context })
|
||||
}
|
||||
|
||||
pub unsafe fn make_current(&self) {
|
||||
self.context.makeCurrentContext();
|
||||
}
|
||||
|
||||
pub unsafe fn make_not_current(&self) {
|
||||
NSOpenGLContext::clearCurrentContext(self.context);
|
||||
}
|
||||
|
||||
pub fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
||||
let symbol_name = CFString::from_str(symbol).unwrap();
|
||||
let framework_name = CFString::from_str("com.apple.opengl").unwrap();
|
||||
let framework =
|
||||
unsafe { CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef()) };
|
||||
let addr = unsafe {
|
||||
CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef())
|
||||
};
|
||||
addr as *const c_void
|
||||
}
|
||||
|
||||
pub fn swap_buffers(&self) {
|
||||
unsafe {
|
||||
self.context.flushBuffer();
|
||||
let () = msg_send![self.view, setNeedsDisplay: YES];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GlContext {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let () = msg_send![self.context, release];
|
||||
let () = msg_send![self.view, release];
|
||||
}
|
||||
}
|
||||
}
|
312
src/gl/win.rs
Normal file
312
src/gl/win.rs
Normal file
|
@ -0,0 +1,312 @@
|
|||
use std::ffi::{c_void, CString, OsStr};
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
|
||||
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
|
||||
|
||||
use winapi::shared::minwindef::{HINSTANCE, HMODULE};
|
||||
use winapi::shared::ntdef::WCHAR;
|
||||
use winapi::shared::windef::{HDC, HGLRC, HWND};
|
||||
use winapi::um::libloaderapi::{FreeLibrary, GetProcAddress, LoadLibraryA};
|
||||
use winapi::um::wingdi::{
|
||||
wglCreateContext, wglDeleteContext, wglGetProcAddress, wglMakeCurrent, ChoosePixelFormat,
|
||||
DescribePixelFormat, SetPixelFormat, SwapBuffers, PFD_DOUBLEBUFFER, PFD_DRAW_TO_WINDOW,
|
||||
PFD_MAIN_PLANE, PFD_SUPPORT_OPENGL, PFD_TYPE_RGBA, PIXELFORMATDESCRIPTOR,
|
||||
};
|
||||
use winapi::um::winnt::IMAGE_DOS_HEADER;
|
||||
use winapi::um::winuser::{
|
||||
CreateWindowExW, DefWindowProcW, DestroyWindow, GetDC, RegisterClassW, ReleaseDC,
|
||||
UnregisterClassW, CS_OWNDC, CW_USEDEFAULT, WNDCLASSW,
|
||||
};
|
||||
|
||||
use crate::{GlConfig, GlError, Profile};
|
||||
|
||||
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_create_context.txt
|
||||
|
||||
type WglCreateContextAttribsARB = extern "system" fn(HDC, HGLRC, *const i32) -> 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;
|
||||
|
||||
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_pixel_format.txt
|
||||
|
||||
type WglChoosePixelFormatARB =
|
||||
extern "system" fn(HDC, *const i32, *const f32, u32, *mut i32, *mut u32) -> i32;
|
||||
|
||||
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;
|
||||
|
||||
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_multisample.txt
|
||||
|
||||
const WGL_SAMPLE_BUFFERS_ARB: i32 = 0x2041;
|
||||
const WGL_SAMPLES_ARB: i32 = 0x2042;
|
||||
|
||||
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_framebuffer_sRGB.txt
|
||||
|
||||
const WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB: i32 = 0x20A9;
|
||||
|
||||
// See https://www.khronos.org/registry/OpenGL/extensions/EXT/WGL_EXT_swap_control.txt
|
||||
|
||||
type WglSwapIntervalEXT = extern "system" fn(i32) -> i32;
|
||||
|
||||
pub type CreationFailedError = ();
|
||||
pub struct GlContext {
|
||||
hwnd: HWND,
|
||||
hdc: HDC,
|
||||
hglrc: HGLRC,
|
||||
gl_library: HMODULE,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
static __ImageBase: IMAGE_DOS_HEADER;
|
||||
}
|
||||
|
||||
impl GlContext {
|
||||
pub unsafe fn create(
|
||||
parent: &impl HasRawWindowHandle,
|
||||
config: GlConfig,
|
||||
) -> Result<GlContext, GlError> {
|
||||
let handle = if let RawWindowHandle::Windows(handle) = parent.raw_window_handle() {
|
||||
handle
|
||||
} else {
|
||||
return Err(GlError::InvalidWindowHandle);
|
||||
};
|
||||
|
||||
if handle.hwnd.is_null() {
|
||||
return Err(GlError::InvalidWindowHandle);
|
||||
}
|
||||
|
||||
// Create temporary window and context to load function pointers
|
||||
|
||||
let class_name_str = format!("raw-gl-context-window-{}", uuid::Uuid::new_v4().to_simple());
|
||||
let mut class_name: Vec<WCHAR> = OsStr::new(&class_name_str).encode_wide().collect();
|
||||
class_name.push(0);
|
||||
|
||||
let hinstance = &__ImageBase as *const IMAGE_DOS_HEADER as HINSTANCE;
|
||||
|
||||
let wnd_class = WNDCLASSW {
|
||||
style: CS_OWNDC,
|
||||
lpfnWndProc: Some(DefWindowProcW),
|
||||
hInstance: hinstance,
|
||||
lpszClassName: class_name.as_ptr(),
|
||||
..std::mem::zeroed()
|
||||
};
|
||||
|
||||
let class = RegisterClassW(&wnd_class);
|
||||
if class == 0 {
|
||||
return Err(GlError::CreationFailed(()));
|
||||
}
|
||||
|
||||
let hwnd_tmp = CreateWindowExW(
|
||||
0,
|
||||
class as *const WCHAR,
|
||||
[0].as_ptr(),
|
||||
0,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
hinstance,
|
||||
std::ptr::null_mut(),
|
||||
);
|
||||
|
||||
if hwnd_tmp.is_null() {
|
||||
return Err(GlError::CreationFailed(()));
|
||||
}
|
||||
|
||||
let hdc_tmp = GetDC(hwnd_tmp);
|
||||
|
||||
let pfd_tmp = 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: 32,
|
||||
cAlphaBits: 8,
|
||||
cDepthBits: 24,
|
||||
cStencilBits: 8,
|
||||
iLayerType: PFD_MAIN_PLANE,
|
||||
..std::mem::zeroed()
|
||||
};
|
||||
|
||||
SetPixelFormat(hdc_tmp, ChoosePixelFormat(hdc_tmp, &pfd_tmp), &pfd_tmp);
|
||||
|
||||
let hglrc_tmp = wglCreateContext(hdc_tmp);
|
||||
if hglrc_tmp.is_null() {
|
||||
ReleaseDC(hwnd_tmp, hdc_tmp);
|
||||
UnregisterClassW(class as *const WCHAR, hinstance);
|
||||
DestroyWindow(hwnd_tmp);
|
||||
return Err(GlError::CreationFailed(()));
|
||||
}
|
||||
|
||||
wglMakeCurrent(hdc_tmp, hglrc_tmp);
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
let wglCreateContextAttribsARB: Option<WglCreateContextAttribsARB> = {
|
||||
let symbol = CString::new("wglCreateContextAttribsARB").unwrap();
|
||||
let addr = wglGetProcAddress(symbol.as_ptr());
|
||||
if !addr.is_null() {
|
||||
Some(std::mem::transmute(addr))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
let wglChoosePixelFormatARB: Option<WglChoosePixelFormatARB> = {
|
||||
let symbol = CString::new("wglChoosePixelFormatARB").unwrap();
|
||||
let addr = wglGetProcAddress(symbol.as_ptr());
|
||||
if !addr.is_null() {
|
||||
Some(std::mem::transmute(addr))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
let wglSwapIntervalEXT: Option<WglSwapIntervalEXT> = {
|
||||
let symbol = CString::new("wglSwapIntervalEXT").unwrap();
|
||||
let addr = wglGetProcAddress(symbol.as_ptr());
|
||||
if !addr.is_null() {
|
||||
Some(std::mem::transmute(addr))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
wglMakeCurrent(hdc_tmp, std::ptr::null_mut());
|
||||
ReleaseDC(hwnd_tmp, hdc_tmp);
|
||||
UnregisterClassW(class as *const WCHAR, hinstance);
|
||||
DestroyWindow(hwnd_tmp);
|
||||
|
||||
// Create actual context
|
||||
|
||||
let hwnd = handle.hwnd as HWND;
|
||||
|
||||
let hdc = GetDC(hwnd);
|
||||
|
||||
#[rustfmt::skip]
|
||||
let pixel_format_attribs = [
|
||||
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,
|
||||
];
|
||||
|
||||
let mut pixel_format = 0;
|
||||
let mut num_formats = 0;
|
||||
wglChoosePixelFormatARB.unwrap()(
|
||||
hdc,
|
||||
pixel_format_attribs.as_ptr(),
|
||||
std::ptr::null(),
|
||||
1,
|
||||
&mut pixel_format,
|
||||
&mut num_formats,
|
||||
);
|
||||
|
||||
let mut pfd: PIXELFORMATDESCRIPTOR = std::mem::zeroed();
|
||||
DescribePixelFormat(
|
||||
hdc,
|
||||
pixel_format,
|
||||
std::mem::size_of::<PIXELFORMATDESCRIPTOR>() as u32,
|
||||
&mut pfd,
|
||||
);
|
||||
SetPixelFormat(hdc, pixel_format, &pfd);
|
||||
|
||||
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 hglrc =
|
||||
wglCreateContextAttribsARB.unwrap()(hdc, std::ptr::null_mut(), ctx_attribs.as_ptr());
|
||||
if hglrc == std::ptr::null_mut() {
|
||||
return Err(GlError::CreationFailed(()));
|
||||
}
|
||||
|
||||
let gl_library_name = CString::new("opengl32.dll").unwrap();
|
||||
let gl_library = LoadLibraryA(gl_library_name.as_ptr());
|
||||
|
||||
wglMakeCurrent(hdc, hglrc);
|
||||
wglSwapIntervalEXT.unwrap()(config.vsync as i32);
|
||||
wglMakeCurrent(hdc, std::ptr::null_mut());
|
||||
|
||||
Ok(GlContext {
|
||||
hwnd,
|
||||
hdc,
|
||||
hglrc,
|
||||
gl_library,
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn make_current(&self) {
|
||||
wglMakeCurrent(self.hdc, self.hglrc);
|
||||
}
|
||||
|
||||
pub unsafe fn make_not_current(&self) {
|
||||
wglMakeCurrent(self.hdc, std::ptr::null_mut());
|
||||
}
|
||||
|
||||
pub fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
||||
let symbol = CString::new(symbol).unwrap();
|
||||
let addr = unsafe { wglGetProcAddress(symbol.as_ptr()) as *const c_void };
|
||||
if !addr.is_null() {
|
||||
addr
|
||||
} else {
|
||||
unsafe { GetProcAddress(self.gl_library, symbol.as_ptr()) as *const c_void }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn swap_buffers(&self) {
|
||||
unsafe {
|
||||
SwapBuffers(self.hdc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GlContext {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
wglMakeCurrent(std::ptr::null_mut(), std::ptr::null_mut());
|
||||
wglDeleteContext(self.hglrc);
|
||||
ReleaseDC(self.hwnd, self.hdc);
|
||||
FreeLibrary(self.gl_library);
|
||||
}
|
||||
}
|
||||
}
|
231
src/gl/x11.rs
Normal file
231
src/gl/x11.rs
Normal file
|
@ -0,0 +1,231 @@
|
|||
use std::ffi::{c_void, CString};
|
||||
use std::os::raw::{c_int, c_ulong};
|
||||
|
||||
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
|
||||
|
||||
use x11::glx;
|
||||
use x11::xlib;
|
||||
|
||||
use crate::{GlConfig, GlError, Profile};
|
||||
|
||||
mod errors;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CreationFailedError {
|
||||
InvalidFBConfig,
|
||||
GetProcAddressFailed,
|
||||
MakeCurrentFailed,
|
||||
ContextCreationFailed,
|
||||
X11Error(errors::XLibError),
|
||||
}
|
||||
|
||||
impl From<errors::XLibError> for GlError {
|
||||
fn from(e: errors::XLibError) -> Self {
|
||||
GlError::CreationFailed(CreationFailedError::X11Error(e))
|
||||
}
|
||||
}
|
||||
|
||||
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/GLX_ARB_create_context.txt
|
||||
|
||||
type GlXCreateContextAttribsARB = unsafe extern "C" fn(
|
||||
dpy: *mut xlib::Display,
|
||||
fbc: glx::GLXFBConfig,
|
||||
share_context: glx::GLXContext,
|
||||
direct: xlib::Bool,
|
||||
attribs: *const c_int,
|
||||
) -> glx::GLXContext;
|
||||
|
||||
// See https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_swap_control.txt
|
||||
|
||||
type GlXSwapIntervalEXT =
|
||||
unsafe extern "C" fn(dpy: *mut xlib::Display, drawable: glx::GLXDrawable, interval: i32);
|
||||
|
||||
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_framebuffer_sRGB.txt
|
||||
|
||||
const GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB: i32 = 0x20B2;
|
||||
|
||||
fn get_proc_address(symbol: &str) -> *const c_void {
|
||||
let symbol = CString::new(symbol).unwrap();
|
||||
unsafe { glx::glXGetProcAddress(symbol.as_ptr() as *const u8).unwrap() as *const c_void }
|
||||
}
|
||||
|
||||
pub struct GlContext {
|
||||
window: c_ulong,
|
||||
display: *mut xlib::_XDisplay,
|
||||
context: glx::GLXContext,
|
||||
}
|
||||
|
||||
impl GlContext {
|
||||
pub unsafe fn create(
|
||||
parent: &impl HasRawWindowHandle,
|
||||
config: GlConfig,
|
||||
) -> Result<GlContext, GlError> {
|
||||
let handle = if let RawWindowHandle::Xlib(handle) = parent.raw_window_handle() {
|
||||
handle
|
||||
} else {
|
||||
return Err(GlError::InvalidWindowHandle);
|
||||
};
|
||||
|
||||
if handle.display.is_null() {
|
||||
return Err(GlError::InvalidWindowHandle);
|
||||
}
|
||||
|
||||
let display = handle.display as *mut xlib::_XDisplay;
|
||||
|
||||
errors::XErrorHandler::handle(display, |error_handler| {
|
||||
let screen = unsafe { xlib::XDefaultScreen(display) };
|
||||
|
||||
#[rustfmt::skip]
|
||||
let fb_attribs = [
|
||||
glx::GLX_X_RENDERABLE, 1,
|
||||
glx::GLX_X_VISUAL_TYPE, glx::GLX_TRUE_COLOR,
|
||||
glx::GLX_DRAWABLE_TYPE, glx::GLX_WINDOW_BIT,
|
||||
glx::GLX_RENDER_TYPE, glx::GLX_RGBA_BIT,
|
||||
glx::GLX_RED_SIZE, config.red_bits as i32,
|
||||
glx::GLX_GREEN_SIZE, config.green_bits as i32,
|
||||
glx::GLX_BLUE_SIZE, config.blue_bits as i32,
|
||||
glx::GLX_ALPHA_SIZE, config.alpha_bits as i32,
|
||||
glx::GLX_DEPTH_SIZE, config.depth_bits as i32,
|
||||
glx::GLX_STENCIL_SIZE, config.stencil_bits as i32,
|
||||
glx::GLX_DOUBLEBUFFER, config.double_buffer as i32,
|
||||
glx::GLX_SAMPLE_BUFFERS, config.samples.is_some() as i32,
|
||||
glx::GLX_SAMPLES, config.samples.unwrap_or(0) as i32,
|
||||
GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, config.srgb as i32,
|
||||
0,
|
||||
];
|
||||
|
||||
let mut n_configs = 0;
|
||||
let fb_config = unsafe {
|
||||
glx::glXChooseFBConfig(display, screen, fb_attribs.as_ptr(), &mut n_configs)
|
||||
};
|
||||
|
||||
error_handler.check()?;
|
||||
|
||||
if n_configs <= 0 {
|
||||
return Err(GlError::CreationFailed(
|
||||
CreationFailedError::InvalidFBConfig,
|
||||
));
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
let glXCreateContextAttribsARB: GlXCreateContextAttribsARB = unsafe {
|
||||
let addr = get_proc_address("glXCreateContextAttribsARB");
|
||||
if addr.is_null() {
|
||||
return Err(GlError::CreationFailed(
|
||||
CreationFailedError::GetProcAddressFailed,
|
||||
));
|
||||
} else {
|
||||
std::mem::transmute(addr)
|
||||
}
|
||||
};
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
let glXSwapIntervalEXT: GlXSwapIntervalEXT = unsafe {
|
||||
let addr = get_proc_address("glXSwapIntervalEXT");
|
||||
if addr.is_null() {
|
||||
return Err(GlError::CreationFailed(
|
||||
CreationFailedError::GetProcAddressFailed,
|
||||
));
|
||||
} else {
|
||||
std::mem::transmute(addr)
|
||||
}
|
||||
};
|
||||
|
||||
error_handler.check()?;
|
||||
|
||||
let profile_mask = match config.profile {
|
||||
Profile::Core => glx::arb::GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
Profile::Compatibility => glx::arb::GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
};
|
||||
|
||||
#[rustfmt::skip]
|
||||
let ctx_attribs = [
|
||||
glx::arb::GLX_CONTEXT_MAJOR_VERSION_ARB, config.version.0 as i32,
|
||||
glx::arb::GLX_CONTEXT_MINOR_VERSION_ARB, config.version.1 as i32,
|
||||
glx::arb::GLX_CONTEXT_PROFILE_MASK_ARB, profile_mask,
|
||||
0,
|
||||
];
|
||||
|
||||
let context = unsafe {
|
||||
glXCreateContextAttribsARB(
|
||||
display,
|
||||
*fb_config,
|
||||
std::ptr::null_mut(),
|
||||
1,
|
||||
ctx_attribs.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
error_handler.check()?;
|
||||
|
||||
if context.is_null() {
|
||||
return Err(GlError::CreationFailed(
|
||||
CreationFailedError::ContextCreationFailed,
|
||||
));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let res = glx::glXMakeCurrent(display, handle.window, context);
|
||||
error_handler.check()?;
|
||||
if res == 0 {
|
||||
return Err(GlError::CreationFailed(
|
||||
CreationFailedError::MakeCurrentFailed,
|
||||
));
|
||||
}
|
||||
|
||||
glXSwapIntervalEXT(display, handle.window, config.vsync as i32);
|
||||
error_handler.check()?;
|
||||
|
||||
if glx::glXMakeCurrent(display, 0, std::ptr::null_mut()) == 0 {
|
||||
error_handler.check()?;
|
||||
return Err(GlError::CreationFailed(
|
||||
CreationFailedError::MakeCurrentFailed,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(GlContext {
|
||||
window: handle.window,
|
||||
display,
|
||||
context,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn make_current(&self) {
|
||||
errors::XErrorHandler::handle(self.display, |error_handler| {
|
||||
let res = glx::glXMakeCurrent(self.display, self.window, self.context);
|
||||
error_handler.check().unwrap();
|
||||
if res == 0 {
|
||||
panic!("make_current failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn make_not_current(&self) {
|
||||
errors::XErrorHandler::handle(self.display, |error_handler| {
|
||||
let res = glx::glXMakeCurrent(self.display, 0, std::ptr::null_mut());
|
||||
error_handler.check().unwrap();
|
||||
if res == 0 {
|
||||
panic!("make_not_current failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
||||
get_proc_address(symbol)
|
||||
}
|
||||
|
||||
pub fn swap_buffers(&self) {
|
||||
errors::XErrorHandler::handle(self.display, |error_handler| {
|
||||
unsafe {
|
||||
glx::glXSwapBuffers(self.display, self.window);
|
||||
}
|
||||
error_handler.check().unwrap();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GlContext {
|
||||
fn drop(&mut self) {}
|
||||
}
|
131
src/gl/x11/errors.rs
Normal file
131
src/gl/x11/errors.rs
Normal file
|
@ -0,0 +1,131 @@
|
|||
use std::ffi::CStr;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use x11::xlib;
|
||||
|
||||
use std::panic::AssertUnwindSafe;
|
||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
use std::sync::Mutex;
|
||||
|
||||
static CURRENT_ERROR_PTR: AtomicPtr<Mutex<Option<xlib::XErrorEvent>>> =
|
||||
AtomicPtr::new(::std::ptr::null_mut());
|
||||
|
||||
/// A helper struct for safe X11 error handling
|
||||
pub struct XErrorHandler<'a> {
|
||||
display: *mut xlib::Display,
|
||||
mutex: &'a Mutex<Option<xlib::XErrorEvent>>,
|
||||
}
|
||||
|
||||
impl<'a> XErrorHandler<'a> {
|
||||
/// Syncs and checks if any previous X11 calls returned an error
|
||||
pub fn check(&mut self) -> Result<(), XLibError> {
|
||||
// Flush all possible previous errors
|
||||
unsafe {
|
||||
xlib::XSync(self.display, 0);
|
||||
}
|
||||
let error = self.mutex.lock().unwrap().take();
|
||||
|
||||
match error {
|
||||
None => Ok(()),
|
||||
Some(inner) => Err(XLibError { inner }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets up a temporary X11 error handler for the duration of the given closure, and allows
|
||||
/// that closure to check on the latest X11 error at any time
|
||||
pub fn handle<T, F: FnOnce(&mut XErrorHandler) -> T>(
|
||||
display: *mut xlib::Display,
|
||||
handler: F,
|
||||
) -> T {
|
||||
unsafe extern "C" fn error_handler(
|
||||
_dpy: *mut xlib::Display,
|
||||
err: *mut xlib::XErrorEvent,
|
||||
) -> i32 {
|
||||
// SAFETY: the error pointer should be safe to copy
|
||||
let err = *err;
|
||||
// SAFETY: the ptr can only point to a valid instance or be null
|
||||
let mutex = CURRENT_ERROR_PTR.load(Ordering::SeqCst).as_ref();
|
||||
let mutex = if let Some(mutex) = mutex {
|
||||
mutex
|
||||
} else {
|
||||
return 2;
|
||||
};
|
||||
|
||||
match mutex.lock() {
|
||||
Ok(mut current_error) => {
|
||||
*current_error = Some(err);
|
||||
0
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"[FATAL] raw-gl-context: Failed to lock for X11 Error Handler: {:?}",
|
||||
e
|
||||
);
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flush all possible previous errors
|
||||
unsafe {
|
||||
xlib::XSync(display, 0);
|
||||
}
|
||||
|
||||
let mut mutex = Mutex::new(None);
|
||||
CURRENT_ERROR_PTR.store(&mut mutex, Ordering::SeqCst);
|
||||
|
||||
let old_handler = unsafe { xlib::XSetErrorHandler(Some(error_handler)) };
|
||||
let panic_result = std::panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
let mut h = XErrorHandler {
|
||||
display,
|
||||
mutex: &mutex,
|
||||
};
|
||||
handler(&mut h)
|
||||
}));
|
||||
// Whatever happened, restore old error handler
|
||||
unsafe { xlib::XSetErrorHandler(old_handler) };
|
||||
CURRENT_ERROR_PTR.store(::core::ptr::null_mut(), Ordering::SeqCst);
|
||||
|
||||
match panic_result {
|
||||
Ok(v) => v,
|
||||
Err(e) => std::panic::resume_unwind(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct XLibError {
|
||||
inner: xlib::XErrorEvent,
|
||||
}
|
||||
|
||||
impl XLibError {
|
||||
pub fn get_display_name(&self, buf: &mut [u8]) -> &CStr {
|
||||
unsafe {
|
||||
xlib::XGetErrorText(
|
||||
self.inner.display,
|
||||
self.inner.error_code.into(),
|
||||
buf.as_mut_ptr().cast(),
|
||||
(buf.len() - 1) as i32,
|
||||
);
|
||||
}
|
||||
|
||||
*buf.last_mut().unwrap() = 0;
|
||||
// SAFETY: whatever XGetErrorText did or not, we guaranteed there is a nul byte at the end of the buffer
|
||||
unsafe { CStr::from_ptr(buf.as_mut_ptr().cast()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for XLibError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let mut buf = [0; 255];
|
||||
let display_name = self.get_display_name(&mut buf).to_string_lossy();
|
||||
|
||||
f.debug_struct("XLibError")
|
||||
.field("error_code", &self.inner.error_code)
|
||||
.field("error_message", &display_name)
|
||||
.field("minor_code", &self.inner.minor_code)
|
||||
.field("request_code", &self.inner.request_code)
|
||||
.field("type", &self.inner.type_)
|
||||
.field("resource_id", &self.inner.resourceid)
|
||||
.field("serial", &self.inner.serial)
|
||||
.finish()
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue