1 | //! Everything related to `EGLDisplay`. |
2 | |
3 | use std::collections::HashSet; |
4 | use std::ffi::{self, CStr}; |
5 | use std::mem::MaybeUninit; |
6 | use std::ops::Deref; |
7 | use std::os::raw::c_char; |
8 | use std::sync::Arc; |
9 | use std::{fmt, ptr}; |
10 | |
11 | use glutin_egl_sys::egl; |
12 | use glutin_egl_sys::egl::types::{EGLAttrib, EGLDisplay, EGLint}; |
13 | |
14 | use once_cell::sync::OnceCell; |
15 | |
16 | use raw_window_handle::RawDisplayHandle; |
17 | |
18 | use crate::config::ConfigTemplate; |
19 | use crate::context::Version; |
20 | use crate::display::{AsRawDisplay, DisplayFeatures, GetDisplayExtensions, RawDisplay}; |
21 | use crate::error::{ErrorKind, Result}; |
22 | use crate::prelude::*; |
23 | use crate::private::Sealed; |
24 | use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; |
25 | |
26 | use super::config::Config; |
27 | use super::context::NotCurrentContext; |
28 | use super::device::Device; |
29 | use super::surface::Surface; |
30 | |
31 | use super::{Egl, EGL}; |
32 | |
33 | /// Extensions that don't require any display. |
34 | pub(crate) static CLIENT_EXTENSIONS: OnceCell<HashSet<&'static str>> = OnceCell::new(); |
35 | |
36 | /// A wrapper for the `EGLDisplay` and its supported extensions. |
37 | #[derive (Debug, Clone)] |
38 | pub struct Display { |
39 | // Inner display to simplify passing it around. |
40 | pub(crate) inner: Arc<DisplayInner>, |
41 | } |
42 | |
43 | impl Display { |
44 | /// Create EGL display with the native display. |
45 | /// |
46 | /// # Safety |
47 | /// |
48 | /// `raw_display` must point to a valid system display. Using zero or |
49 | /// [`std::ptr::null()`] for the display will result in using |
50 | /// `EGL_DEFAULT_DISPLAY`, which is not recommended or will |
51 | /// work on a platform with a concept of native display, like Wayland. |
52 | pub unsafe fn new(raw_display: RawDisplayHandle) -> Result<Self> { |
53 | let egl = match EGL.as_ref() { |
54 | Some(egl) => egl, |
55 | None => return Err(ErrorKind::NotFound.into()), |
56 | }; |
57 | |
58 | CLIENT_EXTENSIONS.get_or_init(|| get_extensions(egl, egl::NO_DISPLAY)); |
59 | |
60 | // Create a EGL display by chaining all display creation functions aborting on |
61 | // `EGL_BAD_ATTRIBUTE`. |
62 | let display = Self::get_platform_display(egl, raw_display) |
63 | .or_else(|err| { |
64 | if err.error_kind() == ErrorKind::BadAttribute { |
65 | Err(err) |
66 | } else { |
67 | Self::get_platform_display_ext(egl, raw_display) |
68 | } |
69 | }) |
70 | .or_else(|err| { |
71 | if err.error_kind() == ErrorKind::BadAttribute { |
72 | Err(err) |
73 | } else { |
74 | Self::get_display(egl, raw_display) |
75 | } |
76 | })?; |
77 | |
78 | Self::initialize_display(egl, display, Some(raw_display)) |
79 | } |
80 | |
81 | /// Create an EGL display using the specified device. |
82 | /// |
83 | /// In most cases, prefer [`Display::new()`] unless you need to render |
84 | /// off screen or use other extensions like EGLStreams. |
85 | /// |
86 | /// This function may take an optional [`RawDisplayHandle`] argument. At the |
87 | /// moment the `raw_display` argument is ignored and this function will |
88 | /// return [`Err`]. This may change in the future. |
89 | /// |
90 | /// # Safety |
91 | /// |
92 | /// If `raw_display` is [`Some`], `raw_display` must point to a valid system |
93 | /// display. |
94 | pub unsafe fn with_device( |
95 | device: &Device, |
96 | raw_display: Option<RawDisplayHandle>, |
97 | ) -> Result<Self> { |
98 | let egl = match EGL.as_ref() { |
99 | Some(egl) => egl, |
100 | None => return Err(ErrorKind::NotFound.into()), |
101 | }; |
102 | |
103 | if raw_display.is_some() { |
104 | return Err(ErrorKind::NotSupported( |
105 | "Display::with_device does not support a `raw_display` argument yet" , |
106 | ) |
107 | .into()); |
108 | } |
109 | |
110 | if !egl.GetPlatformDisplayEXT.is_loaded() { |
111 | return Err(ErrorKind::NotSupported("eglGetPlatformDisplayEXT is not supported" ).into()); |
112 | } |
113 | |
114 | // Okay to unwrap here because the client extensions must have been enumerated |
115 | // while querying the available devices or the device was gotten from an |
116 | // existing display. |
117 | let extensions = CLIENT_EXTENSIONS.get().unwrap(); |
118 | |
119 | if !extensions.contains("EGL_EXT_platform_base" ) |
120 | && !extensions.contains("EGL_EXT_platform_device" ) |
121 | { |
122 | return Err(ErrorKind::NotSupported( |
123 | "Creating a display from a device is not supported" , |
124 | ) |
125 | .into()); |
126 | } |
127 | |
128 | let mut attrs = Vec::<EGLint>::with_capacity(3); |
129 | |
130 | // TODO: Some extensions exist like EGL_EXT_device_drm which allow specifying |
131 | // which DRM master fd to use under the hood by the implementation. This would |
132 | // mean there would need to be an unsafe equivalent to this function. |
133 | |
134 | // Push at the end so we can pop it on failure |
135 | let mut has_display_reference = extensions.contains("EGL_KHR_display_reference" ); |
136 | if has_display_reference { |
137 | attrs.push(egl::TRACK_REFERENCES_KHR as _); |
138 | attrs.push(egl::TRUE as _); |
139 | } |
140 | |
141 | // Push `egl::NONE` to terminate the list. |
142 | attrs.push(egl::NONE as EGLint); |
143 | |
144 | // NOTE: This fallback is needed because libglvnd advertises client extensions |
145 | // if at least one vendor library supports them. This leads to creation |
146 | // failures for the vendor libraries not supporting |
147 | // EGL_KHR_display_reference. Also according to the spec creation is allowed |
148 | // to fail with EGL_KHR_display_reference set to EGL_TRUE even if |
149 | // EGL_KHR_display_reference is advertised in the client extension |
150 | // string, so just always try creation without EGL_KHR_display_reference |
151 | // if it failed using it. |
152 | let platform_display = loop { |
153 | match Self::check_display_error(unsafe { |
154 | egl.GetPlatformDisplayEXT( |
155 | egl::PLATFORM_DEVICE_EXT, |
156 | device.raw_device() as *mut _, |
157 | attrs.as_ptr(), |
158 | ) |
159 | }) { |
160 | Err(_) if has_display_reference => { |
161 | attrs.pop(); |
162 | attrs.pop(); |
163 | attrs.pop(); |
164 | attrs.push(egl::NONE as EGLint); |
165 | has_display_reference = false; |
166 | }, |
167 | platform_display => break platform_display, |
168 | } |
169 | } |
170 | .map(EglDisplay::Ext)?; |
171 | |
172 | Self::initialize_display(egl, platform_display, None) |
173 | } |
174 | |
175 | /// Get the [`Device`] the display is using. |
176 | /// |
177 | /// This function returns [`Err`] if the `EGL_EXT_device_query` or |
178 | /// `EGL_EXT_device_base` extensions are not available. |
179 | pub fn device(&self) -> Result<Device> { |
180 | let no_display_extensions = CLIENT_EXTENSIONS.get().unwrap(); |
181 | |
182 | // Querying the device of a display only requires EGL_EXT_device_query, but we |
183 | // also check if EGL_EXT_device_base is available since |
184 | // EGL_EXT_device_base also provides EGL_EXT_device_query. |
185 | if !no_display_extensions.contains("EGL_EXT_device_query" ) |
186 | || !no_display_extensions.contains("EGL_EXT_device_base" ) |
187 | { |
188 | return Err(ErrorKind::NotSupported( |
189 | "Querying the device from a display is not supported" , |
190 | ) |
191 | .into()); |
192 | } |
193 | |
194 | let device = ptr::null_mut(); |
195 | if unsafe { |
196 | self.inner.egl.QueryDisplayAttribEXT( |
197 | *self.inner.raw, |
198 | egl::DEVICE_EXT as EGLint, |
199 | device as *mut _, |
200 | ) |
201 | } == egl::FALSE |
202 | { |
203 | // Check for EGL_NOT_INITIALIZED in case the display was externally terminated. |
204 | // |
205 | // EGL_BAD_ATTRIBUTE shouldn't be returned since EGL_DEVICE_EXT should be a |
206 | // valid display attribute. |
207 | return Err(super::check_error().err().unwrap_or_else(|| { |
208 | ErrorKind::NotSupported("failed to query device from display" ).into() |
209 | })); |
210 | } |
211 | |
212 | Device::from_ptr(self.inner.egl, device) |
213 | } |
214 | |
215 | /// Terminate the EGL display. |
216 | /// |
217 | /// When the display is managed by glutin with the |
218 | /// `EGL_KHR_display_reference` this function does nothing and |
219 | /// `eglTerminate` will be automatically invoked during display destruction. |
220 | /// |
221 | /// # Safety |
222 | /// |
223 | /// This function will destroy the global EGL state, even the one created |
224 | /// and managed by other libraries. Use this function only when you're |
225 | /// bringing everything down. |
226 | pub unsafe fn terminate(self) { |
227 | if !self.inner.uses_display_reference() { |
228 | unsafe { |
229 | self.inner.egl.Terminate(*self.inner.raw); |
230 | } |
231 | } |
232 | } |
233 | |
234 | fn get_platform_display(egl: &Egl, display: RawDisplayHandle) -> Result<EglDisplay> { |
235 | if !egl.GetPlatformDisplay.is_loaded() { |
236 | return Err(ErrorKind::NotSupported("eglGetPlatformDisplay is not supported" ).into()); |
237 | } |
238 | |
239 | let extensions = CLIENT_EXTENSIONS.get().unwrap(); |
240 | |
241 | let mut attrs = Vec::<EGLAttrib>::with_capacity(5); |
242 | let (platform, mut display) = match display { |
243 | #[cfg (wayland_platform)] |
244 | RawDisplayHandle::Wayland(handle) |
245 | if extensions.contains("EGL_KHR_platform_wayland" ) => |
246 | { |
247 | (egl::PLATFORM_WAYLAND_KHR, handle.display) |
248 | }, |
249 | #[cfg (x11_platform)] |
250 | RawDisplayHandle::Xlib(handle) if extensions.contains("EGL_KHR_platform_x11" ) => { |
251 | attrs.push(egl::PLATFORM_X11_SCREEN_KHR as EGLAttrib); |
252 | attrs.push(handle.screen as EGLAttrib); |
253 | (egl::PLATFORM_X11_KHR, handle.display) |
254 | }, |
255 | RawDisplayHandle::Gbm(handle) if extensions.contains("EGL_KHR_platform_gbm" ) => { |
256 | (egl::PLATFORM_GBM_KHR, handle.gbm_device) |
257 | }, |
258 | RawDisplayHandle::Android(_) if extensions.contains("EGL_KHR_platform_android" ) => { |
259 | (egl::PLATFORM_ANDROID_KHR, egl::DEFAULT_DISPLAY as *mut _) |
260 | }, |
261 | _ => { |
262 | return Err( |
263 | ErrorKind::NotSupported("provided display handle is not supported" ).into() |
264 | ) |
265 | }, |
266 | }; |
267 | |
268 | // Be explicit here. |
269 | if display.is_null() { |
270 | display = egl::DEFAULT_DISPLAY as *mut _; |
271 | } |
272 | |
273 | // Push at the end so we can pop it on failure |
274 | let mut has_display_reference = extensions.contains("EGL_KHR_display_reference" ); |
275 | if has_display_reference { |
276 | attrs.push(egl::TRACK_REFERENCES_KHR as _); |
277 | attrs.push(egl::TRUE as _); |
278 | } |
279 | |
280 | // Push `egl::NONE` to terminate the list. |
281 | attrs.push(egl::NONE as EGLAttrib); |
282 | |
283 | // NOTE: This fallback is needed because libglvnd advertises client extensions |
284 | // if at least one vendor library supports them. This leads to creation |
285 | // failures for the vendor libraries not supporting |
286 | // EGL_KHR_display_reference. Also according to the spec creation is allowed |
287 | // to fail with EGL_KHR_display_reference set to EGL_TRUE even if |
288 | // EGL_KHR_display_reference is advertised in the client extension |
289 | // string, so just always try creation without EGL_KHR_display_reference |
290 | // if it failed using it. |
291 | let platform_display = loop { |
292 | match Self::check_display_error(unsafe { |
293 | egl.GetPlatformDisplay(platform, display as *mut _, attrs.as_ptr()) |
294 | }) { |
295 | Err(_) if has_display_reference => { |
296 | attrs.pop(); |
297 | attrs.pop(); |
298 | attrs.pop(); |
299 | attrs.push(egl::NONE as EGLAttrib); |
300 | has_display_reference = false; |
301 | }, |
302 | platform_display => break platform_display, |
303 | } |
304 | }; |
305 | |
306 | platform_display.map(EglDisplay::Khr) |
307 | } |
308 | |
309 | fn get_platform_display_ext(egl: &Egl, display: RawDisplayHandle) -> Result<EglDisplay> { |
310 | if !egl.GetPlatformDisplayEXT.is_loaded() { |
311 | return Err(ErrorKind::NotSupported("eglGetPlatformDisplayEXT is not supported" ).into()); |
312 | } |
313 | |
314 | let extensions = CLIENT_EXTENSIONS.get().unwrap(); |
315 | |
316 | let mut attrs = Vec::<EGLint>::with_capacity(5); |
317 | let mut legacy = false; |
318 | let (platform, mut display) = match display { |
319 | #[cfg (wayland_platform)] |
320 | RawDisplayHandle::Wayland(handle) |
321 | if extensions.contains("EGL_EXT_platform_wayland" ) => |
322 | { |
323 | (egl::PLATFORM_WAYLAND_EXT, handle.display) |
324 | }, |
325 | #[cfg (x11_platform)] |
326 | RawDisplayHandle::Xlib(handle) if extensions.contains("EGL_EXT_platform_x11" ) => { |
327 | attrs.push(egl::PLATFORM_X11_SCREEN_EXT as EGLint); |
328 | attrs.push(handle.screen as EGLint); |
329 | (egl::PLATFORM_X11_EXT, handle.display) |
330 | }, |
331 | #[cfg (x11_platform)] |
332 | RawDisplayHandle::Xcb(handle) |
333 | if extensions.contains("EGL_MESA_platform_xcb" ) |
334 | || extensions.contains("EGL_EXT_platform_xcb" ) => |
335 | { |
336 | attrs.push(egl::PLATFORM_XCB_SCREEN_EXT as EGLint); |
337 | attrs.push(handle.screen as EGLint); |
338 | (egl::PLATFORM_XCB_EXT, handle.connection) |
339 | }, |
340 | RawDisplayHandle::Gbm(handle) if extensions.contains("EGL_MESA_platform_gbm" ) => { |
341 | (egl::PLATFORM_GBM_MESA, handle.gbm_device) |
342 | }, |
343 | RawDisplayHandle::Windows(..) if extensions.contains("EGL_ANGLE_platform_angle" ) => { |
344 | // Only CreateWindowSurface appears to work with Angle. |
345 | legacy = true; |
346 | (egl::PLATFORM_ANGLE_ANGLE, egl::DEFAULT_DISPLAY as *mut _) |
347 | }, |
348 | _ => { |
349 | return Err( |
350 | ErrorKind::NotSupported("provided display handle is not supported" ).into() |
351 | ) |
352 | }, |
353 | }; |
354 | |
355 | // Be explicit here. |
356 | if display.is_null() { |
357 | display = egl::DEFAULT_DISPLAY as *mut _; |
358 | } |
359 | |
360 | // Push at the end so we can pop it on failure |
361 | let mut has_display_reference = extensions.contains("EGL_KHR_display_reference" ); |
362 | if has_display_reference { |
363 | attrs.push(egl::TRACK_REFERENCES_KHR as _); |
364 | attrs.push(egl::TRUE as _); |
365 | } |
366 | |
367 | // Push `egl::NONE` to terminate the list. |
368 | attrs.push(egl::NONE as EGLint); |
369 | |
370 | // NOTE: This fallback is needed because libglvnd advertises client extensions |
371 | // if at least one vendor library supports them. This leads to creation |
372 | // failures for the vendor libraries not supporting |
373 | // EGL_KHR_display_reference. Also according to the spec creation is allowed |
374 | // to fail with EGL_KHR_display_reference set to EGL_TRUE even if |
375 | // EGL_KHR_display_reference is advertised in the client extension |
376 | // string, so just always try creation without EGL_KHR_display_reference |
377 | // if it failed using it. |
378 | let platform_display = loop { |
379 | match Self::check_display_error(unsafe { |
380 | egl.GetPlatformDisplayEXT(platform, display as *mut _, attrs.as_ptr()) |
381 | }) { |
382 | Err(_) if has_display_reference => { |
383 | attrs.pop(); |
384 | attrs.pop(); |
385 | attrs.pop(); |
386 | attrs.push(egl::NONE as EGLint); |
387 | has_display_reference = false; |
388 | }, |
389 | platform_display => break platform_display, |
390 | } |
391 | }; |
392 | |
393 | platform_display.map(|display| { |
394 | if legacy { |
395 | // NOTE: For angle we use the Legacy code path, as that uses CreateWindowSurface |
396 | // instead of CreatePlatformWindowSurface*. The latter somehow |
397 | // doesn't work, only the former does. But Angle's own example also use the |
398 | // former: https://github.com/google/angle/blob/main/util/EGLWindow.cpp#L424 |
399 | EglDisplay::Legacy(display) |
400 | } else { |
401 | EglDisplay::Ext(display) |
402 | } |
403 | }) |
404 | } |
405 | |
406 | fn get_display(egl: &Egl, display: RawDisplayHandle) -> Result<EglDisplay> { |
407 | let mut display = match display { |
408 | RawDisplayHandle::Gbm(handle) => handle.gbm_device, |
409 | #[cfg (x11_platform)] |
410 | RawDisplayHandle::Xlib(handle) => handle.display, |
411 | RawDisplayHandle::Android(_) => egl::DEFAULT_DISPLAY as *mut _, |
412 | _ => { |
413 | return Err( |
414 | ErrorKind::NotSupported("provided display handle is not supported" ).into() |
415 | ) |
416 | }, |
417 | }; |
418 | |
419 | if display.is_null() { |
420 | display = egl::DEFAULT_DISPLAY as *mut _; |
421 | } |
422 | |
423 | let display = unsafe { egl.GetDisplay(display) }; |
424 | Self::check_display_error(display).map(EglDisplay::Legacy) |
425 | } |
426 | |
427 | fn extract_display_features( |
428 | extensions: &HashSet<&'static str>, |
429 | version: Version, |
430 | ) -> DisplayFeatures { |
431 | // Extract features. |
432 | let mut supported_features = DisplayFeatures::CREATE_ES_CONTEXT |
433 | | DisplayFeatures::MULTISAMPLING_PIXEL_FORMATS |
434 | | DisplayFeatures::SWAP_CONTROL; |
435 | |
436 | supported_features.set( |
437 | DisplayFeatures::FLOAT_PIXEL_FORMAT, |
438 | extensions.contains("EGL_EXT_pixel_format_float" ), |
439 | ); |
440 | |
441 | supported_features |
442 | .set(DisplayFeatures::SRGB_FRAMEBUFFERS, extensions.contains("EGL_KHR_gl_colorspace" )); |
443 | |
444 | supported_features.set( |
445 | DisplayFeatures::CONTEXT_ROBUSTNESS, |
446 | version > Version::new(1, 5) |
447 | || extensions.contains("EGL_EXT_create_context_robustness" ), |
448 | ); |
449 | |
450 | supported_features.set( |
451 | DisplayFeatures::CONTEXT_NO_ERROR, |
452 | extensions.contains("EGL_KHR_create_context_no_error" ), |
453 | ); |
454 | |
455 | supported_features |
456 | } |
457 | |
458 | fn check_display_error(display: EGLDisplay) -> Result<EGLDisplay> { |
459 | if display == egl::NO_DISPLAY { |
460 | // XXX the specification is a bit vague here, so fallback instead of hard |
461 | // assert. |
462 | Err(super::check_error().err().unwrap_or_else(|| { |
463 | ErrorKind::NotSupported("failed to create EGLDisplay without a reason" ).into() |
464 | })) |
465 | } else { |
466 | Ok(display) |
467 | } |
468 | } |
469 | |
470 | fn initialize_display( |
471 | egl: &'static Egl, |
472 | display: EglDisplay, |
473 | raw_display_handle: Option<RawDisplayHandle>, |
474 | ) -> Result<Self> { |
475 | let version = unsafe { |
476 | let (mut major, mut minor) = (0, 0); |
477 | if egl.Initialize(*display, &mut major, &mut minor) == egl::FALSE { |
478 | return Err(super::check_error().expect_err("eglInit failed without a reason" )); |
479 | } |
480 | |
481 | Version::new(major as u8, minor as u8) |
482 | }; |
483 | |
484 | // Load extensions. |
485 | let display_extensions = get_extensions(egl, *display); |
486 | let features = Self::extract_display_features(&display_extensions, version); |
487 | |
488 | let inner = Arc::new(DisplayInner { |
489 | egl, |
490 | raw: display, |
491 | _native_display: raw_display_handle.map(NativeDisplay), |
492 | version, |
493 | display_extensions, |
494 | features, |
495 | }); |
496 | Ok(Self { inner }) |
497 | } |
498 | } |
499 | |
500 | impl GlDisplay for Display { |
501 | type Config = Config; |
502 | type NotCurrentContext = NotCurrentContext; |
503 | type PbufferSurface = Surface<PbufferSurface>; |
504 | type PixmapSurface = Surface<PixmapSurface>; |
505 | type WindowSurface = Surface<WindowSurface>; |
506 | |
507 | unsafe fn find_configs( |
508 | &self, |
509 | template: ConfigTemplate, |
510 | ) -> Result<Box<dyn Iterator<Item = Self::Config> + '_>> { |
511 | unsafe { Self::find_configs(self, template) } |
512 | } |
513 | |
514 | unsafe fn create_window_surface( |
515 | &self, |
516 | config: &Self::Config, |
517 | surface_attributes: &SurfaceAttributes<WindowSurface>, |
518 | ) -> Result<Self::WindowSurface> { |
519 | unsafe { Self::create_window_surface(self, config, surface_attributes) } |
520 | } |
521 | |
522 | unsafe fn create_pbuffer_surface( |
523 | &self, |
524 | config: &Self::Config, |
525 | surface_attributes: &SurfaceAttributes<PbufferSurface>, |
526 | ) -> Result<Self::PbufferSurface> { |
527 | unsafe { Self::create_pbuffer_surface(self, config, surface_attributes) } |
528 | } |
529 | |
530 | unsafe fn create_context( |
531 | &self, |
532 | config: &Self::Config, |
533 | context_attributes: &crate::context::ContextAttributes, |
534 | ) -> Result<Self::NotCurrentContext> { |
535 | unsafe { Self::create_context(self, config, context_attributes) } |
536 | } |
537 | |
538 | unsafe fn create_pixmap_surface( |
539 | &self, |
540 | config: &Self::Config, |
541 | surface_attributes: &SurfaceAttributes<PixmapSurface>, |
542 | ) -> Result<Self::PixmapSurface> { |
543 | unsafe { Self::create_pixmap_surface(self, config, surface_attributes) } |
544 | } |
545 | |
546 | fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { |
547 | unsafe { self.inner.egl.GetProcAddress(addr.as_ptr()) as *const _ } |
548 | } |
549 | |
550 | fn version_string(&self) -> String { |
551 | format!("EGL {}. {}" , self.inner.version.major, self.inner.version.minor) |
552 | } |
553 | |
554 | fn supported_features(&self) -> DisplayFeatures { |
555 | self.inner.features |
556 | } |
557 | } |
558 | |
559 | impl GetDisplayExtensions for Display { |
560 | fn extensions(&self) -> &HashSet<&'static str> { |
561 | &self.inner.display_extensions |
562 | } |
563 | } |
564 | |
565 | impl AsRawDisplay for Display { |
566 | fn raw_display(&self) -> RawDisplay { |
567 | RawDisplay::Egl(*self.inner.raw) |
568 | } |
569 | } |
570 | |
571 | impl Sealed for Display {} |
572 | |
573 | pub(crate) struct DisplayInner { |
574 | /// Pointer to the EGL handler to simplify API calls. |
575 | pub(crate) egl: &'static Egl, |
576 | |
577 | /// Pointer to the egl display. |
578 | pub(crate) raw: EglDisplay, |
579 | |
580 | /// The version of the egl library. |
581 | pub(crate) version: Version, |
582 | |
583 | /// Display EGL extensions. |
584 | pub(crate) display_extensions: HashSet<&'static str>, |
585 | |
586 | /// The features supported by the display. |
587 | pub(crate) features: DisplayFeatures, |
588 | |
589 | /// The raw display used to create EGL display. |
590 | pub(crate) _native_display: Option<NativeDisplay>, |
591 | } |
592 | |
593 | impl DisplayInner { |
594 | fn uses_display_reference(&self) -> bool { |
595 | if !CLIENT_EXTENSIONS.get().unwrap().contains("EGL_KHR_display_reference" ) { |
596 | return false; |
597 | } |
598 | |
599 | // If the EGL_TRACK_REFERENCES_KHR attribute is true, then EGL will internally |
600 | // reference count the display. If that is the case, glutin can |
601 | // terminate the display without worry for the instance being |
602 | // reused elsewhere. |
603 | let mut track_references = MaybeUninit::<EGLAttrib>::uninit(); |
604 | (match self.raw { |
605 | EglDisplay::Khr(khr) => unsafe { |
606 | self.egl.QueryDisplayAttribKHR( |
607 | khr, |
608 | egl::TRACK_REFERENCES_KHR as _, |
609 | track_references.as_mut_ptr(), |
610 | ) |
611 | }, |
612 | EglDisplay::Ext(ext) => unsafe { |
613 | self.egl.QueryDisplayAttribEXT( |
614 | ext, |
615 | egl::TRACK_REFERENCES_KHR as _, |
616 | track_references.as_mut_ptr(), |
617 | ) |
618 | }, |
619 | EglDisplay::Legacy(_) => egl::FALSE, |
620 | } == egl::TRUE) |
621 | } |
622 | } |
623 | |
624 | impl fmt::Debug for DisplayInner { |
625 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
626 | f&mut DebugStruct<'_, '_>.debug_struct("Display" ) |
627 | .field("raw" , &self.raw) |
628 | .field("version" , &self.version) |
629 | .field("features" , &self.features) |
630 | .field(name:"extensions" , &self.display_extensions) |
631 | .finish() |
632 | } |
633 | } |
634 | |
635 | impl Drop for DisplayInner { |
636 | fn drop(&mut self) { |
637 | if self.uses_display_reference() { |
638 | unsafe { |
639 | self.egl.Terminate(*self.raw); |
640 | } |
641 | } |
642 | |
643 | // We cannot call safely call `eglTerminate`. |
644 | // |
645 | // This may sound confusing, but this is a result of how EGL works: |
646 | // |
647 | // From the documentation of `eglGetDisplay`: |
648 | // > Multiple calls made to eglGetDisplay with the same display_id will |
649 | // > return the same EGLDisplay handle. |
650 | // |
651 | // And from the documentation of `eglGetPlatformDisplay`: |
652 | // > Multiple calls made to eglGetPlatformDisplay with the same |
653 | // > parameters will return the same |
654 | // > EGLDisplay handle. |
655 | // |
656 | // Furthermore the following is done when a display is initialized: |
657 | // > Initializing an already initialized EGL display connection has no |
658 | // > effect besides returning the |
659 | // > version numbers. |
660 | // |
661 | // Terminating a display connection and then creating a new display |
662 | // connection will reference the same display. This effectively |
663 | // makes an EGLDisplay a singleton for the specified display_id or |
664 | // platform and native display. |
665 | // |
666 | // Because EGLDisplay is a singleton, this makes the following sequence |
667 | // problematic: |
668 | // |
669 | // 1. A display is created for a platform |
670 | // 2. A second display is created for the same platform |
671 | // 3. The first display is dropped, resulting in eglTerminate being |
672 | // called. |
673 | // 4. A context created from the second display is made |
674 | // current, but it has been terminated and returns an EGL_BAD_DISPLAY |
675 | // error. |
676 | // |
677 | // But wait? This causes a memory leak! |
678 | // |
679 | // Yes it does indeed result in a memory leak since we do not terminate |
680 | // displays on drop. For most applications there is only ever a |
681 | // single EGLDisplay for the lifetime of the application. The cost |
682 | // of not dropping the display is negligible because the display will |
683 | // probably be destroyed on app termination and we can let the |
684 | // operating system deal with tearing down EGL instead. |
685 | } |
686 | } |
687 | |
688 | #[derive (Debug, Clone, Copy)] |
689 | pub(crate) struct NativeDisplay(RawDisplayHandle); |
690 | |
691 | unsafe impl Send for NativeDisplay {} |
692 | unsafe impl Sync for NativeDisplay {} |
693 | |
694 | impl Deref for NativeDisplay { |
695 | type Target = RawDisplayHandle; |
696 | |
697 | fn deref(&self) -> &Self::Target { |
698 | &self.0 |
699 | } |
700 | } |
701 | |
702 | #[derive (Debug, Clone)] |
703 | pub(crate) enum EglDisplay { |
704 | /// The display was created with the KHR extension. |
705 | Khr(EGLDisplay), |
706 | |
707 | /// The display was created with the EXT extension. |
708 | Ext(EGLDisplay), |
709 | |
710 | /// The display in use is a legacy variant. |
711 | Legacy(EGLDisplay), |
712 | } |
713 | |
714 | // The EGL display could be shared between threads. |
715 | unsafe impl Send for EglDisplay {} |
716 | unsafe impl Sync for EglDisplay {} |
717 | |
718 | impl Deref for EglDisplay { |
719 | type Target = EGLDisplay; |
720 | |
721 | fn deref(&self) -> &Self::Target { |
722 | match self { |
723 | EglDisplay::Khr(display: &*const c_void) => display, |
724 | EglDisplay::Ext(display: &*const c_void) => display, |
725 | EglDisplay::Legacy(display: &*const c_void) => display, |
726 | } |
727 | } |
728 | } |
729 | |
730 | /// Collect EGL extensions for the given `display`. |
731 | pub(crate) fn get_extensions(egl: &Egl, display: EGLDisplay) -> HashSet<&'static str> { |
732 | unsafe { |
733 | let extensions: *const i8 = egl.QueryString(dpy:display, name:egl::EXTENSIONS as i32); |
734 | // SAFETY: The EGL specification guarantees the returned string is |
735 | // static and null terminated: |
736 | // |
737 | // > eglQueryString returns a pointer to a static, zero-terminated |
738 | // > string describing properties of the EGL client or of an EGL |
739 | // > display connection. |
740 | extensions_from_ptr(extensions) |
741 | } |
742 | } |
743 | |
744 | /// # Safety |
745 | /// |
746 | /// - The `extensions` pointer must be NULL (representing no extensions) or it |
747 | /// must be non-null and contain a static, null terminated C string. |
748 | pub(crate) unsafe fn extensions_from_ptr(extensions: *const c_char) -> HashSet<&'static str> { |
749 | if extensions.is_null() { |
750 | return HashSet::new(); |
751 | } |
752 | |
753 | // SAFETY: The caller has ensured the string pointer is null terminated. |
754 | if let Ok(extensions: &str) = unsafe { CStr::from_ptr(extensions) }.to_str() { |
755 | extensions.split(' ' ).collect::<HashSet<&'static str>>() |
756 | } else { |
757 | HashSet::new() |
758 | } |
759 | } |
760 | |