glium/backend/glutin/
mod.rs

1#![cfg(feature = "glutin")]
2/*!
3
4Backend implementation for the glutin library
5
6# Features
7
8Only available if the 'glutin' feature is enabled.
9
10*/
11pub use glutin;
12use glutin::surface::Surface;
13use glutin::surface::SwapInterval;
14
15use crate::backend;
16use crate::backend::Backend;
17use crate::backend::Context;
18use crate::context;
19use crate::debug;
20use crate::glutin::context::PossiblyCurrentContext;
21use crate::glutin::display::GetGlDisplay;
22use crate::glutin::prelude::*;
23use crate::glutin::surface::{ResizeableSurface, SurfaceTypeTrait};
24use crate::SwapBuffersError;
25use crate::{Frame, IncompatibleOpenGl};
26use std::cell::RefCell;
27use std::error::Error;
28use std::ffi::CString;
29use std::fmt;
30use std::num::NonZeroU32;
31use std::ops::Deref;
32use std::os::raw::c_void;
33use std::rc::Rc;
34
35#[cfg(feature = "simple_window_builder")]
36pub use self::simple_window_builder::SimpleWindowBuilder;
37
38#[cfg(feature = "simple_window_builder")]
39pub mod simple_window_builder;
40
41/// Wraps a glutin context together with the corresponding Surface.
42/// This is necessary so that we can swap buffers and determine the framebuffer size within glium.
43pub struct ContextSurfacePair<T: SurfaceTypeTrait + ResizeableSurface> {
44    context: PossiblyCurrentContext,
45    surface: glutin::surface::Surface<T>,
46}
47
48impl<T: SurfaceTypeTrait + ResizeableSurface> ContextSurfacePair<T> {
49    fn new(context: PossiblyCurrentContext, surface: glutin::surface::Surface<T>) -> Self {
50        Self { context, surface }
51    }
52
53    #[inline]
54    /// Return the stored framebuffer dimensions
55    pub fn get_framebuffer_dimensions(&self) -> (u32, u32) {
56        (
57            self.surface.width().unwrap(),
58            self.surface.height().unwrap(),
59        )
60    }
61
62    #[inline]
63    /// Swaps the underlying back buffers when the surface is not single buffered.
64    pub fn swap_buffers(&self) -> Result<(), glutin::error::Error> {
65        self.surface.swap_buffers(&self.context)
66    }
67
68    #[inline]
69    /// Set swap interval for the surface.
70    pub fn set_swap_interval(&self, interval: SwapInterval) -> Result<(), glutin::error::Error> {
71        self.surface.set_swap_interval(&self.context, interval)
72    }
73
74    #[inline]
75    /// Resize the associated surface
76    pub fn resize(&self, new_size: (u32, u32)) {
77        // Make sure that no dimension is zero, which happens when minimizing on Windows for example.
78        let width = NonZeroU32::new(new_size.0).unwrap_or(NonZeroU32::new(1).unwrap());
79        let height = NonZeroU32::new(new_size.1).unwrap_or(NonZeroU32::new(1).unwrap());
80        self.surface.resize(&self.context, width, height);
81    }
82}
83
84impl<T: SurfaceTypeTrait + ResizeableSurface> Deref for ContextSurfacePair<T> {
85    type Target = PossiblyCurrentContext;
86    #[inline]
87    fn deref(&self) -> &PossiblyCurrentContext {
88        &self.context
89    }
90}
91
92/// A GL context combined with a facade for drawing upon.
93///
94/// The `Display` uses **glutin** for the **Window** and its associated GL **Context**.
95///
96/// These are stored alongside a glium-specific context.
97#[derive(Clone)]
98pub struct Display<T: SurfaceTypeTrait + ResizeableSurface + 'static> {
99    // contains everything related to the current glium context and its state
100    context: Rc<context::Context>,
101    // The glutin Surface alongside its associated glutin Context.
102    gl_context: Rc<RefCell<Option<ContextSurfacePair<T>>>>,
103}
104
105/// An implementation of the `Backend` trait for glutin.
106#[derive(Clone)]
107pub struct GlutinBackend<T: SurfaceTypeTrait + ResizeableSurface>(
108    Rc<RefCell<Option<ContextSurfacePair<T>>>>,
109);
110
111/// Error that can happen while creating a glium display.
112#[derive(Debug)]
113pub enum DisplayCreationError {
114    /// An error has happened while creating the backend.
115    GlutinError(glutin::error::Error),
116    /// The OpenGL implementation is too old.
117    IncompatibleOpenGl(IncompatibleOpenGl),
118}
119
120impl<T: SurfaceTypeTrait + ResizeableSurface> std::fmt::Debug for Display<T> {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        write!(f, "[glium::backend::glutin::Display]")
123    }
124}
125
126impl<T: SurfaceTypeTrait + ResizeableSurface> Display<T> {
127    /// Create a new glium `Display` from the given context and surface.
128    ///
129    /// Performs a compatibility check to make sure that all core elements of glium are supported
130    /// by the implementation.
131    pub fn new(
132        context: PossiblyCurrentContext,
133        surface: Surface<T>,
134    ) -> Result<Self, DisplayCreationError> {
135        Self::from_context_surface(context, surface).map_err(From::from)
136    }
137
138    /// Create a new glium `Display` from the given context and surface.
139    ///
140    /// Performs a compatibility check to make sure that all core elements of glium are supported
141    /// by the implementation.
142    pub fn from_context_surface(
143        context: PossiblyCurrentContext,
144        surface: Surface<T>,
145    ) -> Result<Self, IncompatibleOpenGl> {
146        Self::with_debug(context, surface, Default::default())
147    }
148
149    /// Create a new glium `Display` from the given context and surface.
150    ///
151    /// This function does the same as `build_glium`, except that the resulting context
152    /// will assume that the current OpenGL context will never change.
153    pub unsafe fn unchecked(
154        context: PossiblyCurrentContext,
155        surface: Surface<T>,
156    ) -> Result<Self, IncompatibleOpenGl> {
157        Self::unchecked_with_debug(context, surface, Default::default())
158    }
159
160    /// The same as the `new` constructor, but allows for specifying debug callback behaviour.
161    pub fn with_debug(
162        context: PossiblyCurrentContext,
163        surface: Surface<T>,
164        debug: debug::DebugCallbackBehavior,
165    ) -> Result<Self, IncompatibleOpenGl> {
166        Self::new_inner(context, surface, debug, true)
167    }
168
169    /// The same as the `unchecked` constructor, but allows for specifying debug callback behaviour.
170    pub unsafe fn unchecked_with_debug(
171        context: PossiblyCurrentContext,
172        surface: Surface<T>,
173        debug: debug::DebugCallbackBehavior,
174    ) -> Result<Self, IncompatibleOpenGl> {
175        Self::new_inner(context, surface, debug, false)
176    }
177
178    fn new_inner(
179        context: PossiblyCurrentContext,
180        surface: Surface<T>,
181        debug: debug::DebugCallbackBehavior,
182        checked: bool,
183    ) -> Result<Self, IncompatibleOpenGl> {
184        let context_surface_pair = ContextSurfacePair::new(context, surface);
185        let gl_window = Rc::new(RefCell::new(Some(context_surface_pair)));
186        let glutin_backend = GlutinBackend(gl_window.clone());
187        let context = unsafe { context::Context::new(glutin_backend, checked, debug) }?;
188        Ok(Display {
189            gl_context: gl_window,
190            context,
191        })
192    }
193
194    /// Resize the underlying surface.
195    #[inline]
196    pub fn resize(&self, new_size: (u32, u32)) {
197        self.gl_context.borrow().as_ref().unwrap().resize(new_size)
198    }
199
200    #[inline]
201    /// Set swap interval for the surface.
202    pub fn set_swap_interval(&self, interval: SwapInterval) -> Result<(), glutin::error::Error> {
203        self.gl_context
204            .borrow()
205            .as_ref()
206            .unwrap()
207            .set_swap_interval(interval)
208    }
209
210    /// Start drawing on the backbuffer.
211    ///
212    /// This function returns a `Frame`, which can be used to draw on it. When the `Frame` is
213    /// destroyed, the buffers are swapped.
214    ///
215    /// Note that destroying a `Frame` is immediate, even if vsync is enabled.
216    #[inline]
217    pub fn draw(&self) -> Frame {
218        let dimensions = self.get_framebuffer_dimensions();
219        Frame::new(self.context.clone(), dimensions)
220    }
221}
222
223impl fmt::Display for DisplayCreationError {
224    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
225        match self {
226            DisplayCreationError::GlutinError(err) => write!(fmt, "{}", err),
227            DisplayCreationError::IncompatibleOpenGl(err) => write!(fmt, "{}", err),
228        }
229    }
230}
231
232impl Error for DisplayCreationError {
233    #[inline]
234    fn source(&self) -> Option<&(dyn Error + 'static)> {
235        match *self {
236            DisplayCreationError::GlutinError(ref err) => Some(err),
237            DisplayCreationError::IncompatibleOpenGl(ref err) => Some(err),
238        }
239    }
240}
241
242impl From<glutin::error::Error> for DisplayCreationError {
243    #[inline]
244    fn from(err: glutin::error::Error) -> DisplayCreationError {
245        DisplayCreationError::GlutinError(err)
246    }
247}
248
249impl From<IncompatibleOpenGl> for DisplayCreationError {
250    #[inline]
251    fn from(err: IncompatibleOpenGl) -> DisplayCreationError {
252        DisplayCreationError::IncompatibleOpenGl(err)
253    }
254}
255
256impl<T: SurfaceTypeTrait + ResizeableSurface> Deref for Display<T> {
257    type Target = Context;
258    #[inline]
259    fn deref(&self) -> &Context {
260        &self.context
261    }
262}
263
264impl<T: SurfaceTypeTrait + ResizeableSurface> backend::Facade for Display<T> {
265    #[inline]
266    fn get_context(&self) -> &Rc<Context> {
267        &self.context
268    }
269}
270
271impl<T: SurfaceTypeTrait + ResizeableSurface> Deref for GlutinBackend<T> {
272    type Target = Rc<RefCell<Option<ContextSurfacePair<T>>>>;
273    #[inline]
274    fn deref(&self) -> &Self::Target {
275        &self.0
276    }
277}
278
279unsafe impl<T: SurfaceTypeTrait + ResizeableSurface> Backend for GlutinBackend<T> {
280    #[inline]
281    fn swap_buffers(&self) -> Result<(), SwapBuffersError> {
282        match self.borrow().as_ref().unwrap().swap_buffers() {
283            Ok(()) => Ok(()),
284            _ => Err(SwapBuffersError::ContextLost),
285        }
286    }
287
288    #[inline]
289    unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
290        let symbol = CString::new(symbol).unwrap();
291        self.borrow()
292            .as_ref()
293            .unwrap()
294            .display()
295            .get_proc_address(&symbol) as *const _
296    }
297
298    #[inline]
299    fn get_framebuffer_dimensions(&self) -> (u32, u32) {
300        self.0
301            .borrow()
302            .as_ref()
303            .unwrap()
304            .get_framebuffer_dimensions()
305    }
306
307    #[inline]
308    fn resize(&self, new_size: (u32, u32)) {
309        self.borrow().as_ref().unwrap().resize(new_size)
310    }
311
312    #[inline]
313    fn set_swap_interval(&self, interval: SwapInterval) {
314        self.borrow()
315            .as_ref()
316            .unwrap()
317            .set_swap_interval(interval)
318            .unwrap()
319    }
320
321    #[inline]
322    fn is_current(&self) -> bool {
323        self.borrow().as_ref().unwrap().is_current()
324    }
325
326    #[inline]
327    unsafe fn make_current(&self) {
328        let pair = self.borrow();
329        pair.as_ref()
330            .unwrap()
331            .context
332            .make_current(&pair.as_ref().unwrap().surface)
333            .unwrap();
334    }
335}