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: [&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 | pub(crate) struct Glx(pub SymWrapper<glx::Glx>); |
62 | |
63 | unsafe impl Sync for Glx {} |
64 | unsafe impl Send for Glx {} |
65 | |
66 | impl 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 | |
76 | impl Deref for Glx { |
77 | type Target = glx::Glx; |
78 | |
79 | fn deref(&self) -> &Self::Target { |
80 | &self.0 |
81 | } |
82 | } |
83 | |
84 | impl DerefMut for Glx { |
85 | #[inline ] |
86 | fn deref_mut(&mut self) -> &mut Self::Target { |
87 | &mut self.0 |
88 | } |
89 | } |
90 | |
91 | pub(crate) struct GlxExtra(glx_extra::Glx); |
92 | |
93 | unsafe impl Sync for GlxExtra {} |
94 | unsafe impl Send for GlxExtra {} |
95 | |
96 | impl 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 | |
106 | impl Deref for GlxExtra { |
107 | type Target = glx_extra::Glx; |
108 | |
109 | fn deref(&self) -> &Self::Target { |
110 | &self.0 |
111 | } |
112 | } |
113 | |
114 | impl 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. |
121 | fn 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. |
178 | static 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. |
185 | fn 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 | |