Skip to content

Commit 40e4152

Browse files
committed
wip
1 parent 44e0b4a commit 40e4152

4 files changed

Lines changed: 279 additions & 154 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ authors = [
1212
"Robbert van der Helm <mail@robbertvanderhelm.nl>",
1313
"Adrien Prokopowicz <prokopylmc@gmail.com>"
1414
]
15-
edition = "2018"
15+
edition = "2021"
1616
license = "MIT OR Apache-2.0"
1717
description = "Low-level windowing system geared towards making audio plugin UIs."
1818
keywords = ["windowing", "audio", "plugin"]

src/gl/x11.rs

Lines changed: 31 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
use super::{GlConfig, GlError, Profile};
22
use crate::x11::XcbConnection;
33
use std::ffi::{c_void, CString};
4-
use std::os::raw::{c_int, c_ulong};
4+
use std::os::raw::c_ulong;
55
use std::rc::Rc;
66
use x11_dl::error::OpenError;
7-
use x11_dl::glx;
8-
use x11_dl::glx::Glx;
9-
use x11_dl::xlib;
7+
use x11_dl::glx::{__GLXFBConfigRec, GLXContext};
108

119
mod errors;
10+
mod glx;
11+
use glx::Glx;
1212

1313
#[derive(Debug)]
1414
pub enum CreationFailedError {
15-
InvalidFBConfig,
15+
NoValidFBConfig,
1616
NoVisual,
1717
GetProcAddressFailed,
1818
MakeCurrentFailed,
@@ -33,42 +33,27 @@ impl From<OpenError> for GlError {
3333
}
3434
}
3535

36-
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/GLX_ARB_create_context.txt
37-
38-
type GlXCreateContextAttribsARB = unsafe extern "C" fn(
39-
dpy: *mut xlib::Display,
40-
fbc: glx::GLXFBConfig,
41-
share_context: glx::GLXContext,
42-
direct: xlib::Bool,
43-
attribs: *const c_int,
44-
) -> glx::GLXContext;
45-
46-
// See https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_swap_control.txt
47-
48-
type GlXSwapIntervalEXT =
49-
unsafe extern "C" fn(dpy: *mut xlib::Display, drawable: glx::GLXDrawable, interval: i32);
50-
51-
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_framebuffer_sRGB.txt
52-
53-
const GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB: i32 = 0x20B2;
54-
5536
fn get_proc_address(glx: &Glx, symbol: &str) -> *const c_void {
5637
let symbol = CString::new(symbol).unwrap();
57-
unsafe { (glx.glXGetProcAddress)(symbol.as_ptr() as *const u8).unwrap() as *const c_void }
38+
39+
match glx.get_proc_address(&symbol) {
40+
Some(ptr) => ptr.as_ptr(),
41+
None => std::ptr::null(),
42+
}
5843
}
5944

6045
pub struct GlContext {
6146
glx: Glx,
6247
window: c_ulong,
6348
connection: Rc<XcbConnection>,
64-
context: glx::GLXContext,
49+
context: GLXContext,
6550
}
6651

6752
/// The frame buffer configuration along with the general OpenGL configuration to somewhat minimize
6853
/// misuse.
6954
pub struct FbConfig {
7055
gl_config: GlConfig,
71-
fb_config: *mut glx::__GLXFBConfigRec,
56+
fb_config: *mut __GLXFBConfigRec,
7257
}
7358

