use super::{GlConfig, GlError};
use crate::x11::XcbConnection;
use std::ffi::{c_void, CString};
use std::os::raw::c_ulong;
use std::rc::Rc;
use x11_dl::error::OpenError;
use x11_dl::glx::GLXContext;
use crate::wrappers::glx::*;
use crate::wrappers::xlib::{XErrorHandler, XLibError};
#[derive(Debug)]
pub enum CreationFailedError {
NoValidFBConfig,
NoVisual,
GetProcAddressFailed,
MakeCurrentFailed,
ContextCreationFailed,
X11Error(XLibError),
OpenError(OpenError),
}
impl From<XLibError> for GlError {
fn from(e: XLibError) -> Self {
GlError::CreationFailed(CreationFailedError::X11Error(e))
}
}
impl From<OpenError> for GlError {
fn from(e: OpenError) -> Self {
GlError::CreationFailed(CreationFailedError::OpenError(e))
}
}
pub struct GlContext {
glx: Glx,
window: c_ulong,
connection: Rc<XcbConnection>,
context: GLXContext,
}
pub struct FbConfig {
gl_config: GlConfig,
fb_config: GlxFbConfig,
}
pub struct WindowConfig {
pub depth: u8,
pub visual: u32,
}
impl GlContext {
pub fn create(
window: c_ulong, connection: Rc<XcbConnection>, config: FbConfig,
) -> Result<GlContext, GlError> {
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(GlError::CreationFailed(CreationFailedError::GetProcAddressFailed));
};
let Some(swap_interval) = glx.get_glx_swap_interval_ext() else {
return Err(GlError::CreationFailed(CreationFailedError::GetProcAddressFailed));
};
let context = create_context.call(
xlib_connection,
&config.gl_config,
config.fb_config,
error_handler,
)?;
let context = GlContext { glx, window, connection: Rc::clone(&connection), context };
unsafe {
context.glx.with_current_context(
xlib_connection,
window,
context.context,
error_handler,
|| {
swap_interval(
xlib_connection.as_raw(),
window,
config.gl_config.vsync as i32,
);
error_handler.check()
},
)??;
}
Ok(context)
})
}
pub fn get_fb_config_and_visual(
connection: &XcbConnection, config: GlConfig,
) -> Result<(FbConfig, WindowConfig), GlError> {
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) {
XErrorHandler::handle(self.connection.conn.xlib_connection(), |error_handler| {
self.glx
.make_current(
self.connection.conn.xlib_connection(),
self.window,
self.context,
error_handler,
)
.unwrap();
})
}
pub unsafe fn make_not_current(&self) {
XErrorHandler::handle(self.connection.conn.xlib_connection(), |error_handler| {
self.glx.clear_current(self.connection.conn.xlib_connection(), error_handler).unwrap();
})
}
pub fn get_proc_address(&self, symbol: &str) -> *const c_void {
let symbol = CString::new(symbol).unwrap();
match self.glx.get_proc_address(&symbol) {
Some(ptr) => ptr.as_ptr(),
None => std::ptr::null(),
}
}
pub fn swap_buffers(&self) {
XErrorHandler::handle(self.connection.conn.xlib_connection(), |error_handler| {
self.glx
.swap_buffers(self.connection.conn.xlib_connection(), self.window, error_handler)
.unwrap()
})
}
}
impl Drop for GlContext {
fn drop(&mut self) {
unsafe { self.glx.destroy_context(self.connection.conn.xlib_connection(), self.context) }
}
}