| 1 | //! Everything related to `EGLDisplay`. |
| 2 | |
| 3 | use std::collections::HashSet; |
| 4 | use std::ffi::{self, CStr}; |
| 5 | use std::fmt; |
| 6 | use std::mem::MaybeUninit; |
| 7 | use std::ops::Deref; |
| 8 | use std::os::raw::c_char; |
| 9 | use std::sync::Arc; |
| 10 | |
| 11 | use glutin_egl_sys::egl; |
| 12 | use glutin_egl_sys::egl::types::{EGLAttrib, EGLDisplay, EGLint}; |
| 13 | |
| 14 | use once_cell::sync::OnceCell; |
| 15 | |
| 16 | use raw_window_handle::{RawDisplayHandle, XlibDisplayHandle}; |
| 17 | |
| 18 | use crate::config::ConfigTemplate; |
| 19 | use crate::context::Version; |
| 20 | use crate::display::{AsRawDisplay, DisplayFeatures, GetDisplayExtensions, RawDisplay}; |
| 21 | use crate::error::{ErrorKind, Result}; |
| 22 | use crate::prelude::*; |
| 23 | use crate::private::Sealed; |
| 24 | use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; |
| 25 | |
| 26 | use super::config::Config; |
| 27 | use super::context::NotCurrentContext; |
| 28 | use super::device::Device; |
| 29 | use super::surface::Surface; |
| 30 | |
| 31 | use super::{Egl, EGL}; |
| 32 | |
| 33 | /// Extensions that don't require any display. |
| 34 | pub(crate) static CLIENT_EXTENSIONS: OnceCell<HashSet<&'static str>> = OnceCell::new(); |
| 35 | |
| 36 | /// A wrapper for the `EGLDisplay` and its supported extensions. |
| 37 | #[derive (Debug, Clone)] |
| 38 | pub struct Display { |
| 39 | // Inner display to simplify passing it around. |
| 40 | pub(crate) inner: Arc<DisplayInner>, |
| 41 | } |
| 42 | |
| 43 | impl Display { |
| 44 | /// Create EGL display with the native display. |
| 45 | /// |
| 46 | /// # Safety |
| 47 | /// |
| 48 | /// `raw_display` must point to a valid system display. Using zero or |
| 49 | /// [`std::ptr::null()`] for the display will result in using |
| 50 | /// `EGL_DEFAULT_DISPLAY`, which is not recommended or will |
| 51 | /// work on a platform with a concept of native display, like Wayland. |
| 52 | pub unsafe fn new(raw_display: RawDisplayHandle) -> Result<Self> { |
| 53 | let egl = match EGL.as_ref() { |
| 54 | Some(egl) => egl, |
| 55 | None => return Err(ErrorKind::NotFound.into()), |
| 56 | }; |
| 57 | |
| 58 | CLIENT_EXTENSIONS.get_or_init(|| get_extensions(egl, egl::NO_DISPLAY)); |
| 59 | |
| 60 | // Create a EGL display by chaining all display creation functions aborting on |
| 61 | // `EGL_BAD_ATTRIBUTE`. |
| 62 | let display = Self::get_platform_display(egl, raw_display) |
| 63 | .or_else(|err| { |
| 64 | if err.error_kind() == ErrorKind::BadAttribute { |
| 65 | Err(err) |
| 66 | } else { |
| 67 | Self::get_platform_display_ext(egl, raw_display) |
| 68 | } |
| 69 | }) |
| 70 | .or_else(|err| { |
| 71 | if err.error_kind() == ErrorKind::BadAttribute { |
| 72 | Err(err) |
| 73 | } else { |
| 74 | Self::get_display(egl, raw_display) |
| 75 | } |
| 76 | })?; |
| 77 | |
| 78 | Self::initialize_display(egl, display, Some(raw_display)) |
| 79 | } |
| 80 | |
| 81 | /// Create an EGL display using the specified device. |
| 82 | /// |
| 83 | /// In most cases, prefer [`Display::new()`] unless you need to render |
| 84 | /// off screen or use other extensions like EGLStreams. |
| 85 | /// |
| 86 | /// This function may take an optional [`RawDisplayHandle`] argument. At the |
| 87 | /// moment the `raw_display` argument is ignored and this function will |
| 88 | /// return [`Err`]. This may change in the future. |
| 89 | /// |
| 90 | /// # Safety |
| 91 | /// |
| 92 | /// If `raw_display` is [`Some`], `raw_display` must point to a valid |
| 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 | |
| 560 | impl 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 | |
| 619 | impl GetDisplayExtensions for Display { |
| 620 | fn extensions(&self) -> &HashSet<&'static str> { |
| 621 | &self.inner.display_extensions |
| 622 | } |
| 623 | } |
| 624 | |
| 625 | impl AsRawDisplay for Display { |
| 626 | fn raw_display(&self) -> RawDisplay { |
| 627 | RawDisplay::Egl(*self.inner.raw) |
| 628 | } |
| 629 | } |
| 630 | |
| 631 | impl Sealed for Display {} |
| 632 | |
| 633 | pub(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 | |
| 653 | impl 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 | |
| 684 | impl 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 | |
| 695 | impl 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)] |
| 749 | pub(crate) struct NativeDisplay(RawDisplayHandle); |
| 750 | |
| 751 | unsafe impl Send for NativeDisplay {} |
| 752 | unsafe impl Sync for NativeDisplay {} |
| 753 | |
| 754 | impl Deref for NativeDisplay { |
| 755 | type Target = RawDisplayHandle; |
| 756 | |
| 757 | fn deref(&self) -> &Self::Target { |
| 758 | &self.0 |
| 759 | } |
| 760 | } |
| 761 | |
| 762 | #[derive (Debug, Clone)] |
| 763 | pub(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. |
| 775 | unsafe impl Send for EglDisplay {} |
| 776 | unsafe impl Sync for EglDisplay {} |
| 777 | |
| 778 | impl 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`. |
| 791 | pub(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. |
| 808 | pub(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 | |