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) => {
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
170impl 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
229impl GetDisplayExtensions for Display {
230 fn extensions(&self) -> &HashSet<&'static str> {
231 &self.inner.client_extensions
232 }
233}
234
235impl AsRawDisplay for Display {
236 fn raw_display(&self) -> RawDisplay {
237 RawDisplay::Glx(self.inner.raw.cast())
238 }
239}
240
241impl Sealed for Display {}
242
243pub(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
254impl 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)]
267pub(crate) struct GlxDisplay(*mut GLXDisplay);
268
269unsafe impl Send for GlxDisplay {}
270unsafe impl Sync for GlxDisplay {}
271
272impl 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.
281fn 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