glutin/api/glx/
display.rs

1//! GLX object creation.
2
3use std::collections::HashSet;
4use std::ffi::{self, CStr};
5use std::fmt;
6use std::ops::Deref;
7use std::sync::atomic::Ordering;
8use std::sync::Arc;
9
10use glutin_glx_sys::glx;
11use glutin_glx_sys::glx::types::Display as GLXDisplay;
12use raw_window_handle::RawDisplayHandle;
13
14use crate::config::ConfigTemplate;
15use crate::context::Version;
16use crate::display::{AsRawDisplay, DisplayFeatures, GetDisplayExtensions, RawDisplay};
17use crate::error::{ErrorKind, Result};
18use crate::prelude::*;
19use crate::private::Sealed;
20use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface};
21
22use super::config::Config;
23use super::context::NotCurrentContext;
24use super::surface::Surface;
25use 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)]
29pub struct Display {
30    pub(crate) inner: Arc<DisplayInner>,
31}
32
33impl 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
172impl 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
231impl GetDisplayExtensions for Display {
232    fn extensions(&self) -> &HashSet<&'static str> {
233        &self.inner.client_extensions
234    }
235}
236
237impl AsRawDisplay for Display {
238    fn raw_display(&self) -> RawDisplay {
239        RawDisplay::Glx(self.inner.raw.cast())
240    }
241}
242
243impl Sealed for Display {}
244
245pub(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
256impl fmt::Debug for DisplayInner {
257    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258        f.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("extensions", &self.client_extensions)
264            .finish()
265    }
266}
267
268#[derive(Debug, Clone, Copy)]
269pub(crate) struct GlxDisplay(*mut GLXDisplay);
270
271unsafe impl Send for GlxDisplay {}
272unsafe impl Sync for GlxDisplay {}
273
274impl 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.
283fn get_extensions(glx: &Glx, display: GlxDisplay) -> HashSet<&'static str> {
284    unsafe {
285        let extensions = glx.GetClientString(display.0, glx::EXTENSIONS as i32);
286        if extensions.is_null() {
287            return HashSet::new();
288        }
289
290        if let Ok(extensions) = CStr::from_ptr(extensions).to_str() {
291            extensions.split(' ').collect::<HashSet<_>>()
292        } else {
293            HashSet::new()
294        }
295    }
296}