1 | //! GLX object creation. |
2 | |
3 | use std::collections::HashSet; |
4 | use std::ffi::{self, CStr}; |
5 | use std::fmt; |
6 | use std::ops::Deref; |
7 | use std::sync::atomic::Ordering; |
8 | use std::sync::Arc; |
9 | |
10 | use glutin_glx_sys::glx; |
11 | use glutin_glx_sys::glx::types::Display as GLXDisplay; |
12 | use raw_window_handle::RawDisplayHandle; |
13 | |
14 | use crate::config::ConfigTemplate; |
15 | use crate::context::Version; |
16 | use crate::display::{AsRawDisplay, DisplayFeatures, GetDisplayExtensions, RawDisplay}; |
17 | use crate::error::{ErrorKind, Result}; |
18 | use crate::prelude::*; |
19 | use crate::private::Sealed; |
20 | use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; |
21 | |
22 | use super::config::Config; |
23 | use super::context::NotCurrentContext; |
24 | use super::surface::Surface; |
25 | use super::{Glx, GlxExtra, XlibErrorHookRegistrar, GLX, GLX_BASE_ERROR, GLX_EXTRA}; |
26 | |
27 | /// A wrapper for the `GLXDisplay`, which is basically an `XDisplay`. |
28 | #[derive (Debug, Clone)] |
29 | pub struct Display { |
30 | pub(crate) inner: Arc<DisplayInner>, |
31 | } |
32 | |
33 | impl Display { |
34 | /// Create GLX display. |
35 | /// |
36 | /// # Safety |
37 | /// |
38 | /// The `display` must point to the valid Xlib display and |
39 | /// `error_hook_registrar` must be registered in your Xlib error handling |
40 | /// callback. |
41 | pub unsafe fn new( |
42 | display: RawDisplayHandle, |
43 | error_hook_registrar: XlibErrorHookRegistrar, |
44 | ) -> Result<Self> { |
45 | // Don't load GLX when unsupported platform was requested. |
46 | let (display, screen) = match display { |
47 | RawDisplayHandle::Xlib(handle) => match handle.display { |
48 | Some(display) => (GlxDisplay(display.as_ptr() as *mut _), handle.screen as i32), |
49 | None => return Err(ErrorKind::BadDisplay.into()), |
50 | }, |
51 | _ => { |
52 | return Err( |
53 | ErrorKind::NotSupported("provided native display isn't supported" ).into() |
54 | ) |
55 | }, |
56 | }; |
57 | |
58 | let glx = match GLX.as_ref() { |
59 | Some(glx) => glx, |
60 | None => return Err(ErrorKind::NotFound.into()), |
61 | }; |
62 | |
63 | // Set the base for errors coming from GLX. |
64 | unsafe { |
65 | let mut error_base = 0; |
66 | let mut event_base = 0; |
67 | if glx.QueryExtension(display.0, &mut error_base, &mut event_base) == 0 { |
68 | // The glx extension isn't present. |
69 | return Err(ErrorKind::InitializationFailed.into()); |
70 | } |
71 | GLX_BASE_ERROR.store(error_base, Ordering::Relaxed); |
72 | } |
73 | |
74 | // This is completely ridiculous, but VirtualBox's OpenGL driver needs |
75 | // some call handled by *it* (i.e. not Mesa) to occur before |
76 | // anything else can happen. That is because VirtualBox's OpenGL |
77 | // driver is going to apply binary patches to Mesa in the DLL |
78 | // constructor and until it's loaded it won't have a chance to do that. |
79 | // |
80 | // The easiest way to do this is to just call `glXQueryVersion()` before |
81 | // doing anything else. See: https://www.virtualbox.org/ticket/8293 |
82 | let version = unsafe { |
83 | let (mut major, mut minor) = (0, 0); |
84 | if glx.QueryVersion(display.0, &mut major, &mut minor) == 0 { |
85 | return Err(ErrorKind::InitializationFailed.into()); |
86 | } |
87 | Version::new(major as u8, minor as u8) |
88 | }; |
89 | |
90 | if version < Version::new(1, 3) { |
91 | return Err(ErrorKind::NotSupported("the glx below 1.3 isn't supported" ).into()); |
92 | } |
93 | |
94 | // Register the error handling hook. |
95 | error_hook_registrar(Box::new(super::glx_error_hook)); |
96 | |
97 | let client_extensions = get_extensions(glx, display); |
98 | let features = Self::extract_display_features(&client_extensions, version); |
99 | |
100 | let inner = Arc::new(DisplayInner { |
101 | raw: display, |
102 | glx, |
103 | glx_extra: GLX_EXTRA.as_ref(), |
104 | version, |
105 | screen, |
106 | features, |
107 | client_extensions, |
108 | }); |
109 | |
110 | Ok(Self { inner }) |
111 | } |
112 | |
113 | /// Get a reference to the initialized GLX API. |
114 | pub fn glx(&self) -> &'static Glx { |
115 | self.inner.glx |
116 | } |
117 | |
118 | fn extract_display_features( |
119 | extensions: &HashSet<&'static str>, |
120 | version: Version, |
121 | ) -> DisplayFeatures { |
122 | let mut features = DisplayFeatures::empty(); |
123 | |
124 | features.set( |
125 | DisplayFeatures::MULTISAMPLING_PIXEL_FORMATS, |
126 | version >= Version::new(1, 4) || extensions.contains("GLX_ARB_multisample" ), |
127 | ); |
128 | |
129 | features.set( |
130 | DisplayFeatures::FLOAT_PIXEL_FORMAT, |
131 | extensions.contains("GLX_ARB_fbconfig_float" ), |
132 | ); |
133 | |
134 | features.set( |
135 | DisplayFeatures::SRGB_FRAMEBUFFERS, |
136 | extensions.contains("GLX_ARB_framebuffer_sRGB" ) |
137 | || extensions.contains("GLX_EXT_framebuffer_sRGB" ), |
138 | ); |
139 | |
140 | features.set( |
141 | DisplayFeatures::CREATE_ES_CONTEXT, |
142 | extensions.contains("GLX_EXT_create_context_es2_profile" ) |
143 | || extensions.contains("GLX_EXT_create_context_es_profile" ), |
144 | ); |
145 | |
146 | features.set( |
147 | DisplayFeatures::SWAP_CONTROL, |
148 | extensions.contains("GLX_EXT_swap_control" ) |
149 | || extensions.contains("GLX_SGI_swap_control" ) |
150 | || extensions.contains("GLX_MESA_swap_control" ), |
151 | ); |
152 | |
153 | features.set( |
154 | DisplayFeatures::CONTEXT_ROBUSTNESS, |
155 | extensions.contains("GLX_ARB_create_context_robustness" ), |
156 | ); |
157 | |
158 | features.set( |
159 | DisplayFeatures::CONTEXT_RELEASE_BEHAVIOR, |
160 | extensions.contains("GLX_ARB_context_flush_control" ), |
161 | ); |
162 | |
163 | features.set( |
164 | DisplayFeatures::CONTEXT_NO_ERROR, |
165 | extensions.contains("GLX_ARB_create_context_no_error" ), |
166 | ); |
167 | |
168 | features |
169 | } |
170 | } |
171 | |
172 | impl GlDisplay for Display { |
173 | type Config = Config; |
174 | type NotCurrentContext = NotCurrentContext; |
175 | type PbufferSurface = Surface<PbufferSurface>; |
176 | type PixmapSurface = Surface<PixmapSurface>; |
177 | type WindowSurface = Surface<WindowSurface>; |
178 | |
179 | unsafe fn find_configs( |
180 | &self, |
181 | template: ConfigTemplate, |
182 | ) -> Result<Box<dyn Iterator<Item = Self::Config> + '_>> { |
183 | unsafe { Self::find_configs(self, template) } |
184 | } |
185 | |
186 | unsafe fn create_window_surface( |
187 | &self, |
188 | config: &Self::Config, |
189 | surface_attributes: &SurfaceAttributes<WindowSurface>, |
190 | ) -> Result<Self::WindowSurface> { |
191 | unsafe { Self::create_window_surface(self, config, surface_attributes) } |
192 | } |
193 | |
194 | unsafe fn create_pbuffer_surface( |
195 | &self, |
196 | config: &Self::Config, |
197 | surface_attributes: &SurfaceAttributes<PbufferSurface>, |
198 | ) -> Result<Self::PbufferSurface> { |
199 | unsafe { Self::create_pbuffer_surface(self, config, surface_attributes) } |
200 | } |
201 | |
202 | unsafe fn create_context( |
203 | &self, |
204 | config: &Self::Config, |
205 | context_attributes: &crate::context::ContextAttributes, |
206 | ) -> Result<Self::NotCurrentContext> { |
207 | unsafe { Self::create_context(self, config, context_attributes) } |
208 | } |
209 | |
210 | unsafe fn create_pixmap_surface( |
211 | &self, |
212 | config: &Self::Config, |
213 | surface_attributes: &SurfaceAttributes<PixmapSurface>, |
214 | ) -> Result<Self::PixmapSurface> { |
215 | unsafe { Self::create_pixmap_surface(self, config, surface_attributes) } |
216 | } |
217 | |
218 | fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { |
219 | unsafe { self.inner.glx.GetProcAddress(addr.as_ptr() as *const _) as *const _ } |
220 | } |
221 | |
222 | fn version_string(&self) -> String { |
223 | format!("GLX {}. {}" , self.inner.version.major, self.inner.version.minor) |
224 | } |
225 | |
226 | fn supported_features(&self) -> DisplayFeatures { |
227 | self.inner.features |
228 | } |
229 | } |
230 | |
231 | impl GetDisplayExtensions for Display { |
232 | fn extensions(&self) -> &HashSet<&'static str> { |
233 | &self.inner.client_extensions |
234 | } |
235 | } |
236 | |
237 | impl AsRawDisplay for Display { |
238 | fn raw_display(&self) -> RawDisplay { |
239 | RawDisplay::Glx(self.inner.raw.cast()) |
240 | } |
241 | } |
242 | |
243 | impl Sealed for Display {} |
244 | |
245 | pub(crate) struct DisplayInner { |
246 | pub(crate) glx: &'static Glx, |
247 | pub(crate) glx_extra: Option<&'static GlxExtra>, |
248 | pub(crate) raw: GlxDisplay, |
249 | pub(crate) screen: i32, |
250 | pub(crate) version: Version, |
251 | pub(crate) features: DisplayFeatures, |
252 | /// Client GLX extensions. |
253 | pub(crate) client_extensions: HashSet<&'static str>, |
254 | } |
255 | |
256 | impl fmt::Debug for DisplayInner { |
257 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
258 | f&mut DebugStruct<'_, '_>.debug_struct("Display" ) |
259 | .field("raw" , &self.raw) |
260 | .field("version" , &self.version) |
261 | .field("screen" , &self.screen) |
262 | .field("features" , &self.features) |
263 | .field(name:"extensions" , &self.client_extensions) |
264 | .finish() |
265 | } |
266 | } |
267 | |
268 | #[derive (Debug, Clone, Copy)] |
269 | pub(crate) struct GlxDisplay(*mut GLXDisplay); |
270 | |
271 | unsafe impl Send for GlxDisplay {} |
272 | unsafe impl Sync for GlxDisplay {} |
273 | |
274 | impl Deref for GlxDisplay { |
275 | type Target = *mut GLXDisplay; |
276 | |
277 | fn deref(&self) -> &Self::Target { |
278 | &self.0 |
279 | } |
280 | } |
281 | |
282 | /// Load the GLX extensions. |
283 | fn get_extensions(glx: &Glx, display: GlxDisplay) -> HashSet<&'static str> { |
284 | unsafe { |
285 | let extensions: *const {unknown} = glx.GetClientString(dpy:display.0, name:glx::EXTENSIONS as i32); |
286 | if extensions.is_null() { |
287 | return HashSet::new(); |
288 | } |
289 | |
290 | if let Ok(extensions: &str) = CStr::from_ptr(extensions).to_str() { |
291 | extensions.split(' ' ).collect::<HashSet<_>>() |
292 | } else { |
293 | HashSet::new() |
294 | } |
295 | } |
296 | } |
297 | |