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 }