1//! GLX platform Api.
2#![allow(clippy::unnecessary_cast)] // needed for 32bit & 64bit support
3
4use std::ffi::{self, CStr, CString};
5use std::ops::{Deref, DerefMut};
6use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
7use std::sync::Mutex;
8
9use libloading::Library;
10use once_cell::sync::Lazy;
11use x11_dl::xlib::{self, XErrorEvent};
12
13use glutin_glx_sys::{glx, glx_extra};
14
15use crate::error::{Error, ErrorKind, Result};
16use crate::lib_loading::{SymLoading, SymWrapper};
17use crate::platform::x11::XLIB;
18
19pub mod config;
20pub mod context;
21pub mod display;
22pub mod surface;
23
24/// When using Xlib we need to get errors from it somehow, however creating
25/// inner `XDisplay` to handle that or change the error hook is unsafe in
26/// multithreaded applications, given that error hook is per process and not
27/// connection.
28///
29/// The hook registrar must call to the function inside xlib error
30/// [`handler`].
31///
32/// The `bool` value returned by that hook tells whether the error was handled
33/// by it or not. So when it returns `true` it means that your error handling
34/// routine shouldn't handle the error as it was handled by the hook already.
35///
36/// [`handler`]: https://tronche.com/gui/x/xlib/event-handling/protocol-errors/XSetErrorHandler.html
37pub type XlibErrorHookRegistrar =
38 Box<dyn Fn(Box<dyn Fn(*mut ffi::c_void, *mut ffi::c_void) -> bool + Send + Sync>)>;
39
40/// The base used for GLX errors.
41static GLX_BASE_ERROR: AtomicI32 = AtomicI32::new(0);
42
43/// The last error arrived from GLX normalized by `GLX_BASE_ERROR`.
44static LAST_GLX_ERROR: Lazy<Mutex<Option<Error>>> = Lazy::new(|| Mutex::new(None));
45
46/// Whether we're in the process of getting GLX error. Otherwise we may handle
47/// the winit's error.
48static SYNCING_GLX_ERROR: AtomicBool = AtomicBool::new(false);
49
50static GLX: Lazy<Option<Glx>> = Lazy::new(|| {
51 let paths: [&str; 2] = ["libGL.so.1", "libGL.so"];
52
53 unsafe { SymWrapper::new(&paths).map(op:Glx).ok() }
54});
55
56static GLX_EXTRA: Lazy<Option<GlxExtra>> = Lazy::new(|| {
57 let glx: &Glx = GLX.as_ref()?;
58 Some(GlxExtra::new(glx))
59});
60
61pub(crate) struct Glx(pub SymWrapper<glx::Glx>);
62
63unsafe impl Sync for Glx {}
64unsafe impl Send for Glx {}
65
66impl SymLoading for glx::Glx {
67 unsafe fn load_with(lib: &Library) -> Self {
68 Self::load_with(|sym: &str| unsafe {
69 lib.get(CString::new(sym.as_bytes()).unwrap().as_bytes_with_nul())
70 .map(|sym| *sym)
71 .unwrap_or(default:std::ptr::null_mut())
72 })
73 }
74}
75
76impl Deref for Glx {
77 type Target = glx::Glx;
78
79 fn deref(&self) -> &Self::Target {
80 &self.0
81 }
82}
83
84impl DerefMut for Glx {
85 #[inline]
86 fn deref_mut(&mut self) -> &mut Self::Target {
87 &mut self.0
88 }
89}
90
91pub(crate) struct GlxExtra(glx_extra::Glx);
92
93unsafe impl Sync for GlxExtra {}
94unsafe impl Send for GlxExtra {}
95
96impl GlxExtra {
97 #[inline]
98 pub fn new(glx: &Glx) -> Self {
99 GlxExtra(glx_extra::Glx::load_with(|proc_name: &str| {
100 let c_str: CString = CString::new(proc_name).unwrap();
101 unsafe { glx.GetProcAddress(procName:c_str.as_ptr() as *const u8) as *const _ }
102 }))
103 }
104}
105
106impl Deref for GlxExtra {
107 type Target = glx_extra::Glx;
108
109 fn deref(&self) -> &Self::Target {
110 &self.0
111 }
112}
113
114impl DerefMut for GlxExtra {
115 #[inline]
116 fn deref_mut(&mut self) -> &mut Self::Target {
117 &mut self.0
118 }
119}
120/// Store the last error received from the GLX.
121fn glx_error_hook(_display: *mut ffi::c_void, xerror_event: *mut ffi::c_void) -> bool {
122 // In case we've not forced the sync, ignore the error.
123 if !SYNCING_GLX_ERROR.load(Ordering::Relaxed) {
124 return false;
125 }
126
127 let xerror = xerror_event as *mut XErrorEvent;
128 unsafe {
129 let code = (*xerror).error_code;
130 let glx_code = code as i32 - GLX_BASE_ERROR.load(Ordering::Relaxed);
131
132 // Get the kind of the error.
133 let kind = match code as u8 {
134 xlib::BadValue => ErrorKind::BadAttribute,
135 xlib::BadMatch => ErrorKind::BadMatch,
136 xlib::BadWindow => ErrorKind::BadNativeWindow,
137 xlib::BadAlloc => ErrorKind::OutOfMemory,
138 xlib::BadPixmap => ErrorKind::BadPixmap,
139 xlib::BadAccess => ErrorKind::BadAccess,
140 _ if glx_code >= 0 => match glx_code as glx::types::GLenum {
141 glx::PROTO_BAD_CONTEXT => ErrorKind::BadContext,
142 glx::PROTO_BAD_CONTEXT_STATE => ErrorKind::BadContext,
143 glx::PROTO_BAD_CURRENT_DRAWABLE => ErrorKind::BadCurrentSurface,
144 glx::PROTO_BAD_CURRENT_WINDOW => ErrorKind::BadCurrentSurface,
145 glx::PROTO_BAD_FBCONFIG => ErrorKind::BadConfig,
146 glx::PROTO_BAD_PBUFFER => ErrorKind::BadPbuffer,
147 glx::PROTO_BAD_PIXMAP => ErrorKind::BadPixmap,
148 glx::PROTO_UNSUPPORTED_PRIVATE_REQUEST => ErrorKind::Misc,
149 glx::PROTO_BAD_DRAWABLE => ErrorKind::BadSurface,
150 glx::PROTO_BAD_WINDOW => ErrorKind::BadSurface,
151 glx::PROTO_BAD_CONTEXT_TAG => ErrorKind::Misc,
152 glx::PROTO_BAD_RENDER_REQUEST => ErrorKind::Misc,
153 glx::PROTO_BAD_LARGE_REQUEST => ErrorKind::Misc,
154 _ => return false,
155 },
156 _ => return false,
157 };
158
159 // Get the string from X11 error.
160 let mut buf = vec![0u8; 1024];
161 (XLIB.as_ref().unwrap().XGetErrorText)(
162 _display as *mut _,
163 (*xerror).error_code as _,
164 buf.as_mut_ptr() as *mut _,
165 buf.len() as _,
166 );
167 let description = CStr::from_ptr(buf.as_ptr() as *const _).to_string_lossy().to_string();
168
169 *LAST_GLX_ERROR.lock().unwrap() =
170 Some(Error::new(Some(code as _), Some(description), kind));
171
172 true
173 }
174}
175
176/// Prevent error being overwritten when accessing the handler from the multiple
177/// threads.
178static ERROR_SECTION_LOCK: Mutex<()> = Mutex::new(());
179
180/// Get the error from the X11.
181///
182/// XXX mesa and I'd guess other GLX implementations, send the error, by taking
183/// the Xlib Error handling hook, getting the current hook, and calling back to
184/// the user, meaning that no `XSync` should be done.
185fn last_glx_error<T, F: FnOnce() -> T>(callback: F) -> Result<T> {
186 let _guard: MutexGuard<'_, ()> = ERROR_SECTION_LOCK.lock().unwrap();
187
188 // Mark that we're syncing the error.
189 SYNCING_GLX_ERROR.store(val:true, order:Ordering::Relaxed);
190
191 // Execute the user routine that may produce GLX error.
192 let result: T = callback();
193
194 // XXX We might want to XSync here in addition, because what mesa is doing might
195 // not be common, but I'd assume that what mesa doing is common.
196
197 // Reset and report last error.
198 let result: Result = match LAST_GLX_ERROR.lock().unwrap().take() {
199 Some(error: Error) => Err(error),
200 None => Ok(result),
201 };
202
203 // Release the mark.
204 SYNCING_GLX_ERROR.store(val:false, order:Ordering::Relaxed);
205
206 result
207}
208