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