1use 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
33pub(crate) static CLIENT_EXTENSIONS: OnceCell<HashSet<&'static str>> = OnceCell::new();
35
36#[derive(Debug, Clone)]
38pub struct Display {
39 pub(crate) inner: Arc<DisplayInner>,
41}
42
43impl Display {
44 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 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 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 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 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 attrs.push(egl::NONE as EGLint);
151
152 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 pub fn device(&self) -> Result<Device> {
188 let no_display_extensions = CLIENT_EXTENSIONS.get().unwrap();
189
190 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 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 pub fn egl(&self) -> &'static Egl {
232 self.inner.egl
233 }
234
235 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 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 attrs.push(egl::NONE as EGLAttrib);
304
305 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 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 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 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 attrs.push(egl::NONE as EGLint);
404
405 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 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 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 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 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 EglDisplay::Ext(display)
536 } else {
537 EglDisplay::Legacy(display)
538 }
539 },
540 display => display,
542 };
543
544 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 pub(crate) egl: &'static Egl,
636
637 pub(crate) raw: EglDisplay,
639
640 pub(crate) version: Version,
642
643 pub(crate) display_extensions: HashSet<&'static str>,
645
646 pub(crate) features: DisplayFeatures,
648
649 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 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 }
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 Khr(EGLDisplay),
766
767 Ext(EGLDisplay),
769
770 Legacy(EGLDisplay),
772}
773
774unsafe 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
790pub(crate) fn get_extensions(egl: &Egl, display: EGLDisplay) -> HashSet<&'static str> {
792 unsafe {
793 let extensions = egl.QueryString(display, egl::EXTENSIONS as i32);
794 extensions_from_ptr(extensions)
801 }
802}
803
804pub(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 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}