luminance_sdl2/
lib.rs

1//! [SDL2](https://crates.io/crates/sdl2) backend for [luminance](https://crates.io/crates/luminance).
2
3#![deny(missing_docs)]
4
5use luminance::context::GraphicsContext;
6use luminance::framebuffer::Framebuffer;
7use luminance::framebuffer::FramebufferError;
8use luminance::texture::Dim2;
9pub use luminance_gl::gl33::StateQueryError;
10use luminance_gl::GL33;
11pub use sdl2;
12use std::fmt;
13use std::os::raw::c_void;
14
15/// Error that can be risen while creating a surface.
16#[non_exhaustive]
17#[derive(Debug)]
18pub enum Sdl2SurfaceError {
19  /// Initialization of the surface went wrong.
20  InitError(String),
21  /// Window creation failed.
22  WindowCreationFailed(sdl2::video::WindowBuildError),
23  /// Failed to create an OpenGL context.
24  GlContextInitFailed(String),
25  /// No available video mode.
26  VideoInitError(String),
27  /// The graphics state is not available.
28  ///
29  /// This error is generated when the initialization code is called on a thread on which the
30  /// graphics state has already been acquired.
31  GraphicsStateError(StateQueryError),
32}
33
34impl fmt::Display for Sdl2SurfaceError {
35  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
36    match *self {
37      Sdl2SurfaceError::InitError(ref e) => write!(f, "initialization error: {}", e),
38      Sdl2SurfaceError::WindowCreationFailed(ref e) => write!(f, "failed to create window: {}", e),
39      Sdl2SurfaceError::GlContextInitFailed(ref e) => {
40        write!(f, "failed to create OpenGL context: {}", e)
41      }
42      Sdl2SurfaceError::VideoInitError(ref e) => {
43        write!(f, "failed to initialize video system: {}", e)
44      }
45      Sdl2SurfaceError::GraphicsStateError(ref e) => {
46        write!(f, "failed to get graphics state: {}", e)
47      }
48    }
49  }
50}
51
52/// A [luminance] GraphicsContext backed by SDL2 and OpenGL 3.3 Core.
53///
54/// ```ignore
55/// use luminance_sdl2::GL33Surface;
56///
57/// let surface = GL33Surface::build_with(|video| video.window("My app", 800, 600))
58///     .expect("failed to create surface");
59///
60/// let sdl = surface.sdl();
61/// ```
62///
63/// [luminance]: https://crates.io/crates/luminance
64pub struct GL33Surface {
65  sdl: sdl2::Sdl,
66  window: sdl2::video::Window,
67  gl: GL33,
68  // This struct needs to stay alive until we are done with OpenGL stuff.
69  _gl_context: sdl2::video::GLContext,
70}
71
72impl GL33Surface {
73  /// Create a new [`GL33Surface`] from a [`sdl2::video::WindowBuilder`].
74  ///
75  /// The callback is passed a reference to [`sdl2::VideoSubsystem`].
76  /// This is your chance to change GL attributes before creating the window with your preferred
77  /// settings.
78  ///
79  /// ```ignore
80  /// use luminance_sdl2::GL33Surface;
81  ///
82  /// let surface = GL33Surface::build_with(|video| {
83  ///     let gl_attr = video.gl_attr();
84  ///     gl_attr.set_multisample_buffers(1);
85  ///     gl_attr.set_multisample_samples(4);
86  ///
87  ///     let mut builder = video.window("My app", 800, 600);
88  ///     builder.fullscreen_desktop();
89  ///     builder
90  /// })
91  ///   .expect("failed to build window");
92  /// ```
93  pub fn build_with<WB>(window_builder: WB) -> Result<Self, Sdl2SurfaceError>
94  where
95    WB: FnOnce(&sdl2::VideoSubsystem) -> sdl2::video::WindowBuilder,
96  {
97    let sdl = sdl2::init().map_err(Sdl2SurfaceError::InitError)?;
98
99    let video_system = sdl.video().map_err(Sdl2SurfaceError::VideoInitError)?;
100
101    let gl_attr = video_system.gl_attr();
102
103    gl_attr.set_context_profile(sdl2::video::GLProfile::Core);
104    gl_attr.set_context_flags().forward_compatible().set();
105    gl_attr.set_context_major_version(3);
106    gl_attr.set_context_minor_version(3);
107
108    let window = window_builder(&video_system)
109      .opengl()
110      .build()
111      .map_err(Sdl2SurfaceError::WindowCreationFailed)?;
112
113    let _gl_context = window
114      .gl_create_context()
115      .map_err(Sdl2SurfaceError::GlContextInitFailed)?;
116
117    gl::load_with(|s| video_system.gl_get_proc_address(s) as *const c_void);
118
119    let gl = GL33::new().map_err(Sdl2SurfaceError::GraphicsStateError)?;
120    let surface = GL33Surface {
121      sdl,
122      window,
123      gl,
124      _gl_context,
125    };
126
127    Ok(surface)
128  }
129
130  /// The entry point to most of the SDL2 API.
131  pub fn sdl(&self) -> &sdl2::Sdl {
132    &self.sdl
133  }
134
135  /// Borrow the underlying SDL2 window of this surface.
136  pub fn window(&self) -> &sdl2::video::Window {
137    &self.window
138  }
139
140  /// Mutably borrow the underlying SDL2 window of this surface.
141  pub fn window_mut(&mut self) -> &mut sdl2::video::Window {
142    &mut self.window
143  }
144
145  /// Get the back buffer.
146  pub fn back_buffer(&mut self) -> Result<Framebuffer<GL33, Dim2, (), ()>, FramebufferError> {
147    let (w, h) = self.window.drawable_size();
148    Framebuffer::back_buffer(self, [w, h])
149  }
150}
151
152unsafe impl GraphicsContext for GL33Surface {
153  type Backend = GL33;
154
155  fn backend(&mut self) -> &mut Self::Backend {
156    &mut self.gl
157  }
158}