7459
/// The configuration a window should be created with after calling
@@ -93,70 +78,21 @@ impl GlContext {
9378
let glx = Glx::open()?;
9479

9580
let context = errors::XErrorHandler::handle(&connection, |error_handler| {
96-
#[allow(non_snake_case)]
97-
let glXCreateContextAttribsARB: GlXCreateContextAttribsARB = {
98-
let addr = get_proc_address(&glx, "glXCreateContextAttribsARB");
99-
if addr.is_null() {
100-
return Err(GlError::CreationFailed(CreationFailedError::GetProcAddressFailed));
101-
} else {
102-
#[allow(clippy::missing_transmute_annotations)]
103-
std::mem::transmute(addr)
104-
}
105-
};
106-
107-
#[allow(non_snake_case)]
108-
let glXSwapIntervalEXT: GlXSwapIntervalEXT = {
109-
let addr = get_proc_address(&glx, "glXSwapIntervalEXT");
110-
if addr.is_null() {
111-
return Err(GlError::CreationFailed(CreationFailedError::GetProcAddressFailed));
112-
} else {
113-
#[allow(clippy::missing_transmute_annotations)]
114-
std::mem::transmute(addr)
115-
}
81+
let Some(create_context) = glx.get_glx_create_context_attribs_arb() else {
82+
return Err(GlError::CreationFailed(CreationFailedError::GetProcAddressFailed));
11683
};
11784

118-
error_handler.check()?;
119-
120-
let profile_mask = match config.gl_config.profile {
121-
Profile::Core => glx::arb::GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
122-
Profile::Compatibility => glx::arb::GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
85+
let Some(swap_interval) = glx.get_glx_swap_interval_ext() else {
86+
return Err(GlError::CreationFailed(CreationFailedError::GetProcAddressFailed));
12387
};
12488

125-
#[rustfmt::skip]
126-
let ctx_attribs = [
127-
glx::arb::GLX_CONTEXT_MAJOR_VERSION_ARB, config.gl_config.version.0 as i32,
128-
glx::arb::GLX_CONTEXT_MINOR_VERSION_ARB, config.gl_config.version.1 as i32,
129-
glx::arb::GLX_CONTEXT_PROFILE_MASK_ARB, profile_mask,
130-
0,
131-
];
132-
133-
let context = glXCreateContextAttribsARB(
134-
connection.dpy,
135-
config.fb_config,
136-
std::ptr::null_mut(),
137-
1,
138-
ctx_attribs.as_ptr(),
139-
);
89+
let context = create_context.call(&connection, &config, error_handler)?;
14090

141-
error_handler.check()?;
142-
143-
if context.is_null() {
144-
return Err(GlError::CreationFailed(CreationFailedError::ContextCreationFailed));
145-
}
146-
147-
let res = (glx.glXMakeCurrent)(connection.dpy, window, context);
148-
error_handler.check()?;
149-
if res == 0 {
150-
return Err(GlError::CreationFailed(CreationFailedError::MakeCurrentFailed));
151-
}
152-
153-
glXSwapIntervalEXT(connection.dpy, window, config.gl_config.vsync as i32);
154-
error_handler.check()?;
155-
156-
if (glx.glXMakeCurrent)(connection.dpy, 0, std::ptr::null_mut()) == 0 {
157-
error_handler.check()?;
158-
return Err(GlError::CreationFailed(CreationFailedError::MakeCurrentFailed));
159-
}
91+
// TODO: handle errors here leaking the context
92+
glx.with_current_context(&connection, window, context, error_handler, || {
93+
swap_interval(connection.dpy, window, config.gl_config.vsync as i32);
94+
error_handler.check()
95+
})??;
16096

16197
Ok(context)
16298
})?;
@@ -173,45 +109,11 @@ impl GlContext {
173109
let glx = Glx::open()?;
174110

175111
errors::XErrorHandler::handle(conn, |error_handler| {
176-
let screen = conn.screen;
177-
178-
#[rustfmt::skip]
179-
let fb_attribs = [
180-
glx::GLX_X_RENDERABLE, 1,
181-
glx::GLX_X_VISUAL_TYPE, glx::GLX_TRUE_COLOR,
182-
glx::GLX_DRAWABLE_TYPE, glx::GLX_WINDOW_BIT,
183-
glx::GLX_RENDER_TYPE, glx::GLX_RGBA_BIT,
184-
glx::GLX_RED_SIZE, config.red_bits as i32,
185-
glx::GLX_GREEN_SIZE, config.green_bits as i32,
186-
glx::GLX_BLUE_SIZE, config.blue_bits as i32,
187-
glx::GLX_ALPHA_SIZE, config.alpha_bits as i32,
188-
glx::GLX_DEPTH_SIZE, config.depth_bits as i32,
189-
glx::GLX_STENCIL_SIZE, config.stencil_bits as i32,
190-
glx::GLX_DOUBLEBUFFER, config.double_buffer as i32,
191-
glx::GLX_SAMPLE_BUFFERS, config.samples.is_some() as i32,
192-
glx::GLX_SAMPLES, config.samples.unwrap_or(0) as i32,
193-
GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, config.srgb as i32,
194-
0,
195-
];
196-
197-
let mut n_configs = 0;
198-
let fb_config = unsafe {
199-
(glx.glXChooseFBConfig)(conn.dpy, screen, fb_attribs.as_ptr(), &mut n_configs)
200-
};
201-
202-
error_handler.check()?;
203-
if n_configs <= 0 || fb_config.is_null() {
204-
return Err(GlError::CreationFailed(CreationFailedError::InvalidFBConfig));
205-
}
112+
let fb_config = glx.choose_best_fb_config(conn, &config, error_handler)?;
206113

207114
// Now that we have a matching framebuffer config, we need to know which visual matches
208115
// thsi config so the window is compatible with the OpenGL context we're about to create
209-
let fb_config = unsafe { fb_config.read() };
210-
let visual = unsafe { (glx.glXGetVisualFromFBConfig)(conn.dpy, fb_config) };
211-
if visual.is_null() {
212-
return Err(GlError::CreationFailed(CreationFailedError::NoVisual));
213-
}
214-
let visual = unsafe { visual.read() };
116+
let visual = glx.get_visual_from_fb_config(conn, fb_config, error_handler)?;
215117

216118
Ok((
217119
FbConfig { fb_config, gl_config: config },
@@ -222,21 +124,15 @@ impl GlContext {
222124

223125
pub unsafe fn make_current(&self) {
224126
errors::XErrorHandler::handle(&self.connection, |error_handler| {
225-
let res = (self.glx.glXMakeCurrent)(self.connection.dpy, self.window, self.context);
226-
error_handler.check().unwrap();
227-
if res == 0 {
228-
panic!("make_current failed")
229-
}
127+
self.glx
128+
.make_current(&self.connection, self.window, self.context, error_handler)
129+
.unwrap();
230130
})
231131
}
232132

233133
pub unsafe fn make_not_current(&self) {
234134
errors::XErrorHandler::handle(&self.connection, |error_handler| {
235-
let res = (self.glx.glXMakeCurrent)(self.connection.dpy, 0, std::ptr::null_mut());
236-
error_handler.check().unwrap();
237-
if res == 0 {
238-
panic!("make_not_current failed")
239-
}
135+
self.glx.clear_current(&self.connection, error_handler).unwrap();
240136
})
241137
}
242138

@@ -245,12 +141,9 @@ impl GlContext {
245141
}
246142

247143
pub fn swap_buffers(&self) {
248-
unsafe {
249-
errors::XErrorHandler::handle(&self.connection, |error_handler| {
250-
(self.glx.glXSwapBuffers)(self.connection.dpy, self.window);
251-
error_handler.check().unwrap();
252-
})
253-
}
144+
errors::XErrorHandler::handle(&self.connection, |error_handler| {
145+
self.glx.swap_buffers(&self.connection, self.window, error_handler).unwrap()
146+
})
254147
}
255148
}
256149

src/gl/x11/errors.rs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,31 @@ use std::fmt::{Debug, Display, Formatter};
33
use x11_dl::xlib;
44

55
use crate::x11::XcbConnection;
6-
use std::cell::RefCell;
6+
use std::cell::Cell;
77
use std::error::Error;
88
use std::os::raw::{c_int, c_uchar, c_ulong};
99
use std::panic::AssertUnwindSafe;
1010

1111
thread_local! {
1212
/// Used as part of [`XErrorHandler::handle()`]. When an X11 error occurs during this function,
13-
/// the error gets copied to this RefCell after which the program is allowed to resume. The
13+
/// the error gets copied to this Cell after which the program is allowed to resume. The
1414
/// error can then be converted to a regular Rust Result value afterward.
15-
static CURRENT_X11_ERROR: RefCell<Option<CaughtXLibError>> = const { RefCell::new(None) };
15+
static CURRENT_X11_ERROR: Cell<Option<CaughtXLibError>> = const { Cell::new(None) };
1616
}
1717

1818
/// A helper struct for safe X11 error handling
1919
pub struct XErrorHandler<'a> {
2020
conn: &'a XcbConnection,
21-
error: &'a RefCell<Option<CaughtXLibError>>,
21+
error: &'a Cell<Option<CaughtXLibError>>,
2222
}
2323

2424
impl<'a> XErrorHandler<'a> {
2525
/// Syncs and checks if any previous X11 calls from the given display returned an error
26-
pub fn check(&mut self) -> Result<(), XLibError> {
26+
pub fn check(&self) -> Result<(), XLibError> {
2727
// Flush all possible previous errors
2828
unsafe { (self.conn.xlib.XSync)(self.conn.dpy, 0) };
2929

30-
let error = self.error.borrow_mut().take();
30+
let error = self.error.take();
3131

3232
match error {
3333
None => Ok(()),
@@ -43,17 +43,16 @@ impl<'a> XErrorHandler<'a> {
4343
unsafe extern "C" fn error_handler(
4444
_dpy: *mut xlib::Display, err: *mut xlib::XErrorEvent,
4545
) -> i32 {
46-
// SAFETY: the error pointer should be safe to access
47-
let err = &*err;
46+
// SAFETY: the error pointer should always be valid for reads
47+
let err = unsafe { err.read() };
4848

4949
CURRENT_X11_ERROR.with(|error| {
50-
let mut error = error.borrow_mut();
51-
match error.as_mut() {
50+
match error.get() {
5251
// If multiple errors occur, keep the first one since that's likely going to be the
5352
// cause of the other errors
5453
Some(_) => 1,
5554
None => {
56-
*error = Some(CaughtXLibError::from_event(err));
55+
error.set(Some(CaughtXLibError::from_event(err)));
5756
0
5857
}
5958
}
@@ -65,9 +64,7 @@ impl<'a> XErrorHandler<'a> {
6564

6665
CURRENT_X11_ERROR.with(|error| {
6766
// Make sure to clear any errors from the last call to this function
68-
{
69-
*error.borrow_mut() = None;
70-
}
67+
error.set(None);
7168

7269
let old_handler = unsafe { (conn.xlib.XSetErrorHandler)(Some(error_handler)) };
7370
let panic_result = std::panic::catch_unwind(AssertUnwindSafe(|| {
@@ -85,6 +82,7 @@ impl<'a> XErrorHandler<'a> {
8582
}
8683
}
8784

85+
#[derive(Copy, Clone)]
8886
struct CaughtXLibError {
8987
type_: c_int,
9088
resourceid: xlib::XID,
@@ -95,7 +93,7 @@ struct CaughtXLibError {
9593
}
9694

9795
impl CaughtXLibError {
98-
fn from_event(error: &xlib::XErrorEvent) -> CaughtXLibError {
96+
fn from_event(error: xlib::XErrorEvent) -> CaughtXLibError {
9997
Self {
10098
type_: error.type_,
10199
resourceid: error.resourceid,

0 commit comments

Comments
 (0)