glutin/
surface.rs

1//! A cross platform OpenGL surface representation.
2#![allow(unreachable_patterns)]
3
4use std::marker::PhantomData;
5use std::num::NonZeroU32;
6
7use raw_window_handle::RawWindowHandle;
8
9use crate::context::{PossiblyCurrentContext, PossiblyCurrentGlContext};
10use crate::display::{Display, GetGlDisplay};
11use crate::error::Result;
12use crate::private::{gl_api_dispatch, Sealed};
13
14#[cfg(cgl_backend)]
15use crate::api::cgl::surface::Surface as CglSurface;
16#[cfg(egl_backend)]
17use crate::api::egl::surface::Surface as EglSurface;
18#[cfg(glx_backend)]
19use crate::api::glx::surface::Surface as GlxSurface;
20#[cfg(wgl_backend)]
21use crate::api::wgl::surface::Surface as WglSurface;
22
23/// A trait to group common operations on the surface.
24pub trait GlSurface<T: SurfaceTypeTrait>: Sealed {
25    /// The type of the surface.
26    type SurfaceType: SurfaceTypeTrait;
27    /// The context to access surface data.
28    type Context: PossiblyCurrentGlContext;
29
30    /// The age of the back buffer of that surface. The `0` indicates that the
31    /// buffer is either a new one or we failed to get the information about
32    /// its age. In both cases you must redraw the entire buffer.
33    ///
34    /// # Platform-specific
35    ///
36    /// - **Wayland:** this call will latch the underlying back buffer, meaning
37    ///   that all resize operations will apply after the next
38    ///   [`GlSurface::swap_buffers`].
39    fn buffer_age(&self) -> u32;
40
41    /// The **physical** width of the underlying surface.
42    fn width(&self) -> Option<u32>;
43
44    /// The **physical** height of the underlying surface.
45    ///
46    /// # Platform specific
47    ///
48    /// - **macOS: this will block if your main thread is blocked.**
49    fn height(&self) -> Option<u32>;
50
51    /// Check whether the surface is single buffered.
52    ///
53    /// # Platform specific
54    ///
55    /// - **macOS: this will block if your main thread is blocked.**
56    fn is_single_buffered(&self) -> bool;
57
58    /// Swaps the underlying back buffers when the surface is not single
59    /// buffered.
60    fn swap_buffers(&self, context: &Self::Context) -> Result<()>;
61
62    /// Check whether the surface is current on to the current thread.
63    fn is_current(&self, context: &Self::Context) -> bool;
64
65    /// Check whether the surface is the current draw surface to the current
66    /// thread.
67    fn is_current_draw(&self, context: &Self::Context) -> bool;
68
69    /// Check whether the surface is the current read surface to the current
70    /// thread.
71    fn is_current_read(&self, context: &Self::Context) -> bool;
72
73    /// Set swap interval for the surface.
74    ///
75    /// See [`crate::surface::SwapInterval`] for details.
76    fn set_swap_interval(&self, context: &Self::Context, interval: SwapInterval) -> Result<()>;
77
78    /// Resize the surface to a new size.
79    ///
80    /// This call is for compatibility reasons, on most platforms it's a no-op.
81    /// It's recommended to call this function before doing any rendering and
82    /// performing [`PossiblyCurrentGlContext::make_current`], and
83    /// [`GlSurface::buffer_age`].
84    ///
85    /// # Platform specific
86    ///
87    /// - **Wayland:** resizes the surface;
88    /// - **macOS: this will block if your main thread is blocked;**
89    /// - **Other:** no op.
90    fn resize(&self, context: &Self::Context, width: NonZeroU32, height: NonZeroU32)
91    where
92        Self::SurfaceType: ResizeableSurface;
93}
94
95/// The marker trait to indicate the type of the surface.
96pub trait SurfaceTypeTrait: Sealed {
97    /// Get the type of the surface.
98    fn surface_type() -> SurfaceType;
99}
100
101/// Marker indicating that the surface could be resized.
102pub trait ResizeableSurface: Sealed {}
103
104/// Trait for accessing the raw GL surface.
105pub trait AsRawSurface {
106    /// Get the raw handle to the surface.
107    fn raw_surface(&self) -> RawSurface;
108}
109
110/// Builder to get the required set of attributes initialized before hand.
111#[derive(Default, Debug, Clone)]
112pub struct SurfaceAttributesBuilder<T: SurfaceTypeTrait + Default> {
113    attributes: SurfaceAttributes<T>,
114}
115
116impl<T: SurfaceTypeTrait + Default> SurfaceAttributesBuilder<T> {
117    /// Get new surface attributes.
118    pub fn new() -> Self {
119        Default::default()
120    }
121
122    /// Specify whether the surface should support srgb or not. Passing `None`
123    /// means you don't care.
124    ///
125    /// # Api-specific.
126    ///
127    /// This only controls EGL surfaces, other platforms use the context for
128    /// that.
129    pub fn with_srgb(mut self, srgb: Option<bool>) -> Self {
130        self.attributes.srgb = srgb;
131        self
132    }
133}
134
135impl SurfaceAttributesBuilder<WindowSurface> {
136    /// Specify whether the single buffer should be used instead of double
137    /// buffering. This doesn't guarantee that the resulted buffer will have
138    /// only single buffer, to know that the single buffer is actually used
139    /// query the created surface with [`Surface::is_single_buffered`].
140    ///
141    /// The surface is requested as double buffered by default.
142    ///
143    /// # Api-specific.
144    ///
145    /// This is EGL specific, other platforms use the context for that.
146    pub fn with_single_buffer(mut self, single_buffer: bool) -> Self {
147        self.attributes.single_buffer = single_buffer;
148        self
149    }
150
151    /// Build the surface attributes suitable to create a window surface.
152    pub fn build(
153        mut self,
154        raw_window_handle: RawWindowHandle,
155        width: NonZeroU32,
156        height: NonZeroU32,
157    ) -> SurfaceAttributes<WindowSurface> {
158        self.attributes.raw_window_handle = Some(raw_window_handle);
159        self.attributes.width = Some(width);
160        self.attributes.height = Some(height);
161        self.attributes
162    }
163}
164
165impl SurfaceAttributesBuilder<PbufferSurface> {
166    /// Request the largest pbuffer.
167    pub fn with_largest_pbuffer(mut self, largest_pbuffer: bool) -> Self {
168        self.attributes.largest_pbuffer = largest_pbuffer;
169        self
170    }
171
172    /// The same as in
173    /// [`SurfaceAttributesBuilder::<WindowSurface>::with_single_buffer`].
174    pub fn with_single_buffer(mut self, single_buffer: bool) -> Self {
175        self.attributes.single_buffer = single_buffer;
176        self
177    }
178
179    /// Build the surface attributes suitable to create a pbuffer surface.
180    pub fn build(
181        mut self,
182        width: NonZeroU32,
183        height: NonZeroU32,
184    ) -> SurfaceAttributes<PbufferSurface> {
185        self.attributes.width = Some(width);
186        self.attributes.height = Some(height);
187        self.attributes
188    }
189}
190
191impl SurfaceAttributesBuilder<PixmapSurface> {
192    /// Build the surface attributes suitable to create a pixmap surface.
193    pub fn build(mut self, native_pixmap: NativePixmap) -> SurfaceAttributes<PixmapSurface> {
194        self.attributes.native_pixmap = Some(native_pixmap);
195        self.attributes
196    }
197}
198
199/// Attributes which are used for creating a particular surface.
200#[derive(Default, Debug, Clone)]
201pub struct SurfaceAttributes<T: SurfaceTypeTrait> {
202    pub(crate) srgb: Option<bool>,
203    pub(crate) single_buffer: bool,
204    pub(crate) width: Option<NonZeroU32>,
205    pub(crate) height: Option<NonZeroU32>,
206    pub(crate) largest_pbuffer: bool,
207    pub(crate) raw_window_handle: Option<RawWindowHandle>,
208    pub(crate) native_pixmap: Option<NativePixmap>,
209    _ty: PhantomData<T>,
210}
211
212/// Marker that used to type-gate methods for window.
213#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
214pub struct WindowSurface;
215
216impl SurfaceTypeTrait for WindowSurface {
217    fn surface_type() -> SurfaceType {
218        SurfaceType::Window
219    }
220}
221
222impl ResizeableSurface for WindowSurface {}
223
224impl Sealed for WindowSurface {}
225
226/// Marker that used to type-gate methods for pbuffer.
227#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
228pub struct PbufferSurface;
229
230impl SurfaceTypeTrait for PbufferSurface {
231    fn surface_type() -> SurfaceType {
232        SurfaceType::Pbuffer
233    }
234}
235
236impl Sealed for PbufferSurface {}
237
238/// Marker that used to type-gate methods for pixmap.
239#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
240pub struct PixmapSurface;
241
242impl SurfaceTypeTrait for PixmapSurface {
243    fn surface_type() -> SurfaceType {
244        SurfaceType::Pixmap
245    }
246}
247
248impl Sealed for PixmapSurface {}
249
250/// The underlying type of the surface.
251#[derive(Debug, Clone, Copy)]
252pub enum SurfaceType {
253    /// The window surface.
254    Window,
255
256    /// Pixmap surface.
257    Pixmap,
258
259    /// Pbuffer surface.
260    Pbuffer,
261}
262
263/// The GL surface that is used for rendering.
264///
265/// Similar to the context, the GL surface is [`Send`] but not [`Sync`]. This
266/// means it could be sent to a different thread as long as it is not current on
267/// another thread.
268///
269/// ```no_run
270/// fn test_send<T: Send>() {}
271/// test_send::<glutin::surface::Surface<glutin::surface::WindowSurface>>();
272/// ```
273/// ```compile_fail
274/// fn test_sync<T: Sync>() {}
275/// test_sync::<glutin::surface::Surface<glutin::surface::WindowSurface>>();
276/// ```
277#[derive(Debug)]
278pub enum Surface<T: SurfaceTypeTrait> {
279    /// The EGL surface.
280    #[cfg(egl_backend)]
281    Egl(EglSurface<T>),
282
283    /// The GLX surface.
284    #[cfg(glx_backend)]
285    Glx(GlxSurface<T>),
286
287    /// The WGL surface.
288    #[cfg(wgl_backend)]
289    Wgl(WglSurface<T>),
290
291    /// The CGL surface.
292    #[cfg(cgl_backend)]
293    Cgl(CglSurface<T>),
294}
295
296impl<T: SurfaceTypeTrait> GlSurface<T> for Surface<T> {
297    type Context = PossiblyCurrentContext;
298    type SurfaceType = T;
299
300    fn buffer_age(&self) -> u32 {
301        gl_api_dispatch!(self; Self(surface) => surface.buffer_age())
302    }
303
304    fn width(&self) -> Option<u32> {
305        gl_api_dispatch!(self; Self(surface) => surface.width())
306    }
307
308    fn height(&self) -> Option<u32> {
309        gl_api_dispatch!(self; Self(surface) => surface.height())
310    }
311
312    fn is_single_buffered(&self) -> bool {
313        gl_api_dispatch!(self; Self(surface) => surface.is_single_buffered())
314    }
315
316    fn swap_buffers(&self, context: &Self::Context) -> Result<()> {
317        match (self, context) {
318            #[cfg(egl_backend)]
319            (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => {
320                surface.swap_buffers(context)
321            },
322            #[cfg(glx_backend)]
323            (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => {
324                surface.swap_buffers(context)
325            },
326            #[cfg(cgl_backend)]
327            (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => {
328                surface.swap_buffers(context)
329            },
330            #[cfg(wgl_backend)]
331            (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => {
332                surface.swap_buffers(context)
333            },
334            _ => unreachable!(),
335        }
336    }
337
338    fn set_swap_interval(&self, context: &Self::Context, interval: SwapInterval) -> Result<()> {
339        match (self, context) {
340            #[cfg(egl_backend)]
341            (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => {
342                surface.set_swap_interval(context, interval)
343            },
344            #[cfg(glx_backend)]
345            (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => {
346                surface.set_swap_interval(context, interval)
347            },
348            #[cfg(cgl_backend)]
349            (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => {
350                surface.set_swap_interval(context, interval)
351            },
352            #[cfg(wgl_backend)]
353            (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => {
354                surface.set_swap_interval(context, interval)
355            },
356            _ => unreachable!(),
357        }
358    }
359
360    fn is_current(&self, context: &Self::Context) -> bool {
361        match (self, context) {
362            #[cfg(egl_backend)]
363            (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => {
364                surface.is_current(context)
365            },
366            #[cfg(glx_backend)]
367            (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => {
368                surface.is_current(context)
369            },
370            #[cfg(cgl_backend)]
371            (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => {
372                surface.is_current(context)
373            },
374            #[cfg(wgl_backend)]
375            (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => {
376                surface.is_current(context)
377            },
378            _ => unreachable!(),
379        }
380    }
381
382    fn is_current_draw(&self, context: &Self::Context) -> bool {
383        match (self, context) {
384            #[cfg(egl_backend)]
385            (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => {
386                surface.is_current_draw(context)
387            },
388            #[cfg(glx_backend)]
389            (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => {
390                surface.is_current_draw(context)
391            },
392            #[cfg(cgl_backend)]
393            (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => {
394                surface.is_current_draw(context)
395            },
396            #[cfg(wgl_backend)]
397            (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => {
398                surface.is_current_draw(context)
399            },
400            _ => unreachable!(),
401        }
402    }
403
404    fn is_current_read(&self, context: &Self::Context) -> bool {
405        match (self, context) {
406            #[cfg(egl_backend)]
407            (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => {
408                surface.is_current_read(context)
409            },
410            #[cfg(glx_backend)]
411            (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => {
412                surface.is_current_read(context)
413            },
414            #[cfg(cgl_backend)]
415            (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => {
416                surface.is_current_read(context)
417            },
418            #[cfg(wgl_backend)]
419            (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => {
420                surface.is_current_read(context)
421            },
422            _ => unreachable!(),
423        }
424    }
425
426    fn resize(&self, context: &Self::Context, width: NonZeroU32, height: NonZeroU32)
427    where
428        Self::SurfaceType: ResizeableSurface,
429    {
430        match (self, context) {
431            #[cfg(egl_backend)]
432            (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => {
433                surface.resize(context, width, height)
434            },
435            #[cfg(glx_backend)]
436            (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => {
437                surface.resize(context, width, height)
438            },
439            #[cfg(cgl_backend)]
440            (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => {
441                surface.resize(context, width, height)
442            },
443            #[cfg(wgl_backend)]
444            (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => {
445                surface.resize(context, width, height)
446            },
447            _ => unreachable!(),
448        }
449    }
450}
451
452impl<T: SurfaceTypeTrait> GetGlDisplay for Surface<T> {
453    type Target = Display;
454
455    fn display(&self) -> Self::Target {
456        gl_api_dispatch!(self; Self(surface) => surface.display(); as Display)
457    }
458}
459
460impl<T: SurfaceTypeTrait> AsRawSurface for Surface<T> {
461    fn raw_surface(&self) -> RawSurface {
462        gl_api_dispatch!(self; Self(surface) => surface.raw_surface())
463    }
464}
465
466impl<T: SurfaceTypeTrait> Sealed for Surface<T> {}
467
468/// A swap interval.
469///
470/// The default swap interval for your [`Surface`] is platform-dependent. For
471/// example, on EGL it is `1` by default, but on GLX it is `0` by default.
472///
473/// Please note that your application's desired swap interval may be overridden
474/// by external, driver-specific configuration, which means that you can't know
475/// in advance whether [`crate::surface::GlSurface::swap_buffers`] will block
476/// or not.
477///
478/// # Platform specific
479///
480/// - **Wayland:** when the window is hidden and [`SwapInterval::Wait`] is used
481///   [`GlSurface::swap_buffers`] and any functions based on it may block until
482///   the window is visible again. Using this variant is not recommended on
483///   Wayland and instead the throttling should be performed by [`frame
484///   callbacks`].
485///
486/// [`frame callbacks`]: https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_surface-request-frame
487#[derive(Debug, Clone, Copy, PartialEq, Eq)]
488pub enum SwapInterval {
489    /// When this variant is used calling
490    /// [`crate::surface::GlSurface::swap_buffers()`] will not block.
491    DontWait,
492
493    /// The swap is synchronized to the `n`'th video frame. This is typically
494    /// set to `1` to enable vsync and prevent screen tearing.
495    Wait(NonZeroU32),
496}
497
498/// A platform native pixmap.
499#[derive(Debug, Clone, Copy, PartialEq, Eq)]
500pub enum NativePixmap {
501    /// XID of X11 pixmap.
502    XlibPixmap(std::os::raw::c_ulong),
503
504    /// XID of X11 pixmap from xcb.
505    XcbPixmap(u32),
506
507    /// HBITMAP handle for windows bitmap.
508    WindowsPixmap(isize),
509}
510
511/// Handle to the raw OpenGL surface.
512#[derive(Debug, Clone, Copy, PartialEq, Eq)]
513pub enum RawSurface {
514    /// A pointer to EGLSurface.
515    #[cfg(egl_backend)]
516    Egl(*const std::ffi::c_void),
517
518    /// GLXDrawable.
519    #[cfg(glx_backend)]
520    Glx(u64),
521
522    /// Either a `HWND` or `HPBUFFEREXT` depending on [`SurfaceType`].
523    #[cfg(wgl_backend)]
524    Wgl(*const std::ffi::c_void),
525
526    /// Pointer to `NSView`.
527    #[cfg(cgl_backend)]
528    Cgl(*const std::ffi::c_void),
529}
530
531/// The rect that is being used in various surface operations.
532///
533/// The origin is in the bottom left of the surface.
534#[repr(C)]
535#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
536pub struct Rect {
537    /// `X` of the origin.
538    pub x: i32,
539    /// `Y` of the origin.
540    pub y: i32,
541    /// Rect width.
542    pub width: i32,
543    /// Rect height.
544    pub height: i32,
545}
546
547impl Rect {
548    /// Helper to simplify rectangle creation.
549    pub fn new(x: i32, y: i32, width: i32, height: i32) -> Self {
550        Self { x, y, width, height }
551    }
552}