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 | |