use anyhow::Context;
use std::{
convert::TryInto,
mem::{self, MaybeUninit},
os::raw::c_int,
};
use x11::{glx, xlib};
pub struct Window {
display: *mut xlib::Display,
id: u64,
gl_context: glx::GLXContext,
attrs: xlib::XWindowAttributes,
}
impl Window {
#[rustfmt::skip]
const FRAME_BUFFER_CONFIG_ATTRIBUTES: [i32; 17] = [
glx::GLX_RENDER_TYPE , glx::GLX_RGBA_BIT,
glx::GLX_DRAWABLE_TYPE, glx::GLX_PBUFFER_BIT,
glx::GLX_DOUBLEBUFFER , xlib::True,
glx::GLX_RED_SIZE , 8,
glx::GLX_GREEN_SIZE , 8,
glx::GLX_BLUE_SIZE , 8,
glx::GLX_ALPHA_SIZE , 8,
glx::GLX_DEPTH_SIZE , 16,
glx::GLX_NONE,
];
#[rustfmt::skip]
const GL_CONFIG_ATTRIBUTES: [i32; 10] = [
0x2091, 3,
0x2092, 0,
0x2094, 0x2,
0x9126, 0x1,
0, 0
];
pub fn from_window_id(id: u64) -> Result<Self, anyhow::Error> {
let display = unsafe { xlib::XOpenDisplay(std::ptr::null()) };
let screen = unsafe { xlib::XDefaultScreen(display) };
let mut attrs = MaybeUninit::uninit();
anyhow::ensure!(
unsafe { xlib::XGetWindowAttributes(display, id, attrs.as_mut_ptr()) } != 0,
"Unable to get window attributes"
);
let attrs = unsafe { attrs.assume_init() };
let mut fb_configs_len = MaybeUninit::uninit();
let fb_configs = unsafe {
glx::glXChooseFBConfig(
display,
screen,
Self::FRAME_BUFFER_CONFIG_ATTRIBUTES.as_ptr(),
fb_configs_len.as_mut_ptr(),
)
};
anyhow::ensure!(!fb_configs.is_null(), "Unable to retrieve any valid fb configs");
let fb_configs_len = unsafe { fb_configs_len.assume_init() };
log::info!("Found {fb_configs_len} frame-buffer configurations at {fb_configs:?}");
anyhow::ensure!(fb_configs_len != 0, "No fg configs found");
let fb_config = unsafe { *fb_configs };
let create_gl_context = unsafe { glx::glXGetProcAddressARB(b"glXCreateContextAttribsARB\0" as *const _) }
.context("Unable to get function")?;
let create_gl_context: unsafe fn(
*mut xlib::Display,
glx::GLXFBConfig,
glx::GLXContext,
xlib::Bool,
*const c_int,
) -> glx::GLXContext = unsafe { mem::transmute(create_gl_context) };
let gl_context = unsafe {
create_gl_context(
display,
fb_config,
std::ptr::null_mut(),
xlib::True,
Self::GL_CONFIG_ATTRIBUTES.as_ptr(),
)
};
anyhow::ensure!(!gl_context.is_null(), "Unable to get gl context");
Ok(Self {
display,
id,
gl_context,
attrs,
})
}
pub fn size(&self) -> [u32; 2] {
[self.width(), self.height()]
}
pub fn width(&self) -> u32 {
self.attrs.width.try_into().expect("Window width was negative")
}
pub fn height(&self) -> u32 {
self.attrs.height.try_into().expect("Window height was negative")
}
pub fn process_events(&self) {
while unsafe { xlib::XPending(self.display) } != 0 {
let mut event = MaybeUninit::uninit();
unsafe { xlib::XNextEvent(self.display, event.as_mut_ptr()) };
}
}
pub fn is_context_current(&self) -> bool {
let gl_context = unsafe { glx::glXGetCurrentContext() };
gl_context == self.gl_context
}
pub fn make_context_current(&self) -> Result<(), anyhow::Error> {
let res = unsafe { glx::glXMakeContextCurrent(self.display, self.id, self.id, self.gl_context) };
anyhow::ensure!(res == 1, "Failed to make context current");
Ok(())
}
pub fn swap_buffers(&self) {
unsafe {
glx::glXSwapBuffers(self.display, self.id);
}
}
}