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