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