use super::*;
use crate::gl::*;
use crate::wrappers::glx::*;
use crate::wrappers::xlib::{XErrorHandler, XLibError};
use crate::platform::x11::xcb_window::XcbWindow;
use std::ffi::{c_ulong, c_void, CStr};
use std::rc::Rc;
use x11_dl::error::OpenError;
use x11_dl::glx::GLXContext;
#[derive(Debug)]
pub enum CreationFailedError {
NoValidFBConfig,
NoVisual,
GetProcAddressFailed,
MakeCurrentFailed,
ContextCreationFailed,
X11Error(XLibError),
OpenError(OpenError),
}
impl Display for CreationFailedError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
CreationFailedError::NoValidFBConfig => {
f.write_str("Could not find a valid Framebuffer configuration")
}
CreationFailedError::NoVisual => {
f.write_str("Could not find a matching visual configuration")
}
CreationFailedError::GetProcAddressFailed => f.write_str("GetProcAddress failed"),
CreationFailedError::MakeCurrentFailed => f.write_str("MakeCurrent failed"),
CreationFailedError::ContextCreationFailed => f.write_str("Faile to create GL context"),
CreationFailedError::X11Error(e) => e.fmt(f),
CreationFailedError::OpenError(e) => e.fmt(f),
}
}
}
pub type GlContext = Rc<GlContextInner>;
pub struct GlContextInner {
glx: Glx,
window: NonZeroU32,
connection: Rc<X11Connection>,
context: GLXContext,
}
pub struct FbConfig {
gl_config: GlConfig,
fb_config: GlxFbConfig,
}
pub struct WindowConfig {
pub depth: u8,
pub visual: u32,
}
impl GlContextInner {
pub fn create(
window: &XcbWindow, connection: Rc<X11Connection>, config: FbConfig,
) -> Result<Rc<GlContextInner>> {
let glx = Glx::open()?;
let xlib_connection = connection.conn.xlib_connection();
XErrorHandler::handle(xlib_connection, |error_handler| {
let Some(create_context) = glx.get_glx_create_context_attribs_arb() else {
return Err(CreationFailedError::GetProcAddressFailed.into());
};
let Some(swap_interval) = glx.get_glx_swap_interval_ext() else {
return Err(CreationFailedError::GetProcAddressFailed.into());
};
let context = create_context.call(
xlib_connection,
&config.gl_config,
config.fb_config,
error_handler,
)?;
let window_id = window.id().get().into();
let context = GlContextInner {
glx,
window: window.id(),
connection: Rc::clone(&connection),
context,
};
unsafe {
context.glx.with_current_context(
xlib_connection,
window_id,
context.context,
error_handler,
|| {
swap_interval(
xlib_connection.as_raw(),
window_id,
config.gl_config.vsync as i32,
);
error_handler.check()
},
)??;
}
Ok(Rc::new(context))
})
}
pub fn get_fb_config_and_visual(
connection: &X11Connection, config: GlConfig,
) -> Result<(FbConfig, WindowConfig)> {
let glx = Glx::open()?;
let xlib_connection = connection.conn.xlib_connection();
XErrorHandler::handle(xlib_connection, |error_handler| {
let fb_config = glx.choose_best_fb_config(xlib_connection, &config, error_handler)?;
let visual =
glx.get_visual_from_fb_config(xlib_connection, fb_config, error_handler)?;
Ok((
FbConfig { fb_config, gl_config: config },
WindowConfig { depth: visual.depth as u8, visual: visual.visualid as u32 },
))
})
}
pub unsafe fn make_current(&self) -> Result<()> {
XErrorHandler::handle(self.connection.conn.xlib_connection(), |error_handler| {
self.glx.make_current(
self.connection.conn.xlib_connection(),
self.window_id(),
self.context,
error_handler,
)
})
}
pub unsafe fn make_not_current(&self) -> Result<()> {
XErrorHandler::handle(self.connection.conn.xlib_connection(), |error_handler| {
self.glx.clear_current(self.connection.conn.xlib_connection(), error_handler)
})
}
fn window_id(&self) -> c_ulong {
self.window.get().into()
}
pub fn get_proc_address(&self, symbol: &CStr) -> *const c_void {
match self.glx.get_proc_address(symbol) {
Some(ptr) => ptr.as_ptr(),
None => std::ptr::null(),
}
}
pub fn swap_buffers(&self) -> Result<()> {
XErrorHandler::handle(self.connection.conn.xlib_connection(), |error_handler| {
self.glx.swap_buffers(
self.connection.conn.xlib_connection(),
self.window_id(),
error_handler,
)
})
}
}
impl Drop for GlContextInner {
fn drop(&mut self) {
unsafe { self.glx.destroy_context(self.connection.conn.xlib_connection(), self.context) }
}
}