1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//! # luminance windowing
//!
//! This is the base, abstract crate for windowing common types and functions in luminance. The
//! `luminance` crate provides you with abstracting over OpenGL, but it doesn’t give you a way to
//! create an OpenGL context. This is due to the fact that creating and managing OpenGL contexts is
//! tightly related to the type of application you target. Typical PC applications might need
//! something like **GLFW** or **glutin** whilst others will directly bind to **X11** or **Windows
//! API**. Several crates – `luminance-*` – exist to solve that problem. They all provide a
//! different implementation for a simple need: opening an OpenGL context, opening a window and
//! manage events. In theory, you could even have a `luminance-gtk` or `luminance-qt` to embed
//! `luminance` in surfaces in those libraries.
//!
//! # What’s included
//!
//! This crate exposes several important types that all backends must use. Among them, you’ll find:
//!
//! - `WindowDim`: abstraction over the dimension of a window and its mode (windowed, fullscreen, fullscreen
//!   restricted).
//! - `WindowOpt`: an opaque type giving access to hints to customize the window integration, such as whether
//!   the cursor should be hidden or not.
//!
//! The `Device` trait must be implemented by a backend so that an application is completely
//! agnostic of the backend. This trait defines several basic methods that will help you to:
//!
//! - Retrieve the dimension of the window / framebuffer.
//! - Iterate over the system events captured by your application.
//! - Draw and swap the buffer chain.

#![deny(missing_docs)]

use luminance::context::GraphicsContext;
use luminance::framebuffer::Framebuffer;
use luminance::texture::{Dim2, Flat};

/// Dimension metrics.
///
///   - `Windowed(width, height)` opens in windowed mode with the wished resolution.
///   - `Fullscreen` opens in fullscreen mode by using the primary monitor resolution.
///   - `FullscreenRestricted(width, height)` is a mix between `Windowed(width, height)` and `Fullscreen`. It
///     opens in fullscreen mode by using the wished resolution.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WindowDim {
  /// Windowed mode.
  Windowed(u32, u32),
  /// Fullscreen mode (adapt to your screen).
  Fullscreen,
  /// Fullscreen mode with restricted viewport dimension.
  FullscreenRestricted(u32, u32),
}

/// Cursor mode.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CursorMode {
  /// The cursor is always visible.
  Visible,
  /// The cursor exists yet has been disabled.
  Invisible,
  /// The cursor is disabled.
  Disabled,
}

/// Different window options.
///
/// Feel free to look at the different methods available to tweak the options. You may want to start
/// with `default()` though.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct WindowOpt {
  cursor_mode: CursorMode,
  num_samples: Option<u32>,
}

impl Default for WindowOpt {
  /// Defaults:
  ///
  /// - `cursor_mode` set to `CursorMode::Visible`.
  /// - `num_samples` set to `None`.
  fn default() -> Self {
    WindowOpt {
      cursor_mode: CursorMode::Visible,
      num_samples: None,
    }
  }
}

impl WindowOpt {
  /// Hide, unhide or disable the cursor. Default to `CursorMode::Visible`.
  #[inline]
  pub fn set_cursor_mode(self, mode: CursorMode) -> Self {
    WindowOpt {
      cursor_mode: mode,
      ..self
    }
  }

  /// Get the cursor mode.
  #[inline]
  pub fn cursor_mode(&self) -> CursorMode {
    self.cursor_mode
  }

  /// Set the number of samples to use for multisampling.
  ///
  /// Pass `None` to disable multisampling.
  #[inline]
  pub fn set_num_samples<S>(self, samples: S) -> Self
  where
    S: Into<Option<u32>>,
  {
    WindowOpt {
      num_samples: samples.into(),
      ..self
    }
  }

  /// Get the number of samples to use in multisampling, if any.
  #[inline]
  pub fn num_samples(&self) -> Option<u32> {
    self.num_samples
  }
}

/// Rendering surface.
///
/// This type holds anything related to rendering. The interface is straight forward, so feel
/// free to have a look around.
pub trait Surface: GraphicsContext + Sized {
  /// Type of events.
  type Event;

  /// Type of surface errors.
  type Error;

  /// Create a surface along with its associated event stream and bootstrap a luminance environment
  /// that lives as long as the surface lives.
  fn new(dim: WindowDim, title: &str, win_opt: WindowOpt) -> Result<Self, Self::Error>;

  /// Retrieve opitions and allow editing them.
  fn opts(&self) -> &WindowOpt;

  /// Change the cursor mode.
  fn set_cursor_mode(&mut self, mode: CursorMode) -> &mut Self;

  /// Change the multisampling state.
  fn set_num_samples<S>(&mut self, samples: S) -> &mut Self
  where
    S: Into<Option<u32>>;

  /// Size of the surface’s framebuffer.
  fn size(&self) -> [u32; 2];

  /// Width of the surface’s framebuffer.
  ///
  /// # Defaults
  ///
  /// Defaults to `.size()[0]`.
  fn width(&self) -> u32 {
    self.size()[0]
  }

  /// Height of the surface’s framebuffer.
  ///
  /// # Defaults
  ///
  /// Defaults to `.size()[1]`.
  fn height(&self) -> u32 {
    self.size()[1]
  }

  // FIXME: existential impl trait
  /// Get an iterator over events by blocking until the first event happens.
  fn wait_events<'a>(&'a mut self) -> Box<dyn Iterator<Item = Self::Event> + 'a>;

  // FIXME: existential impl trait
  /// Get an iterator over events without blocking if no event is there.
  fn poll_events<'a>(&'a mut self) -> Box<dyn Iterator<Item = Self::Event> + 'a>;

  /// Swap the back and front buffers.
  fn swap_buffers(&mut self);

  /// Get access to the back buffer.
  fn back_buffer(&mut self) -> Result<Framebuffer<Flat, Dim2, (), ()>, Self::Error> {
    Ok(Framebuffer::back_buffer(self, self.size()))
  }
}