From 4e21575e04813bf9ef65f5c0a8e10747490f4d00 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 22 Aug 2026 12:05:10 +0200 Subject: [PATCH 01/11] wip --- Cargo.toml | 2 ++ src/platform/x11/error.rs | 13 +++++++ src/platform/x11/gl.rs | 10 ++++++ src/wrappers.rs | 4 +++ src/wrappers/egl.rs | 39 +++++++++++++++++++++ src/wrappers/egl/bound_api.rs | 48 +++++++++++++++++++++++++ src/wrappers/egl/error.rs | 24 +++++++++++++ src/wrappers/egl/sys.rs | 66 +++++++++++++++++++++++++++++++++++ 8 files changed, 206 insertions(+) create mode 100644 src/wrappers/egl.rs create mode 100644 src/wrappers/egl/bound_api.rs create mode 100644 src/wrappers/egl/error.rs create mode 100644 src/wrappers/egl/sys.rs diff --git a/Cargo.toml b/Cargo.toml index 0d8ad815..809da986 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,8 @@ x11-dl = { version = "2.21.0" } calloop = "0.14.4" percent-encoding = "2.3.2" bytemuck = { version = "1.25.0", features = ["extern_crate_alloc"] } +libloading = "0.9.0" +khronos-egl = "6.0.0" [target.'cfg(target_os="windows")'.dependencies] windows = { version = "0.62.2", features = [ diff --git a/src/platform/x11/error.rs b/src/platform/x11/error.rs index 98fdd21c..64489513 100644 --- a/src/platform/x11/error.rs +++ b/src/platform/x11/error.rs @@ -58,6 +58,8 @@ pub enum PlatformError { #[cfg(feature = "opengl")] XLib(crate::wrappers::xlib::XLibError), #[cfg(feature = "opengl")] + EGl(crate::wrappers::egl::EglError), + #[cfg(feature = "opengl")] Gl(super::gl::CreationFailedError), } @@ -87,6 +89,8 @@ impl Display for PlatformError { PlatformError::XLib(e) => e.fmt(f), #[cfg(feature = "opengl")] PlatformError::Gl(e) => e.fmt(f), + #[cfg(feature = "opengl")] + PlatformError::EGl(e) => e.fmt(f), } } } @@ -100,6 +104,8 @@ impl std::error::Error for PlatformError { PlatformError::Handler(e) => Some(e.source()), #[cfg(feature = "opengl")] PlatformError::XLib(e) => Some(e), + #[cfg(feature = "opengl")] + PlatformError::EGl(e) => Some(e), _ => None, } } @@ -221,6 +227,13 @@ impl From for PlatformError { } } +#[cfg(feature = "opengl")] +impl From for PlatformError { + fn from(value: crate::wrappers::egl::EglError) -> Self { + Self::EGl(value) + } +} + pub trait CookieExt { fn check_warn(self); } diff --git a/src/platform/x11/gl.rs b/src/platform/x11/gl.rs index d6557569..1b41fe38 100644 --- a/src/platform/x11/gl.rs +++ b/src/platform/x11/gl.rs @@ -2,8 +2,10 @@ use super::*; use crate::gl::*; use crate::wrappers::glx::*; use crate::wrappers::xlib::{XErrorHandler, XLibError}; +use std::error::Error; use crate::platform::x11::xcb_window::XcbWindow; +use crate::wrappers::egl::{Egl, MissingSymbolError}; use std::ffi::{c_ulong, c_void, CStr}; use std::rc::Rc; use x11_dl::error::OpenError; @@ -18,6 +20,8 @@ pub enum CreationFailedError { ContextCreationFailed, X11Error(XLibError), OpenError(OpenError), + EGLLoadError(libloading::Error), + EGLMissingSymbol(MissingSymbolError), } impl Display for CreationFailedError { @@ -34,6 +38,10 @@ impl Display for CreationFailedError { CreationFailedError::ContextCreationFailed => f.write_str("Faile to create GL context"), CreationFailedError::X11Error(e) => e.fmt(f), CreationFailedError::OpenError(e) => e.fmt(f), + CreationFailedError::EGLLoadError(e) => { + write!(f, "Could not load EGL library: {e}, {:?}", e.source()) + } + CreationFailedError::EGLMissingSymbol(e) => e.fmt(f), } } } @@ -75,6 +83,8 @@ impl GlContextInner { ) -> Result> { let glx = Glx::open()?; + let egl = Egl::open()?; + let xlib_connection = connection.conn.xlib_connection(); XErrorHandler::handle(xlib_connection, |error_handler| { diff --git a/src/wrappers.rs b/src/wrappers.rs index 570c0588..b145f05c 100644 --- a/src/wrappers.rs +++ b/src/wrappers.rs @@ -23,6 +23,10 @@ pub mod xkbcommon; #[cfg(all(target_os = "linux", feature = "opengl"))] pub mod glx; +/// Wrappers and utilities around EGL. +#[cfg(all(target_os = "linux", feature = "opengl"))] +pub mod egl; + /// Wrappers and utilities around the Win32 API. #[cfg(target_os = "windows")] pub mod win32; diff --git a/src/wrappers/egl.rs b/src/wrappers/egl.rs new file mode 100644 index 00000000..d3d530a1 --- /dev/null +++ b/src/wrappers/egl.rs @@ -0,0 +1,39 @@ +use crate::platform::gl::CreationFailedError; +use libloading::Library; +use std::sync::Arc; + +mod bound_api; +mod error; +mod sys; + +use bound_api::BoundApi; +use sys::Functions; + +pub use error::EglError; +pub use sys::MissingSymbolError; + +struct EglInner { + _library: Library, + functions: Functions, +} + +#[derive(Clone)] +pub struct Egl { + inner: Arc, +} + +impl Egl { + pub fn open() -> Result { + let library = + unsafe { Library::new("libEGL.so.1").or_else(|_| Library::new("libEGL.so")) }?; + + let functions = unsafe { Functions::load_from(&library)? }; + + Ok(Self { inner: Arc::new(EglInner { _library: library, functions }) }) + } + + pub fn with_opengl(&self, handler: impl FnOnce(&BoundApi) -> T) -> Result { + let api = BoundApi::new(self)?; + Ok(handler(&api)) + } +} diff --git a/src/wrappers/egl/bound_api.rs b/src/wrappers/egl/bound_api.rs new file mode 100644 index 00000000..31dc90a7 --- /dev/null +++ b/src/wrappers/egl/bound_api.rs @@ -0,0 +1,48 @@ +use super::*; + +pub struct BoundApi { + egl: Egl, + previous_api: Option, +} + +impl BoundApi { + pub(super) fn new(egl: &Egl) -> Result { + let previous_api = egl.query_api(); + let result = unsafe { (egl.inner.functions.eglBindAPI)(sys::OPENGL_API) }; + if result == sys::FALSE { + return Err(EglError::from_last_error(egl)); + } + + Ok(Self { previous_api, egl: egl.clone() }) + } +} + +impl Drop for BoundApi { + fn drop(&mut self) { + let Some(previous_api) = self.previous_api else { return }; + + if let Err(e) = self.egl.bind_api(previous_api) { + crate::warn!("Failed to restore EGL api: {}", e); + } + } +} + +impl Egl { + fn query_api(&self) -> Option { + let result = unsafe { (self.inner.functions.eglQueryAPI)() }; + if result == sys::ENUM_NONE { + None + } else { + Some(result) + } + } + + fn bind_api(&self, api: sys::Enum) -> Result<(), EglError> { + let result = unsafe { (self.inner.functions.eglBindAPI)(api) }; + if result == sys::FALSE { + Err(EglError::from_last_error(self)) + } else { + Ok(()) + } + } +} diff --git a/src/wrappers/egl/error.rs b/src/wrappers/egl/error.rs new file mode 100644 index 00000000..f7b056b4 --- /dev/null +++ b/src/wrappers/egl/error.rs @@ -0,0 +1,24 @@ +use super::*; +use std::error::Error; +use std::ffi::c_int; +use std::fmt::Display; + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct EglError { + code: c_int, +} + +impl EglError { + pub fn from_last_error(egl: &Egl) -> EglError { + let code = unsafe { (egl.inner.functions.eglGetError)() }; + Self { code } + } +} + +impl Display for EglError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + todo!() + } +} + +impl Error for EglError {} diff --git a/src/wrappers/egl/sys.rs b/src/wrappers/egl/sys.rs new file mode 100644 index 00000000..48d64e78 --- /dev/null +++ b/src/wrappers/egl/sys.rs @@ -0,0 +1,66 @@ +#![allow(non_snake_case, non_camel_case_types, reason = "To match EGL function naming")] + +use crate::platform::gl::CreationFailedError; +use libloading::Library; +use std::ffi::*; +use std::fmt::{Display, Formatter}; + +pub type Enum = c_uint; +pub type Boolean = c_uint; +pub type Int = c_int; + +pub type eglGetError = unsafe extern "system" fn() -> c_int; +pub type eglBindAPI = unsafe extern "system" fn(Enum) -> Boolean; +pub type eglQueryAPI = unsafe extern "system" fn() -> Enum; + +pub const NONE: Int = 0x3038; +pub const ENUM_NONE: Enum = 0x3038; +pub const OPENGL_API: Enum = 0x30A2; +pub const FALSE: Boolean = 0; + +#[derive(Debug, Copy, Clone)] +pub struct MissingSymbolError { + name: &'static CStr, +} + +impl Display for MissingSymbolError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + todo!() + } +} + +impl From for CreationFailedError { + fn from(value: MissingSymbolError) -> Self { + Self::EGLMissingSymbol(value) + } +} + +impl From for CreationFailedError { + fn from(value: libloading::Error) -> Self { + Self::EGLLoadError(value) + } +} + +pub struct Functions { + pub eglGetError: eglGetError, + pub eglBindAPI: eglBindAPI, + pub eglQueryAPI: eglQueryAPI, +} + +impl Functions { + pub unsafe fn load_from(library: &Library) -> Result { + Ok(Self { + eglGetError: Self::get(library, c"eglGetError")?, + eglBindAPI: Self::get(library, c"eglBindAPI")?, + eglQueryAPI: Self::get(library, c"eglQueryAPI")?, + }) + } + + unsafe fn get( + library: &Library, name: &'static CStr, + ) -> Result { + let symbol = library.get::>(name)?; + let symbol = symbol.lift_option().ok_or(MissingSymbolError { name })?; + Ok(*symbol) + } +} From 6c43a0c90509c5b8178e8eec8f77c83d5f557734 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 22 Aug 2026 12:58:26 +0200 Subject: [PATCH 02/11] wip --- src/platform/x11/gl.rs | 1 + src/wrappers/egl.rs | 6 ++++++ src/wrappers/egl/extensions.rs | 39 ++++++++++++++++++++++++++++++++++ src/wrappers/egl/sys.rs | 6 ++++++ 4 files changed, 52 insertions(+) create mode 100644 src/wrappers/egl/extensions.rs diff --git a/src/platform/x11/gl.rs b/src/platform/x11/gl.rs index 1b41fe38..5eadff8f 100644 --- a/src/platform/x11/gl.rs +++ b/src/platform/x11/gl.rs @@ -84,6 +84,7 @@ impl GlContextInner { let glx = Glx::open()?; let egl = Egl::open()?; + let exts = egl.query_client_extensions(); let xlib_connection = connection.conn.xlib_connection(); diff --git a/src/wrappers/egl.rs b/src/wrappers/egl.rs index d3d530a1..990eb969 100644 --- a/src/wrappers/egl.rs +++ b/src/wrappers/egl.rs @@ -4,11 +4,13 @@ use std::sync::Arc; mod bound_api; mod error; +mod extensions; mod sys; use bound_api::BoundApi; use sys::Functions; +use crate::wrappers::egl::extensions::Extensions; pub use error::EglError; pub use sys::MissingSymbolError; @@ -36,4 +38,8 @@ impl Egl { let api = BoundApi::new(self)?; Ok(handler(&api)) } + + pub fn query_client_extensions(&self) -> Extensions { + Extensions::new(self) + } } diff --git a/src/wrappers/egl/extensions.rs b/src/wrappers/egl/extensions.rs new file mode 100644 index 00000000..e0d6475f --- /dev/null +++ b/src/wrappers/egl/extensions.rs @@ -0,0 +1,39 @@ +use crate::wrappers::egl::{sys, Egl}; +use std::ffi::CStr; +use std::fmt::Debug; + +pub struct Extensions { + string: Option<&'static CStr>, +} + +impl Debug for Extensions { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self.string { + Some(s) => f.write_str(&s.to_string_lossy()), + None => f.write_str(""), + } + } +} + +impl Extensions { + pub(super) fn new(egl: &Egl) -> Extensions { + Self { string: egl.query_client_extensions_inner() } + } + + pub fn supports(&self, extension_id: &[u8]) -> bool { + let Some(string) = self.string else { return false }; + string.to_bytes().split(|b| *b == b' ').any(|s| s == extension_id) + } +} + +impl Egl { + fn query_client_extensions_inner(&self) -> Option<&'static CStr> { + let result = + unsafe { (self.inner.functions.eglQueryString)(sys::NO_DISPLAY, sys::EXTENSIONS) }; + if result.is_null() { + None + } else { + unsafe { Some(CStr::from_ptr(result)) } + } + } +} diff --git a/src/wrappers/egl/sys.rs b/src/wrappers/egl/sys.rs index 48d64e78..37c487eb 100644 --- a/src/wrappers/egl/sys.rs +++ b/src/wrappers/egl/sys.rs @@ -8,15 +8,19 @@ use std::fmt::{Display, Formatter}; pub type Enum = c_uint; pub type Boolean = c_uint; pub type Int = c_int; +pub type EGLDisplay = *mut c_void; pub type eglGetError = unsafe extern "system" fn() -> c_int; pub type eglBindAPI = unsafe extern "system" fn(Enum) -> Boolean; pub type eglQueryAPI = unsafe extern "system" fn() -> Enum; +pub type eglQueryString = unsafe extern "system" fn(EGLDisplay, Int) -> *const c_char; pub const NONE: Int = 0x3038; pub const ENUM_NONE: Enum = 0x3038; pub const OPENGL_API: Enum = 0x30A2; pub const FALSE: Boolean = 0; +pub const NO_DISPLAY: EGLDisplay = 0 as EGLDisplay; +pub const EXTENSIONS: Int = 0x3055; #[derive(Debug, Copy, Clone)] pub struct MissingSymbolError { @@ -45,6 +49,7 @@ pub struct Functions { pub eglGetError: eglGetError, pub eglBindAPI: eglBindAPI, pub eglQueryAPI: eglQueryAPI, + pub eglQueryString: eglQueryString, } impl Functions { @@ -53,6 +58,7 @@ impl Functions { eglGetError: Self::get(library, c"eglGetError")?, eglBindAPI: Self::get(library, c"eglBindAPI")?, eglQueryAPI: Self::get(library, c"eglQueryAPI")?, + eglQueryString: Self::get(library, c"eglQueryString")?, }) } From 613f63f34401932c7517a017e018b0137ac8b58d Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 22 Aug 2026 14:55:36 +0200 Subject: [PATCH 03/11] wip --- src/platform/x11/gl.rs | 8 +++-- src/wrappers/egl.rs | 7 ++-- src/wrappers/egl/display.rs | 72 +++++++++++++++++++++++++++++++++++++ src/wrappers/egl/sys.rs | 11 ++++++ src/wrappers/glx.rs | 7 ++-- 5 files changed, 97 insertions(+), 8 deletions(-) create mode 100644 src/wrappers/egl/display.rs diff --git a/src/platform/x11/gl.rs b/src/platform/x11/gl.rs index 5eadff8f..f05efa2c 100644 --- a/src/platform/x11/gl.rs +++ b/src/platform/x11/gl.rs @@ -62,6 +62,11 @@ pub struct FbConfig { fb_config: GlxFbConfig, } +enum FbConfigInner { + Glx { glx: Glx, config: GlxFbConfig }, + Egl { egl: Egl }, +} + /// The configuration a window should be created with after calling /// [GlContextInner::get_fb_config_and_visual]. pub struct WindowConfig { @@ -83,9 +88,6 @@ impl GlContextInner { ) -> Result> { let glx = Glx::open()?; - let egl = Egl::open()?; - let exts = egl.query_client_extensions(); - let xlib_connection = connection.conn.xlib_connection(); XErrorHandler::handle(xlib_connection, |error_handler| { diff --git a/src/wrappers/egl.rs b/src/wrappers/egl.rs index 990eb969..3f7ce0b6 100644 --- a/src/wrappers/egl.rs +++ b/src/wrappers/egl.rs @@ -1,8 +1,9 @@ use crate::platform::gl::CreationFailedError; use libloading::Library; -use std::sync::Arc; +use std::rc::Rc; mod bound_api; +mod display; mod error; mod extensions; mod sys; @@ -21,7 +22,7 @@ struct EglInner { #[derive(Clone)] pub struct Egl { - inner: Arc, + inner: Rc, } impl Egl { @@ -31,7 +32,7 @@ impl Egl { let functions = unsafe { Functions::load_from(&library)? }; - Ok(Self { inner: Arc::new(EglInner { _library: library, functions }) }) + Ok(Self { inner: Rc::new(EglInner { _library: library, functions }) }) } pub fn with_opengl(&self, handler: impl FnOnce(&BoundApi) -> T) -> Result { diff --git a/src/wrappers/egl/display.rs b/src/wrappers/egl/display.rs new file mode 100644 index 00000000..d46f0288 --- /dev/null +++ b/src/wrappers/egl/display.rs @@ -0,0 +1,72 @@ +use crate::wrappers::egl::{sys, Egl, EglError}; +use crate::wrappers::xlib::{XlibConnection, XlibXcbConnection}; +use std::ffi::c_void; +use std::ptr::NonNull; + +pub struct EglDisplay { + egl: Egl, + raw: NonNull, +} + +impl EglDisplay { + pub(super) fn new(egl: &Egl, connection: &XlibXcbConnection) -> Result { + let display = egl.create_display_basic(connection.xlib_connection()).unwrap(); + + unsafe { egl.initialize_display(display)? }; + Ok(Self { egl: egl.clone(), raw: display }) + } +} + +impl Drop for EglDisplay { + fn drop(&mut self) { + if let Err(e) = unsafe { self.egl.terminate_display(self.raw) } { + crate::warn!("Failed to terminate EGL display connection: {}", e) + } + } +} + +struct EglVersion { + major: sys::Int, + minor: sys::Int, +} + +impl Egl { + pub fn create_display(&self, connection: &XlibXcbConnection) -> Result { + EglDisplay::new(self, connection) + } + + fn create_display_basic(&self, connection: &XlibConnection) -> Option> { + let result = unsafe { (self.inner.functions.eglGetDisplay)(connection.as_raw().cast()) }; + NonNull::new(result) + } + + unsafe fn initialize_display(&self, raw: NonNull) -> Result { + let mut version = EglVersion { major: 0, minor: 0 }; + + let result = unsafe { + (self.inner.functions.eglInitialize)( + raw.as_ptr(), + &mut version.major, + &mut version.minor, + ) + }; + + if result == sys::FALSE { + return Err(EglError::from_last_error(self)); + } + + dbg!(version.major, version.minor); + + Ok(version) + } + + unsafe fn terminate_display(&self, raw: NonNull) -> Result<(), EglError> { + let result = unsafe { (self.inner.functions.eglTerminate)(raw.as_ptr()) }; + + if result == sys::FALSE { + return Err(EglError::from_last_error(self)); + } + + Ok(()) + } +} diff --git a/src/wrappers/egl/sys.rs b/src/wrappers/egl/sys.rs index 37c487eb..4dbfd31b 100644 --- a/src/wrappers/egl/sys.rs +++ b/src/wrappers/egl/sys.rs @@ -9,11 +9,16 @@ pub type Enum = c_uint; pub type Boolean = c_uint; pub type Int = c_int; pub type EGLDisplay = *mut c_void; +pub type NativeDisplayType = *mut c_void; pub type eglGetError = unsafe extern "system" fn() -> c_int; pub type eglBindAPI = unsafe extern "system" fn(Enum) -> Boolean; pub type eglQueryAPI = unsafe extern "system" fn() -> Enum; pub type eglQueryString = unsafe extern "system" fn(EGLDisplay, Int) -> *const c_char; +pub type eglGetDisplay = unsafe extern "system" fn(NativeDisplayType) -> EGLDisplay; +pub type eglInitialize = + unsafe extern "system" fn(display: EGLDisplay, major: *mut Int, minor: *mut Int) -> Boolean; +pub type eglTerminate = unsafe extern "system" fn(display: EGLDisplay) -> Boolean; pub const NONE: Int = 0x3038; pub const ENUM_NONE: Enum = 0x3038; @@ -50,6 +55,9 @@ pub struct Functions { pub eglBindAPI: eglBindAPI, pub eglQueryAPI: eglQueryAPI, pub eglQueryString: eglQueryString, + pub eglGetDisplay: eglGetDisplay, + pub eglInitialize: eglInitialize, + pub eglTerminate: eglTerminate, } impl Functions { @@ -59,6 +67,9 @@ impl Functions { eglBindAPI: Self::get(library, c"eglBindAPI")?, eglQueryAPI: Self::get(library, c"eglQueryAPI")?, eglQueryString: Self::get(library, c"eglQueryString")?, + eglGetDisplay: Self::get(library, c"eglGetDisplay")?, + eglInitialize: Self::get(library, c"eglInitialize")?, + eglTerminate: Self::get(library, c"eglTerminate")?, }) } diff --git a/src/wrappers/glx.rs b/src/wrappers/glx.rs index 07cbc709..c7ff51be 100644 --- a/src/wrappers/glx.rs +++ b/src/wrappers/glx.rs @@ -6,6 +6,8 @@ use crate::platform::*; use std::ffi::{c_ulong, c_void, CStr}; use std::os::raw::c_int; use std::ptr::NonNull; +use std::rc::Rc; +use std::sync::Arc; use x11_dl::glx::{arb::*, *}; use x11_dl::xlib; use x11_dl::xlib::XVisualInfo; @@ -22,13 +24,14 @@ type GlXCreateContextAttribsARB = unsafe extern "C" fn( /// See https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_framebuffer_sRGB.txt. const GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB: i32 = 0x20B2; +#[derive(Clone)] pub struct Glx { - inner: x11_dl::glx::Glx, + inner: Rc, } impl Glx { pub fn open() -> Result { - Ok(Self { inner: x11_dl::glx::Glx::open()? }) + Ok(Self { inner: Rc::new(x11_dl::glx::Glx::open()?) }) } fn get_fb_attribs(config: &GlConfig) -> [c_int; 29] { From 838bd616bfc13e07433abcf43c01e2f6073da689 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 23 Aug 2026 12:03:26 +0200 Subject: [PATCH 04/11] wip --- src/gl.rs | 2 +- src/platform/x11/gl.rs | 122 ++++++++++++------------------------ src/platform/x11/gl/egl.rs | 53 ++++++++++++++++ src/platform/x11/gl/glx.rs | 107 +++++++++++++++++++++++++++++++ src/wrappers/egl.rs | 3 + src/wrappers/egl/config.rs | 87 +++++++++++++++++++++++++ src/wrappers/egl/display.rs | 10 ++- src/wrappers/egl/sys.rs | 35 +++++++++++ src/wrappers/glx.rs | 1 - 9 files changed, 335 insertions(+), 85 deletions(-) create mode 100644 src/platform/x11/gl/egl.rs create mode 100644 src/platform/x11/gl/glx.rs create mode 100644 src/wrappers/egl/config.rs diff --git a/src/gl.rs b/src/gl.rs index 5acae154..072d4a7d 100644 --- a/src/gl.rs +++ b/src/gl.rs @@ -1,7 +1,7 @@ use std::ffi::{c_void, CStr, CString}; use std::marker::PhantomData; -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub struct GlConfig { pub version: (u8, u8), pub profile: Profile, diff --git a/src/platform/x11/gl.rs b/src/platform/x11/gl.rs index f05efa2c..78930224 100644 --- a/src/platform/x11/gl.rs +++ b/src/platform/x11/gl.rs @@ -1,15 +1,20 @@ use super::*; use crate::gl::*; use crate::wrappers::glx::*; -use crate::wrappers::xlib::{XErrorHandler, XLibError}; +use crate::wrappers::xlib::XLibError; use std::error::Error; +use crate::platform::gl::egl::EglGlContext; +use crate::platform::gl::glx::GlxGlContext; use crate::platform::x11::xcb_window::XcbWindow; -use crate::wrappers::egl::{Egl, MissingSymbolError}; -use std::ffi::{c_ulong, c_void, CStr}; +use crate::wrappers::egl::{EglConfig, EglDisplay, MissingSymbolError}; +use khronos_egl::EGLDisplay; +use std::ffi::{c_void, CStr}; use std::rc::Rc; use x11_dl::error::OpenError; -use x11_dl::glx::GLXContext; + +mod egl; +mod glx; #[derive(Debug)] pub enum CreationFailedError { @@ -48,23 +53,21 @@ impl Display for CreationFailedError { pub type GlContext = Rc; -pub struct GlContextInner { - glx: Glx, - window: NonZeroU32, - connection: Rc, - context: GLXContext, +pub enum GlContextInner { + Glx(GlxGlContext), + Egl(EglGlContext), } /// The frame buffer configuration along with the general OpenGL configuration to somewhat minimize /// misuse. pub struct FbConfig { gl_config: GlConfig, - fb_config: GlxFbConfig, + fb_config: FbConfigInner, } enum FbConfigInner { Glx { glx: Glx, config: GlxFbConfig }, - Egl { egl: Egl }, + Egl { display: EglDisplay, config: EglConfig }, } /// The configuration a window should be created with after calling @@ -84,31 +87,23 @@ impl GlContextInner { /// /// Use [Self::get_fb_config_and_visual] to create both of these things. pub fn create( - window: &XcbWindow, connection: Rc, config: FbConfig, + window: &XcbWindow, connection: Rc, fb_config: FbConfig, ) -> Result> { - 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 inner = + match fb_config.fb_config { + FbConfigInner::Glx { glx, config } => GlContextInner::Glx(GlxGlContext::create( + window, + connection, + fb_config.gl_config, + config, + glx, + )?), + FbConfigInner::Egl { display, config } => GlContextInner::Egl( + EglGlContext::create(window, connection, fb_config.gl_config, config, display)?, + ), }; - let context = create_context.call( - xlib_connection, - &config.gl_config, - config.fb_config, - error_handler, - )?; - - Ok(Rc::new(GlContextInner { - glx, - window: window.id(), - connection: Rc::clone(&connection), - context, - })) - }) + Ok(Rc::new(inner)) } /// Find a matching framebuffer config and window visual for the given OpenGL configuration. @@ -117,66 +112,31 @@ impl GlContextInner { 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)?; - - // Now that we have a matching framebuffer config, we need to know which visual matches - // this config so the window is compatible with the OpenGL context we're about to create - 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 }, - )) - }) + EglGlContext::get_fb_config_and_visual(connection, &config) + .or_else(|_| GlxGlContext::get_fb_config_and_visual(connection, &config)) } 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, - ) - }) + match self { + GlContextInner::Glx(glx) => glx.make_current(), + } } 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() + match self { + GlContextInner::Glx(glx) => glx.make_not_current(), + } } 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(), + match self { + GlContextInner::Glx(glx) => glx.get_proc_address(symbol), } } 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) } + match self { + GlContextInner::Glx(glx) => glx.swap_buffers(), + } } } diff --git a/src/platform/x11/gl/egl.rs b/src/platform/x11/gl/egl.rs new file mode 100644 index 00000000..c70ce8a0 --- /dev/null +++ b/src/platform/x11/gl/egl.rs @@ -0,0 +1,53 @@ +use crate::gl::GlConfig; +use crate::platform::gl::{FbConfig, FbConfigInner, WindowConfig}; +use crate::platform::x11::xcb_window::XcbWindow; +use crate::platform::{PlatformError, X11Connection}; +use crate::wrappers::egl::{Egl, EglConfig, EglDisplay}; +use khronos_egl::EGLDisplay; +use std::num::NonZeroU32; +use std::rc::Rc; +use x11rb::protocol::xproto::Visualid; + +pub struct EglGlContext { + display: EGLDisplay, + window: NonZeroU32, + connection: Rc, +} + +impl EglGlContext { + pub(crate) fn create( + window: &XcbWindow, connection: Rc, gl_config: GlConfig, + egl_config: EglConfig, display: EglDisplay, + ) -> Result { + todo!() + } +} + +impl EglGlContext { + pub fn get_fb_config_and_visual( + connection: &X11Connection, gl_config: &GlConfig, + ) -> Result<(FbConfig, WindowConfig), PlatformError> { + let egl = Egl::open()?; + let display = egl.create_display(&connection.conn)?; + + let config = display.choose_config(&gl_config)?.unwrap(); + let visual = config.get_visual_id(&display)?; + + let depth = Self::find_visual_depth_for_id(&connection, visual).unwrap(); // TODO + + let window_config = WindowConfig { depth, visual }; + let fb_config = + FbConfig { gl_config: *gl_config, fb_config: FbConfigInner::Egl { display, config } }; + + Ok((fb_config, window_config)) + } + + fn find_visual_depth_for_id(connection: &X11Connection, visual_id: Visualid) -> Option { + connection + .default_screen() + .allowed_depths + .iter() + .find(|d| d.visuals.iter().any(|v| v.visual_id == visual_id)) + .map(|d| d.depth) + } +} diff --git a/src/platform/x11/gl/glx.rs b/src/platform/x11/gl/glx.rs new file mode 100644 index 00000000..19fcc22a --- /dev/null +++ b/src/platform/x11/gl/glx.rs @@ -0,0 +1,107 @@ +use super::*; +use crate::gl::GlConfig; +use crate::platform::gl::CreationFailedError; +use crate::platform::x11::xcb_window::XcbWindow; +use crate::platform::X11Connection; +use crate::wrappers::glx::{Glx, GlxFbConfig}; +use crate::wrappers::xlib::XErrorHandler; +use std::ffi::{c_ulong, c_void, CStr}; +use std::num::NonZeroU32; +use std::rc::Rc; +use x11_dl::glx::GLXContext; + +pub struct GlxGlContext { + glx: Glx, + window: NonZeroU32, + connection: Rc, + context: GLXContext, +} + +impl GlxGlContext { + pub fn create( + window: &XcbWindow, connection: Rc, gl_config: GlConfig, + fb_config: GlxFbConfig, glx: Glx, + ) -> Result { + 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 context = + create_context.call(xlib_connection, &gl_config, fb_config, error_handler)?; + + Ok(Self { glx, window: window.id(), connection: Rc::clone(&connection), 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)?; + + // Now that we have a matching framebuffer config, we need to know which visual matches + // this config so the window is compatible with the OpenGL context we're about to create + let visual = + glx.get_visual_from_fb_config(xlib_connection, fb_config, error_handler)?; + + Ok(( + FbConfig { + fb_config: FbConfigInner::Glx { config: fb_config, glx }, + 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 GlxGlContext { + fn drop(&mut self) { + unsafe { self.glx.destroy_context(self.connection.conn.xlib_connection(), self.context) } + } +} diff --git a/src/wrappers/egl.rs b/src/wrappers/egl.rs index 3f7ce0b6..1209a6cd 100644 --- a/src/wrappers/egl.rs +++ b/src/wrappers/egl.rs @@ -3,6 +3,7 @@ use libloading::Library; use std::rc::Rc; mod bound_api; +mod config; mod display; mod error; mod extensions; @@ -12,6 +13,8 @@ use bound_api::BoundApi; use sys::Functions; use crate::wrappers::egl::extensions::Extensions; +pub use config::EglConfig; +pub use display::EglDisplay; pub use error::EglError; pub use sys::MissingSymbolError; diff --git a/src/wrappers/egl/config.rs b/src/wrappers/egl/config.rs new file mode 100644 index 00000000..e784bd69 --- /dev/null +++ b/src/wrappers/egl/config.rs @@ -0,0 +1,87 @@ +use super::*; +use crate::gl::GlConfig; +use crate::wrappers::egl::display::EglDisplay; +use crate::wrappers::egl::sys::*; +use std::ffi::c_void; +use std::ptr::NonNull; +use x11rb::protocol::xproto::Visualid; + +pub struct EglConfig(pub(super) NonNull); + +impl EglConfig { + pub(super) fn choose_config( + gl_config: &GlConfig, display: &EglDisplay, + ) -> Result, EglError> { + let mut config = core::ptr::null_mut(); + let fb_attribs = get_fb_attribs(gl_config); + let mut num_configs = 0; + let result = unsafe { + (display.egl.inner.functions.eglChooseConfig)( + display.raw.as_ptr(), + fb_attribs.as_ptr(), + &mut config, + 1, + &mut num_configs, + ) + }; + + if result == FALSE { + return Err(EglError::from_last_error(&display.egl)); + } + + if num_configs == 0 { + return Ok(None); + } + + let Some(raw) = NonNull::new(config) else { return Ok(None) }; + + Ok(Some(EglConfig(raw))) + } + + fn get_attrib(&self, display: &EglDisplay, attrib: Int) -> Result { + let mut value = 0; + let result = unsafe { + (display.egl.inner.functions.eglGetConfigAttrib)( + display.raw.as_ptr(), + self.0.as_ptr(), + attrib, + &mut value, + ) + }; + + if result == FALSE { + return Err(EglError::from_last_error(&display.egl)); + } + + Ok(value) + } + + pub fn get_visual_id(&self, display: &EglDisplay) -> Result { + let value = self.get_attrib(display, EGL_NATIVE_VISUAL_ID)?; + Ok(value as _) // TODO: cast + } +} + +fn get_fb_attribs(config: &GlConfig) -> [Int; 17] { + let Some(color_size) = (config.red_bits as i32) + .checked_add(config.blue_bits as i32) + .and_then(|c| c.checked_add(config.green_bits as i32)) + else { + panic!("Overflow when computing color size") + }; + + #[rustfmt::skip] + let fb_attribs = [ + EGL_BUFFER_SIZE, color_size, + EGL_RED_SIZE, config.red_bits.into(), + EGL_GREEN_SIZE, config.green_bits.into(), + EGL_BLUE_SIZE, config.blue_bits.into(), + EGL_ALPHA_SIZE, config.alpha_bits.into(), + EGL_DEPTH_SIZE, config.depth_bits.into(), + EGL_STENCIL_SIZE, config.stencil_bits.into(), + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + ]; + + fb_attribs +} diff --git a/src/wrappers/egl/display.rs b/src/wrappers/egl/display.rs index d46f0288..00c467bc 100644 --- a/src/wrappers/egl/display.rs +++ b/src/wrappers/egl/display.rs @@ -1,11 +1,13 @@ +use crate::gl::GlConfig; +use crate::wrappers::egl::config::EglConfig; use crate::wrappers::egl::{sys, Egl, EglError}; use crate::wrappers::xlib::{XlibConnection, XlibXcbConnection}; use std::ffi::c_void; use std::ptr::NonNull; pub struct EglDisplay { - egl: Egl, - raw: NonNull, + pub egl: Egl, + pub(super) raw: NonNull, } impl EglDisplay { @@ -15,6 +17,10 @@ impl EglDisplay { unsafe { egl.initialize_display(display)? }; Ok(Self { egl: egl.clone(), raw: display }) } + + pub fn choose_config(&self, config: &GlConfig) -> Result, EglError> { + EglConfig::choose_config(config, self) + } } impl Drop for EglDisplay { diff --git a/src/wrappers/egl/sys.rs b/src/wrappers/egl/sys.rs index 4dbfd31b..b1739e79 100644 --- a/src/wrappers/egl/sys.rs +++ b/src/wrappers/egl/sys.rs @@ -10,6 +10,7 @@ pub type Boolean = c_uint; pub type Int = c_int; pub type EGLDisplay = *mut c_void; pub type NativeDisplayType = *mut c_void; +pub type EGLConfig = *mut c_void; pub type eglGetError = unsafe extern "system" fn() -> c_int; pub type eglBindAPI = unsafe extern "system" fn(Enum) -> Boolean; @@ -19,6 +20,20 @@ pub type eglGetDisplay = unsafe extern "system" fn(NativeDisplayType) -> EGLDisp pub type eglInitialize = unsafe extern "system" fn(display: EGLDisplay, major: *mut Int, minor: *mut Int) -> Boolean; pub type eglTerminate = unsafe extern "system" fn(display: EGLDisplay) -> Boolean; +pub type eglChooseConfig = unsafe extern "system" fn( + display: EGLDisplay, + attrib_list: *const Int, + configs: *mut EGLConfig, + config_size: Int, + num_config: *mut Int, +) -> Boolean; + +pub type eglGetConfigAttrib = unsafe extern "system" fn( + display: EGLDisplay, + config: EGLConfig, + attribute: Int, + value: *mut Int, +) -> Boolean; pub const NONE: Int = 0x3038; pub const ENUM_NONE: Enum = 0x3038; @@ -27,6 +42,22 @@ pub const FALSE: Boolean = 0; pub const NO_DISPLAY: EGLDisplay = 0 as EGLDisplay; pub const EXTENSIONS: Int = 0x3055; +pub const EGL_SURFACE_TYPE: Int = 0x3033; +pub const EGL_WINDOW_BIT: Int = 0x0004; + +pub const EGL_OPENGL_BIT: Int = 0x30A4; + +pub const EGL_NONE: Int = 0x3038; +pub const EGL_BUFFER_SIZE: Int = 0x3020; +pub const EGL_RED_SIZE: Int = 0x3024; +pub const EGL_GREEN_SIZE: Int = 0x3023; +pub const EGL_BLUE_SIZE: Int = 0x3022; +pub const EGL_ALPHA_SIZE: Int = 0x3021; +pub const EGL_DEPTH_SIZE: Int = 0x3025; +pub const EGL_STENCIL_SIZE: Int = 0x3026; + +pub const EGL_NATIVE_VISUAL_ID: Int = 0x302E; + #[derive(Debug, Copy, Clone)] pub struct MissingSymbolError { name: &'static CStr, @@ -58,6 +89,8 @@ pub struct Functions { pub eglGetDisplay: eglGetDisplay, pub eglInitialize: eglInitialize, pub eglTerminate: eglTerminate, + pub eglChooseConfig: eglChooseConfig, + pub eglGetConfigAttrib: eglGetConfigAttrib, } impl Functions { @@ -70,6 +103,8 @@ impl Functions { eglGetDisplay: Self::get(library, c"eglGetDisplay")?, eglInitialize: Self::get(library, c"eglInitialize")?, eglTerminate: Self::get(library, c"eglTerminate")?, + eglChooseConfig: Self::get(library, c"eglChooseConfig")?, + eglGetConfigAttrib: Self::get(library, c"eglGetConfigAttrib")?, }) } diff --git a/src/wrappers/glx.rs b/src/wrappers/glx.rs index c7ff51be..60f3ed88 100644 --- a/src/wrappers/glx.rs +++ b/src/wrappers/glx.rs @@ -7,7 +7,6 @@ use std::ffi::{c_ulong, c_void, CStr}; use std::os::raw::c_int; use std::ptr::NonNull; use std::rc::Rc; -use std::sync::Arc; use x11_dl::glx::{arb::*, *}; use x11_dl::xlib; use x11_dl::xlib::XVisualInfo; From 36d077713401ac52e857d3dd780876ec47eb3d85 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 23 Aug 2026 14:03:22 +0200 Subject: [PATCH 05/11] wip that works! --- src/platform/x11/gl.rs | 11 ++-- src/platform/x11/gl/egl.rs | 49 +++++++++++---- src/platform/x11/gl/glx.rs | 6 +- src/platform/x11/visual_info.rs | 3 +- src/platform/x11/window_shared.rs | 14 ++--- src/wrappers/egl.rs | 11 +++- src/wrappers/egl/bound_api.rs | 5 +- src/wrappers/egl/config.rs | 13 ++-- src/wrappers/egl/context.rs | 101 ++++++++++++++++++++++++++++++ src/wrappers/egl/display.rs | 52 ++++++++++++--- src/wrappers/egl/surface.rs | 81 ++++++++++++++++++++++++ src/wrappers/egl/sys.rs | 55 ++++++++++++++++ 12 files changed, 352 insertions(+), 49 deletions(-) create mode 100644 src/wrappers/egl/context.rs create mode 100644 src/wrappers/egl/surface.rs diff --git a/src/platform/x11/gl.rs b/src/platform/x11/gl.rs index 78930224..534bd699 100644 --- a/src/platform/x11/gl.rs +++ b/src/platform/x11/gl.rs @@ -8,7 +8,6 @@ use crate::platform::gl::egl::EglGlContext; use crate::platform::gl::glx::GlxGlContext; use crate::platform::x11::xcb_window::XcbWindow; use crate::wrappers::egl::{EglConfig, EglDisplay, MissingSymbolError}; -use khronos_egl::EGLDisplay; use std::ffi::{c_void, CStr}; use std::rc::Rc; use x11_dl::error::OpenError; @@ -87,7 +86,7 @@ impl GlContextInner { /// /// Use [Self::get_fb_config_and_visual] to create both of these things. pub fn create( - window: &XcbWindow, connection: Rc, fb_config: FbConfig, + window: &XcbWindow, connection: &Rc, fb_config: FbConfig, ) -> Result> { let inner = match fb_config.fb_config { @@ -99,7 +98,7 @@ impl GlContextInner { glx, )?), FbConfigInner::Egl { display, config } => GlContextInner::Egl( - EglGlContext::create(window, connection, fb_config.gl_config, config, display)?, + EglGlContext::create(window, &fb_config.gl_config, config, display)?, ), }; @@ -110,7 +109,7 @@ impl GlContextInner { /// This needs to be passed to [Self::create] along with a handle to a window that was created /// using the visual also returned from this function. pub fn get_fb_config_and_visual( - connection: &X11Connection, config: GlConfig, + connection: &Rc, config: GlConfig, ) -> Result<(FbConfig, WindowConfig)> { EglGlContext::get_fb_config_and_visual(connection, &config) .or_else(|_| GlxGlContext::get_fb_config_and_visual(connection, &config)) @@ -119,24 +118,28 @@ impl GlContextInner { pub unsafe fn make_current(&self) -> Result<()> { match self { GlContextInner::Glx(glx) => glx.make_current(), + GlContextInner::Egl(egl) => egl.make_current(), } } pub unsafe fn make_not_current(&self) -> Result<()> { match self { GlContextInner::Glx(glx) => glx.make_not_current(), + GlContextInner::Egl(egl) => egl.make_not_current(), } } pub fn get_proc_address(&self, symbol: &CStr) -> *const c_void { match self { GlContextInner::Glx(glx) => glx.get_proc_address(symbol), + GlContextInner::Egl(egl) => egl.get_proc_address(symbol), } } pub fn swap_buffers(&self) -> Result<()> { match self { GlContextInner::Glx(glx) => glx.swap_buffers(), + GlContextInner::Egl(egl) => egl.swap_buffers(), } } } diff --git a/src/platform/x11/gl/egl.rs b/src/platform/x11/gl/egl.rs index c70ce8a0..bf6db9ad 100644 --- a/src/platform/x11/gl/egl.rs +++ b/src/platform/x11/gl/egl.rs @@ -2,38 +2,40 @@ use crate::gl::GlConfig; use crate::platform::gl::{FbConfig, FbConfigInner, WindowConfig}; use crate::platform::x11::xcb_window::XcbWindow; use crate::platform::{PlatformError, X11Connection}; -use crate::wrappers::egl::{Egl, EglConfig, EglDisplay}; -use khronos_egl::EGLDisplay; -use std::num::NonZeroU32; +use crate::wrappers::egl::{Egl, EglConfig, EglContext, EglDisplay, EglSurface}; +use std::ffi::{c_void, CStr}; use std::rc::Rc; use x11rb::protocol::xproto::Visualid; pub struct EglGlContext { - display: EGLDisplay, - window: NonZeroU32, - connection: Rc, + surface: EglSurface, + context: EglContext, } impl EglGlContext { pub(crate) fn create( - window: &XcbWindow, connection: Rc, gl_config: GlConfig, - egl_config: EglConfig, display: EglDisplay, + window: &XcbWindow, gl_config: &GlConfig, egl_config: EglConfig, display: EglDisplay, ) -> Result { - todo!() + let surface = display.create_surface(egl_config, window.id().get(), gl_config)?; + let context = display + .egl() + .with_opengl(|bound| display.create_context(egl_config, bound, gl_config))??; + + Ok(Self { surface, context }) } } impl EglGlContext { pub fn get_fb_config_and_visual( - connection: &X11Connection, gl_config: &GlConfig, + connection: &Rc, gl_config: &GlConfig, ) -> Result<(FbConfig, WindowConfig), PlatformError> { let egl = Egl::open()?; - let display = egl.create_display(&connection.conn)?; + let display = egl.create_display(connection)?; // TODO: check EGL version - let config = display.choose_config(&gl_config)?.unwrap(); + let config = display.choose_config(gl_config)?.unwrap(); let visual = config.get_visual_id(&display)?; - let depth = Self::find_visual_depth_for_id(&connection, visual).unwrap(); // TODO + let depth = Self::find_visual_depth_for_id(connection, visual).unwrap(); // TODO let window_config = WindowConfig { depth, visual }; let fb_config = @@ -50,4 +52,25 @@ impl EglGlContext { .find(|d| d.visuals.iter().any(|v| v.visual_id == visual_id)) .map(|d| d.depth) } + + pub fn make_current(&self) -> Result<(), PlatformError> { + self.context.make_current(&self.surface)?; + + Ok(()) + } + + pub fn make_not_current(&self) -> Result<(), PlatformError> { + self.surface.display().egl().with_opengl(|gl| self.context.make_not_current(gl))??; + + Ok(()) + } + + pub fn get_proc_address(&self, symbol: &CStr) -> *const c_void { + self.surface.display().egl().get_proc_address(symbol) + } + + pub fn swap_buffers(&self) -> Result<(), PlatformError> { + self.surface.swap_buffers()?; + Ok(()) + } } diff --git a/src/platform/x11/gl/glx.rs b/src/platform/x11/gl/glx.rs index 19fcc22a..6eb68b38 100644 --- a/src/platform/x11/gl/glx.rs +++ b/src/platform/x11/gl/glx.rs @@ -19,7 +19,7 @@ pub struct GlxGlContext { impl GlxGlContext { pub fn create( - window: &XcbWindow, connection: Rc, gl_config: GlConfig, + window: &XcbWindow, connection: &Rc, gl_config: GlConfig, fb_config: GlxFbConfig, glx: Glx, ) -> Result { let xlib_connection = connection.conn.xlib_connection(); @@ -32,7 +32,7 @@ impl GlxGlContext { let context = create_context.call(xlib_connection, &gl_config, fb_config, error_handler)?; - Ok(Self { glx, window: window.id(), connection: Rc::clone(&connection), context }) + Ok(Self { glx, window: window.id(), connection: Rc::clone(connection), context }) }) } @@ -44,7 +44,7 @@ impl GlxGlContext { 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 fb_config = glx.choose_best_fb_config(xlib_connection, config, error_handler)?; // Now that we have a matching framebuffer config, we need to know which visual matches // this config so the window is compatible with the OpenGL context we're about to create diff --git a/src/platform/x11/visual_info.rs b/src/platform/x11/visual_info.rs index 2682d63d..0f3628db 100644 --- a/src/platform/x11/visual_info.rs +++ b/src/platform/x11/visual_info.rs @@ -1,5 +1,6 @@ use super::xcb_connection::X11Connection; use crate::platform::*; +use std::rc::Rc; use x11rb::connection::Connection; use x11rb::protocol::xproto::{ Colormap, ColormapAlloc, ConnectionExt, Screen, VisualClass, Visualid, @@ -19,7 +20,7 @@ pub(crate) struct WindowVisualConfig { impl WindowVisualConfig { #[cfg(feature = "opengl")] pub fn find_best_visual_config_for_gl( - connection: &X11Connection, gl_config: Option, + connection: &Rc, gl_config: Option, ) -> Result { let Some(gl_config) = gl_config else { return Self::find_best_visual_config(connection) }; diff --git a/src/platform/x11/window_shared.rs b/src/platform/x11/window_shared.rs index 203f911d..f3938ff2 100644 --- a/src/platform/x11/window_shared.rs +++ b/src/platform/x11/window_shared.rs @@ -85,14 +85,14 @@ impl WindowInner { let size_hints = get_size_hints(&sizing_strategy, physical_size, initial_scale_factor); + let connection = Rc::new(xcb_connection); + #[cfg(feature = "opengl")] let visual_info = - WindowVisualConfig::find_best_visual_config_for_gl(&xcb_connection, options.gl_config)?; + WindowVisualConfig::find_best_visual_config_for_gl(&connection, options.gl_config)?; #[cfg(not(feature = "opengl"))] - let visual_info = WindowVisualConfig::find_best_visual_config(&xcb_connection)?; - - let connection = Rc::new(xcb_connection); + let visual_info = WindowVisualConfig::find_best_visual_config(&connection)?; let will_have_parent = options.parent.is_some() || options.wait_for_parent; @@ -126,11 +126,7 @@ impl WindowInner { None => None, Some(fb_config) => { // Because of the visual negotation we had to take some extra steps to create this context - Some(super::gl::GlContextInner::create( - &xcb_window, - Rc::clone(&connection), - fb_config, - )?) + Some(super::gl::GlContextInner::create(&xcb_window, &connection, fb_config)?) } }; diff --git a/src/wrappers/egl.rs b/src/wrappers/egl.rs index 1209a6cd..cd82bcf4 100644 --- a/src/wrappers/egl.rs +++ b/src/wrappers/egl.rs @@ -1,21 +1,26 @@ use crate::platform::gl::CreationFailedError; use libloading::Library; +use std::ffi::{c_void, CStr}; use std::rc::Rc; mod bound_api; mod config; +mod context; mod display; mod error; mod extensions; +mod surface; mod sys; -use bound_api::BoundApi; use sys::Functions; use crate::wrappers::egl::extensions::Extensions; +pub use bound_api::BoundApi; pub use config::EglConfig; +pub use context::EglContext; pub use display::EglDisplay; pub use error::EglError; +pub use surface::EglSurface; pub use sys::MissingSymbolError; struct EglInner { @@ -46,4 +51,8 @@ impl Egl { pub fn query_client_extensions(&self) -> Extensions { Extensions::new(self) } + + pub fn get_proc_address(&self, proc_name: &CStr) -> *const c_void { + unsafe { (self.inner.functions.eglGetProcAddress)(proc_name.as_ptr()) } + } } diff --git a/src/wrappers/egl/bound_api.rs b/src/wrappers/egl/bound_api.rs index 31dc90a7..e5611262 100644 --- a/src/wrappers/egl/bound_api.rs +++ b/src/wrappers/egl/bound_api.rs @@ -8,10 +8,7 @@ pub struct BoundApi { impl BoundApi { pub(super) fn new(egl: &Egl) -> Result { let previous_api = egl.query_api(); - let result = unsafe { (egl.inner.functions.eglBindAPI)(sys::OPENGL_API) }; - if result == sys::FALSE { - return Err(EglError::from_last_error(egl)); - } + egl.bind_api(sys::OPENGL_API)?; Ok(Self { previous_api, egl: egl.clone() }) } diff --git a/src/wrappers/egl/config.rs b/src/wrappers/egl/config.rs index e784bd69..916188e3 100644 --- a/src/wrappers/egl/config.rs +++ b/src/wrappers/egl/config.rs @@ -6,6 +6,7 @@ use std::ffi::c_void; use std::ptr::NonNull; use x11rb::protocol::xproto::Visualid; +#[derive(Copy, Clone)] pub struct EglConfig(pub(super) NonNull); impl EglConfig { @@ -16,8 +17,8 @@ impl EglConfig { let fb_attribs = get_fb_attribs(gl_config); let mut num_configs = 0; let result = unsafe { - (display.egl.inner.functions.eglChooseConfig)( - display.raw.as_ptr(), + (display.egl().inner.functions.eglChooseConfig)( + display.as_raw(), fb_attribs.as_ptr(), &mut config, 1, @@ -26,7 +27,7 @@ impl EglConfig { }; if result == FALSE { - return Err(EglError::from_last_error(&display.egl)); + return Err(EglError::from_last_error(display.egl())); } if num_configs == 0 { @@ -41,8 +42,8 @@ impl EglConfig { fn get_attrib(&self, display: &EglDisplay, attrib: Int) -> Result { let mut value = 0; let result = unsafe { - (display.egl.inner.functions.eglGetConfigAttrib)( - display.raw.as_ptr(), + (display.egl().inner.functions.eglGetConfigAttrib)( + display.as_raw(), self.0.as_ptr(), attrib, &mut value, @@ -50,7 +51,7 @@ impl EglConfig { }; if result == FALSE { - return Err(EglError::from_last_error(&display.egl)); + return Err(EglError::from_last_error(display.egl())); } Ok(value) diff --git a/src/wrappers/egl/context.rs b/src/wrappers/egl/context.rs new file mode 100644 index 00000000..a87fe0d3 --- /dev/null +++ b/src/wrappers/egl/context.rs @@ -0,0 +1,101 @@ +use super::*; +use crate::gl::{GlConfig, Profile}; +use crate::wrappers::egl::sys::*; +use std::ptr::NonNull; + +pub struct EglContext { + display: EglDisplay, + raw: NonNull, +} + +impl EglContext { + pub(super) fn create( + display: &EglDisplay, config: EglConfig, gl_config: &GlConfig, + ) -> Result { + let raw = display.egl().create_context(display, config, gl_config)?; + + Ok(Self { display: display.clone(), raw }) + } + + pub fn make_current(&self, surface: &EglSurface) -> Result<(), EglError> { + self.display.egl().make_current(surface, self) + } + + pub fn make_not_current(&self, _bound_api: &BoundApi) -> Result<(), EglError> { + self.display.egl().make_not_current(self) + } +} + +impl Egl { + fn get_context_attribs(gl_config: &GlConfig) -> [Int; 7] { + let profile_mask = match gl_config.profile { + Profile::Core => EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + Profile::Compatibility => EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, + }; + + #[rustfmt::skip] + let fb_attribs = [ + EGL_CONTEXT_MAJOR_VERSION, gl_config.version.0.into(), + EGL_CONTEXT_MINOR_VERSION, gl_config.version.1.into(), + EGL_CONTEXT_OPENGL_PROFILE_MASK, profile_mask, + EGL_NONE, + ]; + + fb_attribs + } + + fn create_context( + &self, display: &EglDisplay, config: EglConfig, gl_config: &GlConfig, + ) -> Result, EglError> { + let attribs = Self::get_context_attribs(gl_config); + let result = unsafe { + (self.inner.functions.eglCreateContext)( + display.as_raw(), + config.0.as_ptr(), + core::ptr::null_mut(), + attribs.as_ptr(), + ) + }; + + NonNull::new(result).ok_or_else(|| EglError::from_last_error(display.egl())) + } + + fn make_current(&self, surface: &EglSurface, context: &EglContext) -> Result<(), EglError> { + let result = unsafe { + (self.inner.functions.eglMakeCurrent)( + surface.display().as_raw(), + surface.as_raw(), + surface.as_raw(), + context.raw.as_ptr(), + ) + }; + + if result == FALSE { + Err(EglError::from_last_error(self)) + } else { + Ok(()) + } + } + + fn make_not_current(&self, context: &EglContext) -> Result<(), EglError> { + let current_context = unsafe { (self.inner.functions.eglGetCurrentContext)() }; + if current_context != context.raw.as_ptr() { + return Ok(()); + } + + let result = unsafe { + (self.inner.functions.eglMakeCurrent)( + context.display.as_raw(), + core::ptr::null_mut(), + core::ptr::null_mut(), + core::ptr::null_mut(), + ) + }; + + if result == FALSE { + Err(EglError::from_last_error(self)) + } else { + Ok(()) + } + } +} diff --git a/src/wrappers/egl/display.rs b/src/wrappers/egl/display.rs index 00c467bc..1de06e45 100644 --- a/src/wrappers/egl/display.rs +++ b/src/wrappers/egl/display.rs @@ -1,29 +1,65 @@ use crate::gl::GlConfig; +use crate::platform::X11Connection; +use crate::wrappers::egl::bound_api::BoundApi; use crate::wrappers::egl::config::EglConfig; -use crate::wrappers::egl::{sys, Egl, EglError}; +use crate::wrappers::egl::context::EglContext; +use crate::wrappers::egl::surface::EglSurface; +use crate::wrappers::egl::sys::EGLContext; +use crate::wrappers::egl::{sys, Egl, EglError, EglInner}; use crate::wrappers::xlib::{XlibConnection, XlibXcbConnection}; use std::ffi::c_void; use std::ptr::NonNull; +use std::rc::Rc; +use x11rb::protocol::xproto::Window; + +struct EglDisplayInner { + egl: Egl, + raw: NonNull, + // Kept to ensure the connection isn't dropped as long as the EGL display is alive + _connection: Rc, +} +#[derive(Clone)] pub struct EglDisplay { - pub egl: Egl, - pub(super) raw: NonNull, + inner: Rc, } impl EglDisplay { - pub(super) fn new(egl: &Egl, connection: &XlibXcbConnection) -> Result { - let display = egl.create_display_basic(connection.xlib_connection()).unwrap(); + pub(super) fn new(egl: &Egl, connection: &Rc) -> Result { + let display = egl.create_display_basic(connection.conn.xlib_connection()).unwrap(); + let egl = egl.clone(); unsafe { egl.initialize_display(display)? }; - Ok(Self { egl: egl.clone(), raw: display }) + let inner = EglDisplayInner { egl, raw: display, _connection: Rc::clone(connection) }; + Ok(Self { inner: Rc::new(inner) }) } pub fn choose_config(&self, config: &GlConfig) -> Result, EglError> { EglConfig::choose_config(config, self) } + + pub fn egl(&self) -> &Egl { + &self.inner.egl + } + + pub fn as_raw(&self) -> sys::EGLDisplay { + self.inner.raw.as_ptr() + } + + pub fn create_surface( + &self, config: EglConfig, window: Window, gl_config: &GlConfig, + ) -> Result { + EglSurface::create(self, config, window, gl_config) + } + + pub fn create_context( + &self, config: EglConfig, _bound: &BoundApi, gl_config: &GlConfig, + ) -> Result { + EglContext::create(self, config, gl_config) + } } -impl Drop for EglDisplay { +impl Drop for EglDisplayInner { fn drop(&mut self) { if let Err(e) = unsafe { self.egl.terminate_display(self.raw) } { crate::warn!("Failed to terminate EGL display connection: {}", e) @@ -37,7 +73,7 @@ struct EglVersion { } impl Egl { - pub fn create_display(&self, connection: &XlibXcbConnection) -> Result { + pub fn create_display(&self, connection: &Rc) -> Result { EglDisplay::new(self, connection) } diff --git a/src/wrappers/egl/surface.rs b/src/wrappers/egl/surface.rs new file mode 100644 index 00000000..be8b653c --- /dev/null +++ b/src/wrappers/egl/surface.rs @@ -0,0 +1,81 @@ +use super::sys::*; +use super::*; +use crate::gl::GlConfig; +use std::ffi::c_void; +use std::ptr::NonNull; +use std::rc::Rc; +use x11rb::protocol::xproto::Window; + +struct EglSurfaceInner { + display: EglDisplay, + raw: NonNull, +} + +#[derive(Clone)] +pub struct EglSurface { + inner: Rc, +} + +impl EglSurface { + pub(super) fn create( + display: &EglDisplay, config: EglConfig, window: Window, gl_config: &GlConfig, + ) -> Result { + let raw = display.egl().create_surface(display, config, window, gl_config)?; + let inner = EglSurfaceInner { display: display.clone(), raw }; + + Ok(Self { inner: Rc::new(inner) }) + } + + pub fn display(&self) -> &EglDisplay { + &self.inner.display + } + + pub fn as_raw(&self) -> *mut c_void { + self.inner.raw.as_ptr() + } + + pub fn swap_buffers(&self) -> Result<(), EglError> { + self.display().egl().swap_buffers(self) + } +} + +impl Egl { + fn get_surface_attribs(gl_config: &GlConfig) -> [Int; 3] { + #[rustfmt::skip] + let fb_attribs = [ + EGL_GL_COLORSPACE, if gl_config.srgb { EGL_GL_COLORSPACE_SRGB } else { EGL_GL_COLORSPACE_LINEAR }, + // EGL_RENDER_BUFFER: EGL_BACK_BUFFER is default + EGL_NONE, + ]; + + fb_attribs + } + + fn create_surface( + &self, display: &EglDisplay, config: EglConfig, window: Window, gl_config: &GlConfig, + ) -> Result, EglError> { + let attribs = Self::get_surface_attribs(gl_config); + let result = unsafe { + (self.inner.functions.eglCreateWindowSurface)( + display.as_raw(), + config.0.as_ptr(), + window as _, + attribs.as_ptr(), + ) + }; + + NonNull::new(result).ok_or_else(|| EglError::from_last_error(self)) + } + + fn swap_buffers(&self, surface: &EglSurface) -> Result<(), EglError> { + let result = unsafe { + (self.inner.functions.eglSwapBuffers)(surface.display().as_raw(), surface.as_raw()) + }; + + if result == FALSE { + Err(EglError::from_last_error(self)) + } else { + Ok(()) + } + } +} diff --git a/src/wrappers/egl/sys.rs b/src/wrappers/egl/sys.rs index b1739e79..fdce7892 100644 --- a/src/wrappers/egl/sys.rs +++ b/src/wrappers/egl/sys.rs @@ -9,8 +9,11 @@ pub type Enum = c_uint; pub type Boolean = c_uint; pub type Int = c_int; pub type EGLDisplay = *mut c_void; +pub type EGLSurface = *mut c_void; pub type NativeDisplayType = *mut c_void; +pub type NativeWindowType = *mut c_void; pub type EGLConfig = *mut c_void; +pub type EGLContext = *mut c_void; pub type eglGetError = unsafe extern "system" fn() -> c_int; pub type eglBindAPI = unsafe extern "system" fn(Enum) -> Boolean; @@ -34,6 +37,32 @@ pub type eglGetConfigAttrib = unsafe extern "system" fn( attribute: Int, value: *mut Int, ) -> Boolean; +pub type eglCreateWindowSurface = unsafe extern "system" fn( + display: EGLDisplay, + config: EGLConfig, + win: NativeWindowType, + attrib_list: *const Int, +) -> EGLSurface; +pub type eglCreateContext = unsafe extern "system" fn( + display: EGLDisplay, + config: EGLConfig, + share_context: EGLContext, + attrib_list: *const Int, +) -> EGLContext; +pub type eglDestroySurface = + unsafe extern "system" fn(display: EGLDisplay, surface: EGLSurface) -> Boolean; +pub type eglDestroyContext = + unsafe extern "system" fn(display: EGLDisplay, context: EGLContext) -> Boolean; +pub type eglMakeCurrent = unsafe extern "system" fn( + display: EGLDisplay, + draw: EGLSurface, + read: EGLSurface, + ctx: EGLContext, +) -> Boolean; +pub type eglGetProcAddress = unsafe extern "system" fn(procname: *const c_char) -> *const c_void; +pub type eglGetCurrentContext = unsafe extern "system" fn() -> EGLContext; +pub type eglSwapBuffers = + unsafe extern "system" fn(display: EGLDisplay, surface: EGLSurface) -> Boolean; pub const NONE: Int = 0x3038; pub const ENUM_NONE: Enum = 0x3038; @@ -56,6 +85,16 @@ pub const EGL_ALPHA_SIZE: Int = 0x3021; pub const EGL_DEPTH_SIZE: Int = 0x3025; pub const EGL_STENCIL_SIZE: Int = 0x3026; +pub const EGL_GL_COLORSPACE: Int = 0x309D; +pub const EGL_GL_COLORSPACE_LINEAR: Int = 0x308A; +pub const EGL_GL_COLORSPACE_SRGB: Int = 0x3089; + +pub const EGL_CONTEXT_MAJOR_VERSION: Int = 0x3098; +pub const EGL_CONTEXT_MINOR_VERSION: Int = 0x30FB; +pub const EGL_CONTEXT_OPENGL_PROFILE_MASK: Int = 0x30FD; +pub const EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT: Int = 0x00000001; +pub const EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT: Int = 0x00000002; + pub const EGL_NATIVE_VISUAL_ID: Int = 0x302E; #[derive(Debug, Copy, Clone)] @@ -91,6 +130,14 @@ pub struct Functions { pub eglTerminate: eglTerminate, pub eglChooseConfig: eglChooseConfig, pub eglGetConfigAttrib: eglGetConfigAttrib, + pub eglCreateWindowSurface: eglCreateWindowSurface, + pub eglDestroySurface: eglDestroySurface, + pub eglCreateContext: eglCreateContext, + pub eglGetProcAddress: eglGetProcAddress, + pub eglMakeCurrent: eglMakeCurrent, + pub eglDestroyContext: eglDestroyContext, + pub eglGetCurrentContext: eglGetCurrentContext, + pub eglSwapBuffers: eglSwapBuffers, } impl Functions { @@ -105,6 +152,14 @@ impl Functions { eglTerminate: Self::get(library, c"eglTerminate")?, eglChooseConfig: Self::get(library, c"eglChooseConfig")?, eglGetConfigAttrib: Self::get(library, c"eglGetConfigAttrib")?, + eglCreateWindowSurface: Self::get(library, c"eglCreateWindowSurface")?, + eglDestroySurface: Self::get(library, c"eglDestroySurface")?, + eglCreateContext: Self::get(library, c"eglCreateContext")?, + eglMakeCurrent: Self::get(library, c"eglMakeCurrent")?, + eglGetProcAddress: Self::get(library, c"eglGetProcAddress")?, + eglDestroyContext: Self::get(library, c"eglDestroyContext")?, + eglGetCurrentContext: Self::get(library, c"eglGetCurrentContext")?, + eglSwapBuffers: Self::get(library, c"eglSwapBuffers")?, }) } From 98adcfb291b233a11138b1a4687f9995df042b10 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 23 Aug 2026 17:26:48 +0200 Subject: [PATCH 06/11] cleanup --- src/platform/x11/gl.rs | 16 ++++++++++++- src/wrappers/egl.rs | 2 +- src/wrappers/egl/context.rs | 20 +++++++++++++++++ src/wrappers/egl/display.rs | 45 +++++++++++++++++++++---------------- src/wrappers/egl/error.rs | 1 + src/wrappers/egl/surface.rs | 35 ++++++++++++++++++++--------- 6 files changed, 87 insertions(+), 32 deletions(-) diff --git a/src/platform/x11/gl.rs b/src/platform/x11/gl.rs index 534bd699..e6b28352 100644 --- a/src/platform/x11/gl.rs +++ b/src/platform/x11/gl.rs @@ -7,7 +7,7 @@ use std::error::Error; use crate::platform::gl::egl::EglGlContext; use crate::platform::gl::glx::GlxGlContext; use crate::platform::x11::xcb_window::XcbWindow; -use crate::wrappers::egl::{EglConfig, EglDisplay, MissingSymbolError}; +use crate::wrappers::egl::{EglConfig, EglDisplay, EglError, EglVersion, MissingSymbolError}; use std::ffi::{c_void, CStr}; use std::rc::Rc; use x11_dl::error::OpenError; @@ -26,6 +26,9 @@ pub enum CreationFailedError { OpenError(OpenError), EGLLoadError(libloading::Error), EGLMissingSymbol(MissingSymbolError), + EglError(EglError), + EglNoDisplay, + EglUnsupportedVersion(EglVersion), } impl Display for CreationFailedError { @@ -46,10 +49,21 @@ impl Display for CreationFailedError { write!(f, "Could not load EGL library: {e}, {:?}", e.source()) } CreationFailedError::EGLMissingSymbol(e) => e.fmt(f), + CreationFailedError::EglError(e) => e.fmt(f), + CreationFailedError::EglNoDisplay => f.write_str("EGL returned no valid display"), + CreationFailedError::EglUnsupportedVersion(e) => { + write!(f, "Unsupported EGL version: {}.{} (EGL 1.5 is required)", e.major, e.minor) + } } } } +impl From for CreationFailedError { + fn from(err: EglError) -> Self { + CreationFailedError::EglError(err) + } +} + pub type GlContext = Rc; pub enum GlContextInner { diff --git a/src/wrappers/egl.rs b/src/wrappers/egl.rs index cd82bcf4..df8717b9 100644 --- a/src/wrappers/egl.rs +++ b/src/wrappers/egl.rs @@ -18,7 +18,7 @@ use crate::wrappers::egl::extensions::Extensions; pub use bound_api::BoundApi; pub use config::EglConfig; pub use context::EglContext; -pub use display::EglDisplay; +pub use display::{EglDisplay, EglVersion}; pub use error::EglError; pub use surface::EglSurface; pub use sys::MissingSymbolError; diff --git a/src/wrappers/egl/context.rs b/src/wrappers/egl/context.rs index a87fe0d3..d8714d47 100644 --- a/src/wrappers/egl/context.rs +++ b/src/wrappers/egl/context.rs @@ -26,6 +26,14 @@ impl EglContext { } } +impl Drop for EglContext { + fn drop(&mut self) { + if let Err(e) = unsafe { self.display.egl().destroy_context(self) } { + crate::warn!("Failed to destroy EGL context: {e}"); + } + } +} + impl Egl { fn get_context_attribs(gl_config: &GlConfig) -> [Int; 7] { let profile_mask = match gl_config.profile { @@ -98,4 +106,16 @@ impl Egl { Ok(()) } } + + unsafe fn destroy_context(&self, context: &EglContext) -> Result<(), EglError> { + let result = unsafe { + (self.inner.functions.eglDestroyContext)(context.display.as_raw(), context.raw.as_ptr()) + }; + + if result == FALSE { + Err(EglError::from_last_error(self)) + } else { + Ok(()) + } + } } diff --git a/src/wrappers/egl/display.rs b/src/wrappers/egl/display.rs index 1de06e45..4185711f 100644 --- a/src/wrappers/egl/display.rs +++ b/src/wrappers/egl/display.rs @@ -1,12 +1,12 @@ use crate::gl::GlConfig; +use crate::platform::gl::CreationFailedError; use crate::platform::X11Connection; use crate::wrappers::egl::bound_api::BoundApi; use crate::wrappers::egl::config::EglConfig; use crate::wrappers::egl::context::EglContext; use crate::wrappers::egl::surface::EglSurface; -use crate::wrappers::egl::sys::EGLContext; -use crate::wrappers::egl::{sys, Egl, EglError, EglInner}; -use crate::wrappers::xlib::{XlibConnection, XlibXcbConnection}; +use crate::wrappers::egl::{sys, Egl, EglError}; +use crate::wrappers::xlib::XlibConnection; use std::ffi::c_void; use std::ptr::NonNull; use std::rc::Rc; @@ -25,15 +25,6 @@ pub struct EglDisplay { } impl EglDisplay { - pub(super) fn new(egl: &Egl, connection: &Rc) -> Result { - let display = egl.create_display_basic(connection.conn.xlib_connection()).unwrap(); - let egl = egl.clone(); - - unsafe { egl.initialize_display(display)? }; - let inner = EglDisplayInner { egl, raw: display, _connection: Rc::clone(connection) }; - Ok(Self { inner: Rc::new(inner) }) - } - pub fn choose_config(&self, config: &GlConfig) -> Result, EglError> { EglConfig::choose_config(config, self) } @@ -67,14 +58,32 @@ impl Drop for EglDisplayInner { } } -struct EglVersion { - major: sys::Int, - minor: sys::Int, +#[derive(Debug, Copy, Clone)] +pub struct EglVersion { + pub major: sys::Int, + pub minor: sys::Int, } impl Egl { - pub fn create_display(&self, connection: &Rc) -> Result { - EglDisplay::new(self, connection) + pub fn create_display( + &self, connection: &Rc, + ) -> Result { + let display = self + .create_display_basic(connection.conn.xlib_connection()) + .ok_or(CreationFailedError::EglNoDisplay)?; + + let egl = self.clone(); + + let version = unsafe { egl.initialize_display(display)? }; + + // Initialize DisplayInner here so its drop impl will run if code below fails + let inner = EglDisplayInner { egl, raw: display, _connection: Rc::clone(connection) }; + + if version.major != 1 || version.minor < 5 { + return Err(CreationFailedError::EglUnsupportedVersion(version)); + } + + Ok(EglDisplay { inner: Rc::new(inner) }) } fn create_display_basic(&self, connection: &XlibConnection) -> Option> { @@ -97,8 +106,6 @@ impl Egl { return Err(EglError::from_last_error(self)); } - dbg!(version.major, version.minor); - Ok(version) } diff --git a/src/wrappers/egl/error.rs b/src/wrappers/egl/error.rs index f7b056b4..4ad12bfd 100644 --- a/src/wrappers/egl/error.rs +++ b/src/wrappers/egl/error.rs @@ -9,6 +9,7 @@ pub struct EglError { } impl EglError { + // TODO: handle NO_ERROR pub fn from_last_error(egl: &Egl) -> EglError { let code = unsafe { (egl.inner.functions.eglGetError)() }; Self { code } diff --git a/src/wrappers/egl/surface.rs b/src/wrappers/egl/surface.rs index be8b653c..8030929a 100644 --- a/src/wrappers/egl/surface.rs +++ b/src/wrappers/egl/surface.rs @@ -3,35 +3,28 @@ use super::*; use crate::gl::GlConfig; use std::ffi::c_void; use std::ptr::NonNull; -use std::rc::Rc; use x11rb::protocol::xproto::Window; -struct EglSurfaceInner { +pub struct EglSurface { display: EglDisplay, raw: NonNull, } -#[derive(Clone)] -pub struct EglSurface { - inner: Rc, -} - impl EglSurface { pub(super) fn create( display: &EglDisplay, config: EglConfig, window: Window, gl_config: &GlConfig, ) -> Result { let raw = display.egl().create_surface(display, config, window, gl_config)?; - let inner = EglSurfaceInner { display: display.clone(), raw }; - Ok(Self { inner: Rc::new(inner) }) + Ok(Self { display: display.clone(), raw }) } pub fn display(&self) -> &EglDisplay { - &self.inner.display + &self.display } pub fn as_raw(&self) -> *mut c_void { - self.inner.raw.as_ptr() + self.raw.as_ptr() } pub fn swap_buffers(&self) -> Result<(), EglError> { @@ -39,6 +32,14 @@ impl EglSurface { } } +impl Drop for EglSurface { + fn drop(&mut self) { + if let Err(e) = unsafe { self.display.egl().destroy_surface(self) } { + crate::warn!("Failed to destroy EGL surface: {e}"); + } + } +} + impl Egl { fn get_surface_attribs(gl_config: &GlConfig) -> [Int; 3] { #[rustfmt::skip] @@ -78,4 +79,16 @@ impl Egl { Ok(()) } } + + unsafe fn destroy_surface(&self, surface: &EglSurface) -> Result<(), EglError> { + let result = unsafe { + (self.inner.functions.eglDestroySurface)(surface.display().as_raw(), surface.as_raw()) + }; + + if result == FALSE { + Err(EglError::from_last_error(self)) + } else { + Ok(()) + } + } } From fed51f8b409783354a9acc6b78e067d7ecb6f8e8 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Mon, 24 Aug 2026 04:54:45 +0200 Subject: [PATCH 07/11] fixes --- src/platform/x11/gl.rs | 9 +++++++++ src/platform/x11/gl/egl.rs | 9 +++++---- src/wrappers/egl/config.rs | 6 ++++-- src/wrappers/egl/error.rs | 8 ++++++-- src/wrappers/egl/sys.rs | 6 ++---- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/platform/x11/gl.rs b/src/platform/x11/gl.rs index e6b28352..fe2f4dfb 100644 --- a/src/platform/x11/gl.rs +++ b/src/platform/x11/gl.rs @@ -11,6 +11,7 @@ use crate::wrappers::egl::{EglConfig, EglDisplay, EglError, EglVersion, MissingS use std::ffi::{c_void, CStr}; use std::rc::Rc; use x11_dl::error::OpenError; +use x11rb::protocol::xproto::Visualid; mod egl; mod glx; @@ -29,6 +30,8 @@ pub enum CreationFailedError { EglError(EglError), EglNoDisplay, EglUnsupportedVersion(EglVersion), + EglUnknownVisualId(Visualid), + EglInvalidVisualId(i32, TryFromIntError), } impl Display for CreationFailedError { @@ -54,6 +57,12 @@ impl Display for CreationFailedError { CreationFailedError::EglUnsupportedVersion(e) => { write!(f, "Unsupported EGL version: {}.{} (EGL 1.5 is required)", e.major, e.minor) } + CreationFailedError::EglInvalidVisualId(id, e) => { + write!(f, "Invalid Visual ID ({id}) returned by EGL: {e}") + } + CreationFailedError::EglUnknownVisualId(id) => { + write!(f, "Unknown Visual ID returned by EGL: {id}") + } } } } diff --git a/src/platform/x11/gl/egl.rs b/src/platform/x11/gl/egl.rs index bf6db9ad..f00514a0 100644 --- a/src/platform/x11/gl/egl.rs +++ b/src/platform/x11/gl/egl.rs @@ -1,5 +1,5 @@ use crate::gl::GlConfig; -use crate::platform::gl::{FbConfig, FbConfigInner, WindowConfig}; +use crate::platform::gl::{CreationFailedError, FbConfig, FbConfigInner, WindowConfig}; use crate::platform::x11::xcb_window::XcbWindow; use crate::platform::{PlatformError, X11Connection}; use crate::wrappers::egl::{Egl, EglConfig, EglContext, EglDisplay, EglSurface}; @@ -30,12 +30,13 @@ impl EglGlContext { connection: &Rc, gl_config: &GlConfig, ) -> Result<(FbConfig, WindowConfig), PlatformError> { let egl = Egl::open()?; - let display = egl.create_display(connection)?; // TODO: check EGL version + let display = egl.create_display(connection)?; - let config = display.choose_config(gl_config)?.unwrap(); + let config = display.choose_config(gl_config)?.ok_or(CreationFailedError::EglNoDisplay)?; let visual = config.get_visual_id(&display)?; - let depth = Self::find_visual_depth_for_id(connection, visual).unwrap(); // TODO + let depth = Self::find_visual_depth_for_id(connection, visual) + .ok_or(CreationFailedError::EglUnknownVisualId(visual))?; let window_config = WindowConfig { depth, visual }; let fb_config = diff --git a/src/wrappers/egl/config.rs b/src/wrappers/egl/config.rs index 916188e3..483092e8 100644 --- a/src/wrappers/egl/config.rs +++ b/src/wrappers/egl/config.rs @@ -57,9 +57,11 @@ impl EglConfig { Ok(value) } - pub fn get_visual_id(&self, display: &EglDisplay) -> Result { + pub fn get_visual_id(&self, display: &EglDisplay) -> Result { let value = self.get_attrib(display, EGL_NATIVE_VISUAL_ID)?; - Ok(value as _) // TODO: cast + let value: Visualid = + value.try_into().map_err(|e| CreationFailedError::EglInvalidVisualId(value, e))?; + Ok(value) } } diff --git a/src/wrappers/egl/error.rs b/src/wrappers/egl/error.rs index 4ad12bfd..3d61d9d7 100644 --- a/src/wrappers/egl/error.rs +++ b/src/wrappers/egl/error.rs @@ -1,4 +1,5 @@ use super::*; +use crate::wrappers::egl::sys::*; use std::error::Error; use std::ffi::c_int; use std::fmt::Display; @@ -9,7 +10,6 @@ pub struct EglError { } impl EglError { - // TODO: handle NO_ERROR pub fn from_last_error(egl: &Egl) -> EglError { let code = unsafe { (egl.inner.functions.eglGetError)() }; Self { code } @@ -18,7 +18,11 @@ impl EglError { impl Display for EglError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - todo!() + if self.code == EGL_SUCCESS { + f.write_str("EGL call failed but error code is EGL_SUCCESS") + } else { + write!(f, "EGL error code: {:x}", self.code) + } } } diff --git a/src/wrappers/egl/sys.rs b/src/wrappers/egl/sys.rs index fdce7892..50c3d9b0 100644 --- a/src/wrappers/egl/sys.rs +++ b/src/wrappers/egl/sys.rs @@ -64,18 +64,16 @@ pub type eglGetCurrentContext = unsafe extern "system" fn() -> EGLContext; pub type eglSwapBuffers = unsafe extern "system" fn(display: EGLDisplay, surface: EGLSurface) -> Boolean; -pub const NONE: Int = 0x3038; pub const ENUM_NONE: Enum = 0x3038; pub const OPENGL_API: Enum = 0x30A2; pub const FALSE: Boolean = 0; pub const NO_DISPLAY: EGLDisplay = 0 as EGLDisplay; pub const EXTENSIONS: Int = 0x3055; +pub const EGL_SUCCESS: Int = 0x3000; pub const EGL_SURFACE_TYPE: Int = 0x3033; pub const EGL_WINDOW_BIT: Int = 0x0004; -pub const EGL_OPENGL_BIT: Int = 0x30A4; - pub const EGL_NONE: Int = 0x3038; pub const EGL_BUFFER_SIZE: Int = 0x3020; pub const EGL_RED_SIZE: Int = 0x3024; @@ -104,7 +102,7 @@ pub struct MissingSymbolError { impl Display for MissingSymbolError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - todo!() + write!(f, "Missing EGL symbol: {}", self.name.to_string_lossy()) } } From a577e7f40962fe8e428f8fafb50359b2b3200f6b Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Mon, 24 Aug 2026 05:08:33 +0200 Subject: [PATCH 08/11] fixes for MSRV --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 809da986..fa2a0c04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,8 +46,7 @@ x11-dl = { version = "2.21.0" } calloop = "0.14.4" percent-encoding = "2.3.2" bytemuck = { version = "1.25.0", features = ["extern_crate_alloc"] } -libloading = "0.9.0" -khronos-egl = "6.0.0" +libloading = "0.8.9" # Libloading 0.9 is out but its MSRV is 1.88 [target.'cfg(target_os="windows")'.dependencies] windows = { version = "0.62.2", features = [ From 1531147ad6876d7322e56b1d1b174c924367ce1a Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Mon, 24 Aug 2026 05:24:31 +0200 Subject: [PATCH 09/11] clippy fix --- src/platform/x11/visual_info.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/platform/x11/visual_info.rs b/src/platform/x11/visual_info.rs index 0f3628db..bdaa7199 100644 --- a/src/platform/x11/visual_info.rs +++ b/src/platform/x11/visual_info.rs @@ -1,6 +1,5 @@ use super::xcb_connection::X11Connection; use crate::platform::*; -use std::rc::Rc; use x11rb::connection::Connection; use x11rb::protocol::xproto::{ Colormap, ColormapAlloc, ConnectionExt, Screen, VisualClass, Visualid, @@ -20,7 +19,7 @@ pub(crate) struct WindowVisualConfig { impl WindowVisualConfig { #[cfg(feature = "opengl")] pub fn find_best_visual_config_for_gl( - connection: &Rc, gl_config: Option, + connection: &std::rc::Rc, gl_config: Option, ) -> Result { let Some(gl_config) = gl_config else { return Self::find_best_visual_config(connection) }; From a7ea13718065813dbee7eb085ea3038b52e0a21f Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Mon, 24 Aug 2026 05:26:44 +0200 Subject: [PATCH 10/11] fix --- src/wrappers/egl/sys.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wrappers/egl/sys.rs b/src/wrappers/egl/sys.rs index 50c3d9b0..bd5435fd 100644 --- a/src/wrappers/egl/sys.rs +++ b/src/wrappers/egl/sys.rs @@ -164,7 +164,7 @@ impl Functions { unsafe fn get( library: &Library, name: &'static CStr, ) -> Result { - let symbol = library.get::>(name)?; + let symbol = library.get::>(name.to_bytes_with_nul())?; let symbol = symbol.lift_option().ok_or(MissingSymbolError { name })?; Ok(*symbol) } From fa4070af88e04b6ea7707395a3eecda1b771e0e4 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:32:57 +0200 Subject: [PATCH 11/11] clippy fix --- src/platform/win/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index d808362e..89ed90fd 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -341,7 +341,7 @@ impl WindowImpl for BaseviewWindow { window.register_drag_drop(drop_target.as_interface())?; #[cfg(feature = "opengl")] - if let Some(gl_config) = self.gl_config.clone() { + if let Some(gl_config) = self.gl_config { let gl_context = gl::GlContextInner::create(window, gl_config)?; let Ok(()) = self.window_state.gl_context.set(Rc::new(gl_context)) else {