1//! The OpenGL platform display selection and creation.
2#![allow(unreachable_patterns)]
3
4use std::collections::HashSet;
5use std::ffi::{self, CStr};
6use std::fmt;
7
8use bitflags::bitflags;
9use raw_window_handle::RawDisplayHandle;
10
11use crate::config::{Config, ConfigTemplate, GlConfig};
12use crate::context::{ContextAttributes, NotCurrentContext, NotCurrentGlContext};
13use crate::error::Result;
14use crate::private::{gl_api_dispatch, Sealed};
15use crate::surface::{
16 GlSurface, PbufferSurface, PixmapSurface, Surface, SurfaceAttributes, WindowSurface,
17};
18
19#[cfg(cgl_backend)]
20use crate::api::cgl::display::Display as CglDisplay;
21#[cfg(egl_backend)]
22use crate::api::egl::display::Display as EglDisplay;
23#[cfg(glx_backend)]
24use crate::api::glx::display::Display as GlxDisplay;
25#[cfg(glx_backend)]
26use crate::api::glx::XlibErrorHookRegistrar;
27#[cfg(wgl_backend)]
28use crate::api::wgl::display::Display as WglDisplay;
29
30/// A trait to group common display operations.
31pub trait GlDisplay: Sealed {
32 /// A window surface created by the display.
33 type WindowSurface: GlSurface<WindowSurface>;
34 /// A pixmap surface created by the display.
35 type PixmapSurface: GlSurface<PixmapSurface>;
36 /// A pbuffer surface created by the display.
37 type PbufferSurface: GlSurface<PbufferSurface>;
38 /// A config that is used by the display.
39 type Config: GlConfig;
40 /// A context that is being used by the display.
41 type NotCurrentContext: NotCurrentGlContext;
42
43 /// Find configurations matching the given `template`.
44 ///
45 /// # Safety
46 ///
47 /// Some platforms use [`RawWindowHandle`] to pick configs, so it
48 /// must point to a valid object if it was passed on
49 /// [`crate::config::ConfigTemplate`].
50 ///
51 /// [`RawWindowHandle`]: raw_window_handle::RawWindowHandle
52 unsafe fn find_configs(
53 &self,
54 template: ConfigTemplate,
55 ) -> Result<Box<dyn Iterator<Item = Self::Config> + '_>>;
56
57 /// Create the graphics platform context.
58 ///
59 /// # Safety
60 ///
61 /// Some platforms use [`RawWindowHandle`] for context creation, so it must
62 /// point to a valid object.
63 ///
64 /// # Platform-specific
65 ///
66 /// - **Wayland:** this call may latch the underlying back buffer of the
67 /// currently active context (will do with mesa drivers), meaning that all
68 /// resize operations will apply to it after the next
69 /// [`GlSurface::swap_buffers`]. To workaround this behavior the current
70 /// context should be made [`not current`].
71 ///
72 /// [`RawWindowHandle`]: raw_window_handle::RawWindowHandle
73 /// [`not current`]: crate::context::PossiblyCurrentGlContext::make_not_current
74 unsafe fn create_context(
75 &self,
76 config: &Self::Config,
77 context_attributes: &ContextAttributes,
78 ) -> Result<Self::NotCurrentContext>;
79
80 /// Create the surface that can be used to render into native window.
81 ///
82 /// # Safety
83 ///
84 /// The [`RawWindowHandle`] must point to a valid object.
85 ///
86 /// [`RawWindowHandle`]: raw_window_handle::RawWindowHandle
87 unsafe fn create_window_surface(
88 &self,
89 config: &Self::Config,
90 surface_attributes: &SurfaceAttributes<WindowSurface>,
91 ) -> Result<Self::WindowSurface>;
92
93 /// Create the surface that can be used to render into pbuffer.
94 ///
95 /// # Safety
96 ///
97 /// The function is safe in general, but marked as not for compatibility
98 /// reasons.
99 unsafe fn create_pbuffer_surface(
100 &self,
101 config: &Self::Config,
102 surface_attributes: &SurfaceAttributes<PbufferSurface>,
103 ) -> Result<Self::PbufferSurface>;
104
105 /// Create the surface that can be used to render into pixmap.
106 ///
107 /// # Safety
108 ///
109 /// The [`NativePixmap`] must represent a valid native pixmap.
110 ///
111 /// [`NativePixmap`]: crate::surface::NativePixmap
112 unsafe fn create_pixmap_surface(
113 &self,
114 config: &Self::Config,
115 surface_attributes: &SurfaceAttributes<PixmapSurface>,
116 ) -> Result<Self::PixmapSurface>;
117
118 /// Return the address of an OpenGL function.
119 ///
120 /// # Api-specific
121 ///
122 /// - **WGL:** to load all the functions you must have a current context on
123 /// the calling thread, otherwise only a limited set of functions will be
124 /// loaded.
125 fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void;
126
127 /// Helper to obtain the information about the underlying display.
128 ///
129 /// This function is intended to be used for logging purposes to help with
130 /// troubleshooting issues.
131 fn version_string(&self) -> String;
132
133 /// Get the features supported by the display.
134 ///
135 /// These features could be used to check that something is supported
136 /// beforehand instead of doing fallback.
137 fn supported_features(&self) -> DisplayFeatures;
138}
139
140/// Get the [`Display`].
141pub trait GetGlDisplay: Sealed {
142 /// The display used by the object.
143 type Target: GlDisplay;
144
145 /// Obtain the GL display used to create a particular GL object.
146 fn display(&self) -> Self::Target;
147}
148
149/// Obtain the underlying api extensions.
150pub trait GetDisplayExtensions: Sealed {
151 /// Supported extensions by the display.
152 ///
153 /// # Api-specific
154 ///
155 /// - **WGL:** to have extensions loaded, `raw_window_handle` must be used
156 /// when creating the display.
157 fn extensions(&self) -> &HashSet<&'static str>;
158}
159
160/// Get the raw handle to the [`Display`].
161pub trait AsRawDisplay {
162 /// A raw handle to the underlying Api display.
163 fn raw_display(&self) -> RawDisplay;
164}
165
166/// The graphics display to handle underlying graphics platform in a
167/// cross-platform way.
168///
169/// The display can be accessed from any thread.
170///
171/// ```no_run
172/// fn test_send<T: Send>() {}
173/// fn test_sync<T: Sync>() {}
174/// test_send::<glutin::display::Display>();
175/// test_sync::<glutin::display::Display>();
176/// ```
177#[derive(Debug, Clone)]
178pub enum Display {
179 /// The EGL display.
180 #[cfg(egl_backend)]
181 Egl(EglDisplay),
182
183 /// The GLX display.
184 #[cfg(glx_backend)]
185 Glx(GlxDisplay),
186
187 /// The WGL display.
188 #[cfg(wgl_backend)]
189 Wgl(WglDisplay),
190
191 /// The CGL display.
192 #[cfg(cgl_backend)]
193 Cgl(CglDisplay),
194}
195
196impl Display {
197 /// Create a graphics platform display from the given raw display handle.
198 ///
199 /// The display mixing isn't supported, so if you created EGL display you
200 /// can't use it with the GLX display objects. Interaction between those
201 /// will result in a runtime panic.
202 ///
203 /// # Safety
204 ///
205 /// The `display` must point to the valid platform display and be valid for
206 /// the entire lifetime of all Objects created with that display.
207 ///
208 /// The `preference` must contain pointers to the valid values if GLX or WGL
209 /// specific options were used.
210 pub unsafe fn new(display: RawDisplayHandle, preference: DisplayApiPreference) -> Result<Self> {
211 match preference {
212 #[cfg(egl_backend)]
213 DisplayApiPreference::Egl => unsafe { Ok(Self::Egl(EglDisplay::new(display)?)) },
214 #[cfg(glx_backend)]
215 DisplayApiPreference::Glx(registrar) => unsafe {
216 Ok(Self::Glx(GlxDisplay::new(display, registrar)?))
217 },
218 #[cfg(all(egl_backend, glx_backend))]
219 DisplayApiPreference::GlxThenEgl(registrar) => unsafe {
220 if let Ok(display) = GlxDisplay::new(display, registrar) {
221 Ok(Self::Glx(display))
222 } else {
223 Ok(Self::Egl(EglDisplay::new(display)?))
224 }
225 },
226 #[cfg(all(egl_backend, glx_backend))]
227 DisplayApiPreference::EglThenGlx(registrar) => unsafe {
228 if let Ok(display) = EglDisplay::new(display) {
229 Ok(Self::Egl(display))
230 } else {
231 Ok(Self::Glx(GlxDisplay::new(display, registrar)?))
232 }
233 },
234 #[cfg(wgl_backend)]
235 DisplayApiPreference::Wgl(window_handle) => unsafe {
236 Ok(Self::Wgl(WglDisplay::new(display, window_handle)?))
237 },
238 #[cfg(all(egl_backend, wgl_backend))]
239 DisplayApiPreference::EglThenWgl(window_handle) => unsafe {
240 if let Ok(display) = EglDisplay::new(display) {
241 Ok(Self::Egl(display))
242 } else {
243 Ok(Self::Wgl(WglDisplay::new(display, window_handle)?))
244 }
245 },
246 #[cfg(all(egl_backend, wgl_backend))]
247 DisplayApiPreference::WglThenEgl(window_handle) => unsafe {
248 if let Ok(display) = WglDisplay::new(display, window_handle) {
249 Ok(Self::Wgl(display))
250 } else {
251 Ok(Self::Egl(EglDisplay::new(display)?))
252 }
253 },
254 #[cfg(cgl_backend)]
255 DisplayApiPreference::Cgl => unsafe { Ok(Self::Cgl(CglDisplay::new(display)?)) },
256 }
257 }
258}
259
260impl GlDisplay for Display {
261 type Config = Config;
262 type NotCurrentContext = NotCurrentContext;
263 type PbufferSurface = Surface<PbufferSurface>;
264 type PixmapSurface = Surface<PixmapSurface>;
265 type WindowSurface = Surface<WindowSurface>;
266
267 unsafe fn find_configs(
268 &self,
269 template: ConfigTemplate,
270 ) -> Result<Box<dyn Iterator<Item = Self::Config> + '_>> {
271 match self {
272 #[cfg(egl_backend)]
273 Self::Egl(display) => unsafe {
274 Ok(Box::new(display.find_configs(template)?.map(Config::Egl)))
275 },
276 #[cfg(glx_backend)]
277 Self::Glx(display) => unsafe {
278 Ok(Box::new(display.find_configs(template)?.map(Config::Glx)))
279 },
280 #[cfg(wgl_backend)]
281 Self::Wgl(display) => unsafe {
282 Ok(Box::new(display.find_configs(template)?.map(Config::Wgl)))
283 },
284 #[cfg(cgl_backend)]
285 Self::Cgl(display) => unsafe {
286 Ok(Box::new(display.find_configs(template)?.map(Config::Cgl)))
287 },
288 }
289 }
290
291 unsafe fn create_context(
292 &self,
293 config: &Self::Config,
294 context_attributes: &ContextAttributes,
295 ) -> Result<Self::NotCurrentContext> {
296 match (self, config) {
297 #[cfg(egl_backend)]
298 (Self::Egl(display), Config::Egl(config)) => unsafe {
299 Ok(NotCurrentContext::Egl(display.create_context(config, context_attributes)?))
300 },
301 #[cfg(glx_backend)]
302 (Self::Glx(display), Config::Glx(config)) => unsafe {
303 Ok(NotCurrentContext::Glx(display.create_context(config, context_attributes)?))
304 },
305 #[cfg(wgl_backend)]
306 (Self::Wgl(display), Config::Wgl(config)) => unsafe {
307 Ok(NotCurrentContext::Wgl(display.create_context(config, context_attributes)?))
308 },
309 #[cfg(cgl_backend)]
310 (Self::Cgl(display), Config::Cgl(config)) => unsafe {
311 Ok(NotCurrentContext::Cgl(display.create_context(config, context_attributes)?))
312 },
313 _ => unreachable!(),
314 }
315 }
316
317 unsafe fn create_window_surface(
318 &self,
319 config: &Self::Config,
320 surface_attributes: &SurfaceAttributes<WindowSurface>,
321 ) -> Result<Self::WindowSurface> {
322 match (self, config) {
323 #[cfg(egl_backend)]
324 (Self::Egl(display), Config::Egl(config)) => unsafe {
325 Ok(Surface::Egl(display.create_window_surface(config, surface_attributes)?))
326 },
327 #[cfg(glx_backend)]
328 (Self::Glx(display), Config::Glx(config)) => unsafe {
329 Ok(Surface::Glx(display.create_window_surface(config, surface_attributes)?))
330 },
331 #[cfg(wgl_backend)]
332 (Self::Wgl(display), Config::Wgl(config)) => unsafe {
333 Ok(Surface::Wgl(display.create_window_surface(config, surface_attributes)?))
334 },
335 #[cfg(cgl_backend)]
336 (Self::Cgl(display), Config::Cgl(config)) => unsafe {
337 Ok(Surface::Cgl(display.create_window_surface(config, surface_attributes)?))
338 },
339 _ => unreachable!(),
340 }
341 }
342
343 unsafe fn create_pbuffer_surface(
344 &self,
345 config: &Self::Config,
346 surface_attributes: &SurfaceAttributes<PbufferSurface>,
347 ) -> Result<Self::PbufferSurface> {
348 match (self, config) {
349 #[cfg(egl_backend)]
350 (Self::Egl(display), Config::Egl(config)) => unsafe {
351 Ok(Surface::Egl(display.create_pbuffer_surface(config, surface_attributes)?))
352 },
353 #[cfg(glx_backend)]
354 (Self::Glx(display), Config::Glx(config)) => unsafe {
355 Ok(Surface::Glx(display.create_pbuffer_surface(config, surface_attributes)?))
356 },
357 #[cfg(wgl_backend)]
358 (Self::Wgl(display), Config::Wgl(config)) => unsafe {
359 Ok(Surface::Wgl(display.create_pbuffer_surface(config, surface_attributes)?))
360 },
361 #[cfg(cgl_backend)]
362 (Self::Cgl(display), Config::Cgl(config)) => unsafe {
363 Ok(Surface::Cgl(display.create_pbuffer_surface(config, surface_attributes)?))
364 },
365 _ => unreachable!(),
366 }
367 }
368
369 unsafe fn create_pixmap_surface(
370 &self,
371 config: &Self::Config,
372 surface_attributes: &SurfaceAttributes<PixmapSurface>,
373 ) -> Result<Self::PixmapSurface> {
374 match (self, config) {
375 #[cfg(egl_backend)]
376 (Self::Egl(display), Config::Egl(config)) => unsafe {
377 Ok(Surface::Egl(display.create_pixmap_surface(config, surface_attributes)?))
378 },
379 #[cfg(glx_backend)]
380 (Self::Glx(display), Config::Glx(config)) => unsafe {
381 Ok(Surface::Glx(display.create_pixmap_surface(config, surface_attributes)?))
382 },
383 #[cfg(wgl_backend)]
384 (Self::Wgl(display), Config::Wgl(config)) => unsafe {
385 Ok(Surface::Wgl(display.create_pixmap_surface(config, surface_attributes)?))
386 },
387 #[cfg(cgl_backend)]
388 (Self::Cgl(display), Config::Cgl(config)) => unsafe {
389 Ok(Surface::Cgl(display.create_pixmap_surface(config, surface_attributes)?))
390 },
391 _ => unreachable!(),
392 }
393 }
394
395 fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void {
396 gl_api_dispatch!(self; Self(display) => display.get_proc_address(addr))
397 }
398
399 fn version_string(&self) -> String {
400 gl_api_dispatch!(self; Self(display) => display.version_string())
401 }
402
403 fn supported_features(&self) -> DisplayFeatures {
404 gl_api_dispatch!(self; Self(display) => display.supported_features())
405 }
406}
407
408impl AsRawDisplay for Display {
409 fn raw_display(&self) -> RawDisplay {
410 gl_api_dispatch!(self; Self(display) => display.raw_display())
411 }
412}
413
414impl Sealed for Display {}
415
416/// Preference of the display that should be used.
417pub enum DisplayApiPreference {
418 /// Use only EGL.
419 ///
420 /// The EGL is a cross platform recent OpenGL platform. That being said
421 /// it's usually lacking on Windows and not present at all on macOS
422 /// natively.
423 ///
424 /// Be also aware that some features may not be present with it, like window
425 /// transparency on X11 with mesa.
426 ///
427 /// But despite this issues it should be preferred on at least Linux over
428 /// GLX, given that GLX is phasing away.
429 ///
430 /// # Platform-specific
431 ///
432 /// **Windows:** ANGLE can be used if `libEGL.dll` and `libGLESv2.dll` are
433 /// in the library search path.
434 #[cfg(egl_backend)]
435 Egl,
436
437 /// Use only GLX.
438 ///
439 /// The native GLX platform, it's not very optimal since it's usually tied
440 /// to Xlib. It's know to work fine, but be aware that you must register
441 /// glutin with your X11 error handling callback, since it's a
442 /// per-process global state.
443 ///
444 /// The hook to register glutin error handler in the X11 error handling
445 /// function.
446 #[cfg(glx_backend)]
447 Glx(XlibErrorHookRegistrar),
448
449 /// Use only WGL.
450 ///
451 /// The most spread platform on Windows and what should be used on it by
452 /// default. EGL usually not present there so you'd have to account for that
453 /// and create the window beforehand.
454 ///
455 /// When raw window handle isn't provided the display will lack extensions
456 /// support and most features will be lacking.
457 #[cfg(wgl_backend)]
458 Wgl(Option<raw_window_handle::RawWindowHandle>),
459
460 /// Use only CGL.
461 ///
462 /// The only option on macOS for now.
463 #[cfg(cgl_backend)]
464 Cgl,
465
466 /// Prefer EGL and fallback to GLX.
467 ///
468 /// See [`Egl`] and [`Glx`] to decide what you want.
469 ///
470 /// [`Egl`]: Self::Egl
471 /// [`Glx`]: Self::Glx
472 #[cfg(all(egl_backend, glx_backend))]
473 EglThenGlx(XlibErrorHookRegistrar),
474
475 /// Prefer GLX and fallback to EGL.
476 ///
477 /// See [`Egl`] and [`Glx`] to decide what you want.
478 ///
479 /// [`Egl`]: Self::Egl
480 /// [`Glx`]: Self::Glx
481 #[cfg(all(egl_backend, glx_backend))]
482 GlxThenEgl(XlibErrorHookRegistrar),
483
484 /// Prefer EGL and fallback to WGL.
485 ///
486 /// See [`Egl`] and [`Wgl`] to decide what you want.
487 ///
488 /// [`Egl`]: Self::Egl
489 /// [`Wgl`]: Self::Wgl
490 #[cfg(all(egl_backend, wgl_backend))]
491 EglThenWgl(Option<raw_window_handle::RawWindowHandle>),
492
493 /// Prefer WGL and fallback to EGL.
494 ///
495 /// See [`Egl`] and [`Wgl`] to decide what you want.
496 ///
497 /// [`Egl`]: Self::Egl
498 /// [`Wgl`]: Self::Wgl
499 #[cfg(all(egl_backend, wgl_backend))]
500 WglThenEgl(Option<raw_window_handle::RawWindowHandle>),
501}
502
503impl fmt::Debug for DisplayApiPreference {
504 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
505 let api: &str = match self {
506 #[cfg(egl_backend)]
507 DisplayApiPreference::Egl => "Egl",
508 #[cfg(glx_backend)]
509 DisplayApiPreference::Glx(_) => "Glx",
510 #[cfg(all(egl_backend, glx_backend))]
511 DisplayApiPreference::GlxThenEgl(_) => "GlxThenEgl",
512 #[cfg(all(egl_backend, glx_backend))]
513 DisplayApiPreference::EglThenGlx(_) => "EglThenGlx",
514 #[cfg(wgl_backend)]
515 DisplayApiPreference::Wgl(_) => "Wgl",
516 #[cfg(all(egl_backend, wgl_backend))]
517 DisplayApiPreference::EglThenWgl(_) => "EglThenWgl",
518 #[cfg(all(egl_backend, wgl_backend))]
519 DisplayApiPreference::WglThenEgl(_) => "WglThenEgl",
520 #[cfg(cgl_backend)]
521 DisplayApiPreference::Cgl => "Cgl",
522 };
523
524 f.write_fmt(format_args!("DisplayApiPreference::{api}"))
525 }
526}
527
528bitflags! {
529 /// The features and extensions supported by the [`Display`].
530 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
531 pub struct DisplayFeatures: u32 {
532 /// The display supports creating [`robust`] context.
533 ///
534 /// [`robust`]: crate::context::Robustness
535 const CONTEXT_ROBUSTNESS = 0b0000_0001;
536
537 /// The display supports creating [`no error`] context.
538 ///
539 /// [`no error`]: crate::context::Robustness::NoError
540 const CONTEXT_NO_ERROR = 0b0000_0010;
541
542 /// The display supports [`floating`] pixel formats.
543 ///
544 /// [`floating`]: crate::config::ConfigTemplateBuilder::with_float_pixels
545 const FLOAT_PIXEL_FORMAT = 0b0000_0100;
546
547 /// The display supports changing the [`swap interval`] on surfaces.
548 ///
549 /// [`swap interval`]: crate::surface::GlSurface::set_swap_interval
550 const SWAP_CONTROL = 0b0000_1000;
551
552 /// The display supports creating context with explicit [`release behavior`].
553 ///
554 /// [`release behavior`]: crate::context::ReleaseBehavior
555 const CONTEXT_RELEASE_BEHAVIOR = 0b0001_0000;
556
557 /// The display supports creating OpenGL ES [`context`].
558 ///
559 /// [`context`]: crate::context::ContextApi::Gles
560 const CREATE_ES_CONTEXT = 0b0010_0000;
561
562 /// The display supports pixel formats with [`multisampling`].
563 ///
564 /// [`multisampling`]: crate::config::ConfigTemplateBuilder::with_multisampling
565 const MULTISAMPLING_PIXEL_FORMATS = 0b0100_0000;
566
567 /// The display supports creating surfaces backed by [`SRGB`] framebuffers.
568 ///
569 /// [`SRGB`]: crate::surface::SurfaceAttributesBuilder::with_srgb
570 const SRGB_FRAMEBUFFERS = 0b1000_0000;
571 }
572}
573
574/// Raw GL platform display.
575#[derive(Debug, Clone, Copy, PartialEq, Eq)]
576pub enum RawDisplay {
577 /// Raw EGL display.
578 #[cfg(egl_backend)]
579 Egl(*const std::ffi::c_void),
580
581 /// Raw GLX display.
582 #[cfg(glx_backend)]
583 Glx(*const std::ffi::c_void),
584
585 /// Raw display is WGL.
586 #[cfg(wgl_backend)]
587 Wgl,
588
589 /// Raw display is CGL.
590 #[cfg(cgl_backend)]
591 Cgl,
592}
593