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