glutin/api/egl/
display.rs

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.debug_struct("Display")
687            .field("raw", &self.raw)
688            .field("version", &self.version)
689            .field("features", &self.features)
690            .field("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) => display,
784            EglDisplay::Ext(display) => display,
785            EglDisplay::Legacy(display) => 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 = egl.QueryString(display, 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) = unsafe { CStr::from_ptr(extensions) }.to_str() {
815        extensions.split(' ').collect::<HashSet<&'static str>>()
816    } else {
817        HashSet::new()
818    }
819}