Skip to main content

luminance_glutin/
lib.rs

1//! The [glutin](https://crates.io/crates/glutin) platform crate for [luminance](https://crates.io/crates/luminance).
2
3#![deny(missing_docs)]
4
5use gl;
6use glutin::{
7  event_loop::EventLoop, window::WindowBuilder, Api, ContextBuilder, ContextError, CreationError,
8  GlProfile, GlRequest, NotCurrent, PossiblyCurrent, WindowedContext,
9};
10use luminance::context::GraphicsContext;
11use luminance::framebuffer::{Framebuffer, FramebufferError};
12use luminance::texture::Dim2;
13pub use luminance_gl::gl33::StateQueryError;
14use luminance_gl::GL33;
15use std::error;
16use std::fmt;
17use std::os::raw::c_void;
18
19/// Error that might occur when creating a Glutin surface.
20#[derive(Debug)]
21pub enum GlutinError {
22  /// Something went wrong when creating the Glutin surface. The carried [`CreationError`] provides
23  /// more information.
24  CreationError(CreationError),
25  /// OpenGL context error.
26  ContextError(ContextError),
27  /// Graphics state error that might occur when querying the initial state.
28  GraphicsStateError(StateQueryError),
29}
30
31impl fmt::Display for GlutinError {
32  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
33    match *self {
34      GlutinError::CreationError(ref e) => write!(f, "Glutin surface creation error: {}", e),
35      GlutinError::ContextError(ref e) => write!(f, "Glutin OpenGL context creation error: {}", e),
36      GlutinError::GraphicsStateError(ref e) => {
37        write!(f, "OpenGL graphics state initialization error: {}", e)
38      }
39    }
40  }
41}
42
43impl error::Error for GlutinError {
44  fn source(&self) -> Option<&(dyn error::Error + 'static)> {
45    match self {
46      GlutinError::CreationError(e) => Some(e),
47      GlutinError::ContextError(e) => Some(e),
48      GlutinError::GraphicsStateError(e) => Some(e),
49    }
50  }
51}
52
53impl From<CreationError> for GlutinError {
54  fn from(e: CreationError) -> Self {
55    GlutinError::CreationError(e)
56  }
57}
58
59impl From<ContextError> for GlutinError {
60  fn from(e: ContextError) -> Self {
61    GlutinError::ContextError(e)
62  }
63}
64
65impl From<StateQueryError> for GlutinError {
66  fn from(e: StateQueryError) -> Self {
67    GlutinError::GraphicsStateError(e)
68  }
69}
70
71/// The Glutin surface.
72///
73/// You want to create such an object in order to use any [luminance] construct.
74///
75/// [luminance]: https://crates.io/crates/luminance
76pub struct GlutinSurface {
77  /// The windowed context.
78  pub ctx: WindowedContext<PossiblyCurrent>,
79  /// OpenGL 3.3 state.
80  gl: GL33,
81}
82
83unsafe impl GraphicsContext for GlutinSurface {
84  type Backend = GL33;
85
86  fn backend(&mut self) -> &mut Self::Backend {
87    &mut self.gl
88  }
89}
90
91impl GlutinSurface {
92  /// Create a new [`GlutinSurface`] by consuming a [`WindowBuilder`].
93  ///
94  /// This is an alternative method to [`new_gl33`] that is more flexible as you have access to the
95  /// whole `glutin` types.
96  ///
97  /// `window_builder` is the default object when passed to your closure and `ctx_builder` is
98  /// already initialized for the OpenGL context (you’re not supposed to change it!).
99  ///
100  /// [`new_gl33`]: crate::GlutinSurface::new_gl33
101  pub fn new_gl33_from_builders<'a, WB, CB>(
102    window_builder: WB,
103    ctx_builder: CB,
104  ) -> Result<(Self, EventLoop<()>), GlutinError>
105  where
106    WB: FnOnce(&mut EventLoop<()>, WindowBuilder) -> WindowBuilder,
107    CB:
108      FnOnce(&mut EventLoop<()>, ContextBuilder<'a, NotCurrent>) -> ContextBuilder<'a, NotCurrent>,
109  {
110    let mut event_loop = EventLoop::new();
111
112    let window_builder = window_builder(&mut event_loop, WindowBuilder::new());
113
114    let windowed_ctx = ctx_builder(
115      &mut event_loop,
116      ContextBuilder::new()
117        .with_gl(GlRequest::Specific(Api::OpenGl, (3, 3)))
118        .with_gl_profile(GlProfile::Core),
119    )
120    .build_windowed(window_builder, &event_loop)?;
121
122    let ctx = unsafe { windowed_ctx.make_current().map_err(|(_, e)| e)? };
123
124    // init OpenGL
125    gl::load_with(|s| ctx.get_proc_address(s) as *const c_void);
126
127    ctx.window().set_visible(true);
128
129    let gl = GL33::new().map_err(GlutinError::GraphicsStateError)?;
130    let surface = GlutinSurface { ctx, gl };
131
132    Ok((surface, event_loop))
133  }
134
135  /// Create a new [`GlutinSurface`] from scratch.
136  pub fn new_gl33(
137    window_builder: WindowBuilder,
138    samples: u16,
139  ) -> Result<(Self, EventLoop<()>), GlutinError> {
140    let event_loop = EventLoop::new();
141
142    let windowed_ctx = ContextBuilder::new()
143      .with_gl(GlRequest::Specific(Api::OpenGl, (3, 3)))
144      .with_gl_profile(GlProfile::Core)
145      .with_multisampling(samples)
146      .with_double_buffer(Some(true))
147      .build_windowed(window_builder, &event_loop)?;
148
149    let ctx = unsafe { windowed_ctx.make_current().map_err(|(_, e)| e)? };
150
151    // init OpenGL
152    gl::load_with(|s| ctx.get_proc_address(s) as *const c_void);
153
154    ctx.window().set_visible(true);
155
156    let gl = GL33::new().map_err(GlutinError::GraphicsStateError)?;
157    let surface = GlutinSurface { ctx, gl };
158
159    Ok((surface, event_loop))
160  }
161
162  /// Get the underlying size (in physical pixels) of the surface.
163  ///
164  /// This is equivalent to getting the inner size of the windowed context and converting it to
165  /// a physical size by using the HiDPI factor of the windowed context.
166  pub fn size(&self) -> [u32; 2] {
167    let size = self.ctx.window().inner_size();
168    [size.width, size.height]
169  }
170
171  /// Get access to the back buffer.
172  pub fn back_buffer(&mut self) -> Result<Framebuffer<GL33, Dim2, (), ()>, FramebufferError> {
173    Framebuffer::back_buffer(self, self.size())
174  }
175
176  /// Swap the back and front buffers.
177  pub fn swap_buffers(&mut self) {
178    let _ = self.ctx.swap_buffers();
179  }
180}