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