1//! Everything related to `EGLDisplay`.
2
3use std::collections::HashSet;
4use std::ffi::{self, CStr};
5use std::mem::MaybeUninit;
6use std::ops::Deref;
7use std::os::raw::c_char;
8use std::sync::Arc;
9use std::{fmt, ptr};
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;
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 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
500impl 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
559impl GetDisplayExtensions for Display {
560 fn extensions(&self) -> &HashSet<&'static str> {
561 &self.inner.display_extensions
562 }
563}
564
565impl AsRawDisplay for Display {
566 fn raw_display(&self) -> RawDisplay {
567 RawDisplay::Egl(*self.inner.raw)
568 }
569}
570
571impl Sealed for Display {}
572
573pub(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
593impl 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
624impl 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
635impl 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)]
689pub(crate) struct NativeDisplay(RawDisplayHandle);
690
691unsafe impl Send for NativeDisplay {}
692unsafe impl Sync for NativeDisplay {}
693
694impl Deref for NativeDisplay {
695 type Target = RawDisplayHandle;
696
697 fn deref(&self) -> &Self::Target {
698 &self.0
699 }
700}
701
702#[derive(Debug, Clone)]
703pub(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.
715unsafe impl Send for EglDisplay {}
716unsafe impl Sync for EglDisplay {}
717
718impl 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`.
731pub(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.
748pub(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