/ src / platform / mod.rs
mod.rs
  1  #![allow(dead_code)]
  2  use std::io;
  3  
  4  use std::fmt;
  5  
  6  #[cfg(not(feature = "glfw"))]
  7  #[path = "dummy.rs"]
  8  pub mod backend;
  9  
 10  #[cfg(feature = "glfw")]
 11  #[path = "glfw.rs"]
 12  pub mod backend;
 13  
 14  /// Initialize the platform.
 15  pub fn init(
 16      title: &str,
 17      w: u32,
 18      h: u32,
 19      hints: &[WindowHint],
 20      context: GraphicsContext,
 21  ) -> io::Result<(backend::Window, backend::Events)> {
 22      backend::init(title, w, h, hints, context)
 23  }
 24  
 25  #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 26  pub enum GraphicsContext {
 27      None,
 28      Gl,
 29  }
 30  
 31  #[derive(Debug, Copy, Clone)]
 32  pub enum WindowHint {
 33      Resizable(bool),
 34      Visible(bool),
 35  }
 36  
 37  /// Describes an event from a `Window`.
 38  #[derive(Clone, Debug, PartialEq)]
 39  pub enum WindowEvent {
 40      /// The size of the window has changed. Contains the client area's new dimensions.
 41      Resized(LogicalSize),
 42  
 43      /// The position of the window has changed. Contains the window's new position.
 44      Moved(LogicalPosition),
 45  
 46      /// The window was minimized.
 47      Minimized,
 48  
 49      /// The window was restored after having been minimized.
 50      Restored,
 51  
 52      /// The window has been requested to close.
 53      CloseRequested,
 54  
 55      /// The window has been destroyed.
 56      Destroyed,
 57  
 58      /// The window received a unicode character.
 59      ReceivedCharacter(char, ModifiersState),
 60  
 61      /// The window gained or lost focus.
 62      Focused(bool),
 63  
 64      /// An event from the keyboard has been received.
 65      KeyboardInput(KeyboardInput),
 66  
 67      /// The cursor has moved on the window.
 68      CursorMoved {
 69          /// Coords in pixels relative to the top-left corner of the window.
 70          position: LogicalPosition,
 71      },
 72  
 73      /// The cursor has entered the window.
 74      CursorEntered,
 75  
 76      /// The cursor has left the window.
 77      CursorLeft,
 78  
 79      /// A mouse button press has been received.
 80      MouseInput {
 81          state: InputState,
 82          button: MouseButton,
 83          modifiers: ModifiersState,
 84      },
 85  
 86      /// The mouse wheel has been used.
 87      MouseWheel { delta: LogicalDelta },
 88  
 89      /// The OS or application has requested that the window be redrawn.
 90      RedrawRequested,
 91  
 92      /// There are no more inputs to process, the application can do work.
 93      Ready,
 94  
 95      /// The content scale factor of the window has changed.  For example,
 96      /// the window was moved to a higher DPI screen.
 97      ScaleFactorChanged(f64),
 98  
 99      /// No-op event, for events we don't handle.
100      Noop,
101  }
102  
103  impl WindowEvent {
104      /// Events that are triggered by user input.
105      pub fn is_input(&self) -> bool {
106          matches!(
107              self,
108              Self::Resized(_)
109                  | Self::Moved(_)
110                  | Self::Minimized
111                  | Self::Restored
112                  | Self::CloseRequested
113                  | Self::Destroyed
114                  | Self::ReceivedCharacter(_, _)
115                  | Self::Focused(_)
116                  | Self::KeyboardInput(_)
117                  | Self::CursorMoved { .. }
118                  | Self::CursorEntered
119                  | Self::CursorLeft
120                  | Self::MouseInput { .. }
121                  | Self::ScaleFactorChanged(_)
122          )
123      }
124  }
125  
126  /// Describes a keyboard input event.
127  #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
128  pub struct KeyboardInput {
129      pub state: InputState,
130      pub key: Option<Key>,
131      pub modifiers: ModifiersState,
132  }
133  
134  /// Describes the input state of a key.
135  #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
136  pub enum InputState {
137      Pressed,
138      Released,
139      Repeated,
140  }
141  
142  /// Describes a mouse button.
143  #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
144  pub enum MouseButton {
145      Left,
146      Right,
147      Middle,
148      Other(u8),
149  }
150  
151  /// Symbolic name for a keyboard key.
152  #[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
153  #[repr(u32)]
154  #[rustfmt::skip]
155  pub enum Key {
156      // Number keys.
157      Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0,
158  
159      // Alpha keys.
160      A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
161  
162      // Arrow keys.
163      Left, Up, Right, Down,
164  
165      // Control characters.
166      Backspace, Return, Space, Tab,
167      Escape, Insert, Home, Delete, End, PageDown, PageUp,
168  
169      // Punctuation.
170      Apostrophe, Grave, Caret, Comma, Period, Colon, Semicolon,
171      LBracket, RBracket,
172      Slash, Backslash,
173  
174      // Modifiers.
175      Alt, Control, Shift,
176  
177      // Math keys.
178      Equal, Minus,
179  
180      // Key is unknown/unsupported.
181      Unknown,
182  }
183  
184  impl From<char> for Key {
185      #[rustfmt::skip]
186      fn from(c: char) -> Self {
187          match c {
188              '0' => Key::Num0, '1' => Key::Num1, '2' => Key::Num2,
189              '3' => Key::Num3, '4' => Key::Num4, '5' => Key::Num5,
190              '6' => Key::Num6, '7' => Key::Num7, '8' => Key::Num8,
191              '9' => Key::Num9,
192  
193              'a' => Key::A, 'b' => Key::B, 'c' => Key::C, 'd' => Key::D,
194              'e' => Key::E, 'f' => Key::F, 'g' => Key::G, 'h' => Key::H,
195              'i' => Key::I, 'j' => Key::J, 'k' => Key::K, 'l' => Key::L,
196              'm' => Key::M, 'n' => Key::N, 'o' => Key::O, 'p' => Key::P,
197              'q' => Key::Q, 'r' => Key::R, 's' => Key::S, 't' => Key::T,
198              'u' => Key::U, 'v' => Key::V, 'w' => Key::W, 'x' => Key::X,
199              'y' => Key::Y, 'z' => Key::Z,
200  
201              '/' => Key::Slash, '[' => Key::LBracket, ']' => Key::RBracket,
202              '`' => Key::Grave, ',' => Key::Comma, '.' => Key::Period,
203              '=' => Key::Equal, '-' => Key::Minus, '\'' => Key::Apostrophe,
204              ';' => Key::Semicolon, ':' => Key::Colon, ' ' => Key::Space,
205              '\\' => Key::Backslash,
206              _ => Key::Unknown,
207          }
208      }
209  }
210  
211  impl fmt::Display for Key {
212      fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213          match self {
214              Key::A => "a".fmt(f),
215              Key::B => "b".fmt(f),
216              Key::C => "c".fmt(f),
217              Key::D => "d".fmt(f),
218              Key::E => "e".fmt(f),
219              Key::F => "f".fmt(f),
220              Key::G => "g".fmt(f),
221              Key::H => "h".fmt(f),
222              Key::I => "i".fmt(f),
223              Key::J => "j".fmt(f),
224              Key::K => "k".fmt(f),
225              Key::L => "l".fmt(f),
226              Key::M => "m".fmt(f),
227              Key::N => "n".fmt(f),
228              Key::O => "o".fmt(f),
229              Key::P => "p".fmt(f),
230              Key::Q => "q".fmt(f),
231              Key::R => "r".fmt(f),
232              Key::S => "s".fmt(f),
233              Key::T => "t".fmt(f),
234              Key::U => "u".fmt(f),
235              Key::V => "v".fmt(f),
236              Key::W => "w".fmt(f),
237              Key::X => "x".fmt(f),
238              Key::Y => "y".fmt(f),
239              Key::Z => "z".fmt(f),
240              Key::Num0 => "0".fmt(f),
241              Key::Num1 => "1".fmt(f),
242              Key::Num2 => "2".fmt(f),
243              Key::Num3 => "3".fmt(f),
244              Key::Num4 => "4".fmt(f),
245              Key::Num5 => "5".fmt(f),
246              Key::Num6 => "6".fmt(f),
247              Key::Num7 => "7".fmt(f),
248              Key::Num8 => "8".fmt(f),
249              Key::Num9 => "9".fmt(f),
250              Key::LBracket => "[".fmt(f),
251              Key::RBracket => "]".fmt(f),
252              Key::Comma => ",".fmt(f),
253              Key::Period => ".".fmt(f),
254              Key::Slash => "/".fmt(f),
255              Key::Backslash => "\\".fmt(f),
256              Key::Apostrophe => "'".fmt(f),
257              Key::Control => "<ctrl>".fmt(f),
258              Key::Shift => "<shift>".fmt(f),
259              Key::Alt => "<alt>".fmt(f),
260              Key::Up => "<up>".fmt(f),
261              Key::Down => "<down>".fmt(f),
262              Key::Left => "<left>".fmt(f),
263              Key::Right => "<right>".fmt(f),
264              Key::Return => "<return>".fmt(f),
265              Key::Backspace => "<backspace>".fmt(f),
266              Key::Space => "<space>".fmt(f),
267              Key::Tab => "<tab>".fmt(f),
268              Key::Escape => "<esc>".fmt(f),
269              Key::Insert => "<insert>".fmt(f),
270              Key::Delete => "<delete>".fmt(f),
271              Key::Home => "<home>".fmt(f),
272              Key::PageUp => "<pgup>".fmt(f),
273              Key::PageDown => "<pgdown>".fmt(f),
274              Key::Grave => "`".fmt(f),
275              Key::Caret => "^".fmt(f),
276              Key::End => "<end>".fmt(f),
277              Key::Colon => ":".fmt(f),
278              Key::Semicolon => ";".fmt(f),
279              Key::Equal => "=".fmt(f),
280              Key::Minus => "-".fmt(f),
281              _ => "???".fmt(f),
282          }
283      }
284  }
285  
286  impl Key {
287      pub fn is_modifier(self) -> bool {
288          matches!(self, Key::Alt | Key::Control | Key::Shift)
289      }
290  }
291  
292  /// Represents the current state of the keyboard modifiers
293  #[derive(Default, Debug, Hash, PartialEq, Eq, Clone, Copy)]
294  pub struct ModifiersState {
295      /// The "shift" key
296      pub shift: bool,
297      /// The "control" key
298      pub ctrl: bool,
299      /// The "alt" key
300      pub alt: bool,
301      /// The "meta" key. This is the "windows" key on PC and "command" key on Mac.
302      pub meta: bool,
303  }
304  
305  impl fmt::Display for ModifiersState {
306      fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307          let mut s = String::new();
308          if self.ctrl {
309              s.push_str("<ctrl>");
310          }
311          if self.alt {
312              s.push_str("<alt>");
313          }
314          if self.meta {
315              s.push_str("<meta>");
316          }
317          if self.shift {
318              s.push_str("<shift>");
319          }
320          s.fmt(f)
321      }
322  }
323  
324  /// A delta represented in logical pixels.
325  #[derive(Debug, Copy, Clone, PartialEq)]
326  pub struct LogicalDelta {
327      pub x: f64,
328      pub y: f64,
329  }
330  
331  /// A position represented in logical pixels.
332  #[derive(Debug, Copy, Clone, PartialEq)]
333  pub struct LogicalPosition {
334      pub x: f64,
335      pub y: f64,
336  }
337  
338  impl LogicalPosition {
339      pub fn new(x: f64, y: f64) -> Self {
340          LogicalPosition { x, y }
341      }
342  
343      pub fn from_physical<T: Into<PhysicalPosition>>(physical: T, scale_factor: f64) -> Self {
344          physical.into().to_logical(self::pixel_ratio(scale_factor))
345      }
346  
347      pub fn to_physical(self, scale_factor: f64) -> PhysicalPosition {
348          let x = self.x * self::pixel_ratio(scale_factor);
349          let y = self.y * self::pixel_ratio(scale_factor);
350          PhysicalPosition::new(x, y)
351      }
352  }
353  
354  /// A position represented in physical pixels.
355  #[derive(Debug, Copy, Clone, PartialEq)]
356  pub struct PhysicalPosition {
357      pub x: f64,
358      pub y: f64,
359  }
360  
361  impl PhysicalPosition {
362      pub fn new(x: f64, y: f64) -> Self {
363          PhysicalPosition { x, y }
364      }
365  
366      pub fn from_logical<T: Into<LogicalPosition>>(logical: T, scale_factor: f64) -> Self {
367          logical.into().to_physical(self::pixel_ratio(scale_factor))
368      }
369  
370      pub fn to_logical(self, scale_factor: f64) -> LogicalPosition {
371          let x = self.x / self::pixel_ratio(scale_factor);
372          let y = self.y / self::pixel_ratio(scale_factor);
373          LogicalPosition::new(x, y)
374      }
375  }
376  
377  /// A size represented in logical pixels.
378  #[derive(Debug, Copy, Clone, PartialEq)]
379  pub struct LogicalSize {
380      pub width: f64,
381      pub height: f64,
382  }
383  
384  impl LogicalSize {
385      pub const fn new(width: f64, height: f64) -> Self {
386          LogicalSize { width, height }
387      }
388  
389      pub fn from_physical<T: Into<PhysicalSize>>(physical: T, scale_factor: f64) -> Self {
390          physical.into().to_logical(self::pixel_ratio(scale_factor))
391      }
392  
393      pub fn to_physical(self, scale_factor: f64) -> PhysicalSize {
394          let width = self.width * self::pixel_ratio(scale_factor);
395          let height = self.height * self::pixel_ratio(scale_factor);
396          PhysicalSize::new(width, height)
397      }
398  
399      pub fn is_zero(&self) -> bool {
400          self.width < 1. || self.height < 1.
401      }
402  }
403  
404  impl From<(u32, u32)> for LogicalSize {
405      fn from((width, height): (u32, u32)) -> Self {
406          Self::new(width as f64, height as f64)
407      }
408  }
409  
410  impl From<LogicalSize> for (u32, u32) {
411      /// Note that this rounds instead of truncating.
412      fn from(other: LogicalSize) -> (u32, u32) {
413          (other.width.round() as _, other.height.round() as _)
414      }
415  }
416  
417  /// A size represented in physical pixels.
418  #[derive(Debug, Copy, Clone, PartialEq)]
419  pub struct PhysicalSize {
420      pub width: f64,
421      pub height: f64,
422  }
423  
424  impl PhysicalSize {
425      pub fn new(width: f64, height: f64) -> Self {
426          PhysicalSize { width, height }
427      }
428  
429      pub fn from_logical<T: Into<LogicalSize>>(logical: T, scale_factor: f64) -> Self {
430          logical.into().to_physical(self::pixel_ratio(scale_factor))
431      }
432  
433      pub fn to_logical(self, scale_factor: f64) -> LogicalSize {
434          let width = self.width / self::pixel_ratio(scale_factor);
435          let height = self.height / self::pixel_ratio(scale_factor);
436          LogicalSize::new(width, height)
437      }
438  }
439  
440  impl From<(u32, u32)> for PhysicalSize {
441      fn from((width, height): (u32, u32)) -> Self {
442          Self::new(width as f64, height as f64)
443      }
444  }
445  
446  impl From<PhysicalSize> for (u32, u32) {
447      /// Note that this rounds instead of truncating.
448      fn from(other: PhysicalSize) -> (u32, u32) {
449          (other.width.round() as _, other.height.round() as _)
450      }
451  }
452  
453  /// The ratio between screen coordinates and pixels, given the
454  /// content scale.
455  /// On macOS, screen coordinates don't map 1:1 with pixels. Hence,
456  /// our ratio between screen coordinates and pixels is whatever
457  /// the scaling factor is, which is always `2.0` on modern hardware.
458  #[cfg(target_os = "macos")]
459  pub fn pixel_ratio(scale_factor: f64) -> f64 {
460      scale_factor
461  }
462  
463  /// The ratio between screen coordinates and pixels, given the
464  /// content scale.
465  /// On Linux and Windows, screen coordinates always map 1:1 with pixels.
466  /// No matter the DPI settings and display, we always want to map a screen
467  /// coordinate with a single pixel.
468  #[cfg(not(target_os = "macos"))]
469  pub fn pixel_ratio(_scale_factor: f64) -> f64 {
470      1.0
471  }