diff options
Diffstat (limited to 'engine/src/windowing')
| -rw-r--r-- | engine/src/windowing/dpi.rs | 217 | ||||
| -rw-r--r-- | engine/src/windowing/keyboard.rs | 826 | ||||
| -rw-r--r-- | engine/src/windowing/monitor.rs | 50 | ||||
| -rw-r--r-- | engine/src/windowing/mouse.rs | 197 | ||||
| -rw-r--r-- | engine/src/windowing/window.rs | 291 | ||||
| -rw-r--r-- | engine/src/windowing/window/platform.rs | 12 |
6 files changed, 1593 insertions, 0 deletions
diff --git a/engine/src/windowing/dpi.rs b/engine/src/windowing/dpi.rs new file mode 100644 index 0000000..e3b1be1 --- /dev/null +++ b/engine/src/windowing/dpi.rs @@ -0,0 +1,217 @@ +use crate::reflection::Reflection; + +macro_rules! gen_struct_from_to_impls { + ($struct: ident, fields=($($field: ident),*)) => { + impl<Pixel> From<winit::dpi::$struct<Pixel>> for $struct<Pixel> + { + fn from(source: winit::dpi::$struct<Pixel>) -> Self + { + Self { + $($field: source.$field),* + } + } + } + + impl<Pixel> From<$struct<Pixel>> for winit::dpi::$struct<Pixel> + { + fn from(source: $struct<Pixel>) -> Self + { + Self { + $($field: source.$field),* + } + } + } + }; +} + +macro_rules! gen_enum_from_to_impls { + ($enum: ident, variants=($($variant: ident),*)) => { + impl From<winit::dpi::$enum> for $enum + { + fn from(source: winit::dpi::$enum) -> Self + { + match source { + $(winit::dpi::$enum::$variant(pos) => Self::$variant(pos.into())),* + } + } + } + + impl From<$enum> for winit::dpi::$enum + { + fn from(source: $enum) -> Self + { + match source { + $($enum::$variant(pos) => Self::$variant(pos.into())),* + } + } + } + }; +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Reflection)] +#[reflection(impl_with_generics(<u32>, <i32>, <f32>, <f64>))] +pub struct PhysicalPosition<Pixel> +{ + pub x: Pixel, + pub y: Pixel, +} + +impl<Pixel> PhysicalPosition<Pixel> +{ + pub fn to_logical<LogicalPixel>( + &self, + scale_factor: f64, + ) -> LogicalPosition<LogicalPixel> + where + Pixel: Into<f64> + Clone, + LogicalPixel: From<f64>, + { + let x = self.x.clone().into(); + let y = self.y.clone().into(); + + LogicalPosition { + x: (x / scale_factor).into(), + y: (y / scale_factor).into(), + } + } + + pub fn try_convert_from<SourcePixel>( + source: PhysicalPosition<SourcePixel>, + ) -> Result<Self, Pixel::Error> + where + Pixel: TryFrom<SourcePixel>, + { + Ok(Self { + x: source.x.try_into()?, + y: source.y.try_into()?, + }) + } +} + +gen_struct_from_to_impls!(PhysicalPosition, fields = (x, y)); + +#[derive(Debug, Default, Clone, Copy, PartialEq, Reflection)] +#[reflection(impl_with_generics(<f64>))] +pub struct LogicalPosition<Pixel> +{ + pub x: Pixel, + pub y: Pixel, +} + +impl<Pixel> LogicalPosition<Pixel> +{ + pub fn try_convert_from<SourcePixel>( + source: LogicalPosition<SourcePixel>, + ) -> Result<Self, Pixel::Error> + where + Pixel: TryFrom<SourcePixel>, + { + Ok(Self { + x: source.x.try_into()?, + y: source.y.try_into()?, + }) + } +} + +gen_struct_from_to_impls!(LogicalPosition, fields = (x, y)); + +#[derive(Debug, Clone, Copy, PartialEq, Reflection)] +pub enum Position +{ + Physical(PhysicalPosition<i32>), + Logical(LogicalPosition<f64>), +} + +impl Default for Position +{ + fn default() -> Self + { + Self::Logical(LogicalPosition::default()) + } +} + +gen_enum_from_to_impls!(Position, variants = (Physical, Logical)); + +#[derive(Debug, Default, Clone, Copy, PartialEq, Reflection)] +#[reflection(impl_with_generics(<u32>))] +pub struct PhysicalSize<Pixel> +{ + pub width: Pixel, + pub height: Pixel, +} + +impl<Pixel> PhysicalSize<Pixel> +{ + pub fn to_logical<LogicalPixel>(&self, scale_factor: f64) -> LogicalSize<LogicalPixel> + where + Pixel: Into<f64> + Clone, + LogicalPixel: From<f64>, + { + let width = self.width.clone().into(); + let height = self.height.clone().into(); + + LogicalSize { + width: (width / scale_factor).into(), + height: (height / scale_factor).into(), + } + } +} + +impl<Pixel> PhysicalSize<Pixel> +{ + pub fn try_convert_from<SourcePixel>( + source: PhysicalSize<SourcePixel>, + ) -> Result<Self, Pixel::Error> + where + Pixel: TryFrom<SourcePixel>, + { + Ok(Self { + width: source.width.try_into()?, + height: source.height.try_into()?, + }) + } +} + +gen_struct_from_to_impls!(PhysicalSize, fields = (width, height)); + +#[derive(Debug, Default, Clone, Copy, PartialEq, Reflection)] +#[reflection(impl_with_generics(<f64>))] +pub struct LogicalSize<Pixel> +{ + pub width: Pixel, + pub height: Pixel, +} + +impl<Pixel> LogicalSize<Pixel> +{ + pub fn try_convert_from<SourcePixel>( + source: LogicalSize<SourcePixel>, + ) -> Result<Self, Pixel::Error> + where + Pixel: TryFrom<SourcePixel>, + { + Ok(Self { + width: source.width.try_into()?, + height: source.height.try_into()?, + }) + } +} + +gen_struct_from_to_impls!(LogicalSize, fields = (width, height)); + +#[derive(Debug, Clone, Copy, PartialEq, Reflection)] +pub enum Size +{ + Physical(PhysicalSize<u32>), + Logical(LogicalSize<f64>), +} + +impl Default for Size +{ + fn default() -> Self + { + Self::Logical(LogicalSize::default()) + } +} + +gen_enum_from_to_impls!(Size, variants = (Physical, Logical)); diff --git a/engine/src/windowing/keyboard.rs b/engine/src/windowing/keyboard.rs new file mode 100644 index 0000000..dffa95e --- /dev/null +++ b/engine/src/windowing/keyboard.rs @@ -0,0 +1,826 @@ +use util_macros::VariantArr; + +use crate::ecs::Sole; +use crate::util::BitArray; + +#[derive(Debug, Default, Sole)] +pub struct Keyboard +{ + keys: BitArray<{ (Key::KEYS.len() * BITS_PER_KEY).div_ceil(8) }, BITS_PER_KEY>, + text_keys: String, +} + +impl Keyboard +{ + /// Returns whether the given key was just pressed this frame. This function will + /// return `false` if the key was also pressed the previous frame. + pub fn just_pressed(&self, key: Key) -> bool + { + self.get_key_state(key) == KeyState::Pressed + && self.get_prev_key_state(key) == KeyState::Released + } + + /// Returns whether the given key was just released this frame. This function will + /// return `false` if the key was also released the previous frame. + pub fn just_released(&self, key: Key) -> bool + { + self.get_key_state(key) == KeyState::Released + && self.get_prev_key_state(key) == KeyState::Pressed + } + + /// Returns whether the given key is currently pressed. + pub fn pressed(&self, key: Key) -> bool + { + self.get_key_state(key) == KeyState::Pressed + } + + /// Returns whether the given key is currently released. + pub fn released(&self, key: Key) -> bool + { + self.get_key_state(key) == KeyState::Released + } + + /// Returns a iterator of key & key state pairs where each of the keys have a + /// different state than they had the previous frame. + pub fn new_key_states(&self) -> impl Iterator<Item = (Key, KeyState)> + use<'_> + { + self.keys.iter_occupied().filter_map(|(key_index, bits)| { + if bits == KEY_CURR_PRESSED_BITS | KEY_PREV_PRESSED_BITS { + return None; + } + + let key = Key::KEYS[key_index]; + + let curr_state = match bits & KEY_CURR_PRESSED_BITS { + KEY_CURR_PRESSED_BITS => KeyState::Pressed, + 0 => KeyState::Released, + _ => unreachable!(), + }; + + Some((key, curr_state)) + }) + } + + #[must_use] + pub fn get_key_state(&self, key: Key) -> KeyState + { + let bits = self.keys.get(key as usize); + + let state = match bits & KEY_CURR_PRESSED_BITS { + KEY_CURR_PRESSED_BITS => KeyState::Pressed, + 0 => KeyState::Released, + _ => unreachable!(), + }; + + state + } + + #[must_use] + pub fn get_prev_key_state(&self, key: Key) -> KeyState + { + let bits = self.keys.get(key as usize); + + let state = match bits & KEY_PREV_PRESSED_BITS { + KEY_PREV_PRESSED_BITS => KeyState::Pressed, + 0 => KeyState::Released, + _ => unreachable!(), + }; + + state + } + + pub fn text_keys(&self) -> &str + { + &self.text_keys + } + + pub fn set_key_state(&mut self, key: Key, key_state: KeyState) + { + self.keys.clear_and_set( + key as usize, + match key_state { + KeyState::Pressed => KEY_CURR_PRESSED_BITS, + KeyState::Released => 0, + }, + KEY_CURR_PRESSED_BITS, + ); + } + + pub fn make_key_states_previous(&mut self) + { + for byte in self.keys.bytes_mut() { + *byte = (*byte >> 1) & 0b01010101 | *byte & 0b10101010; + } + } + + pub fn set_text_keys(&mut self, text_keys: impl IntoIterator<Item = char>) + { + self.text_keys.clear(); + self.text_keys.extend(text_keys); + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, VariantArr)] +#[variant_arr(name = KEYS)] +#[non_exhaustive] +pub enum Key +{ + /// <kbd>`</kbd> on a US keyboard. This is also called a backtick or grave. + /// This is the <kbd>半角</kbd>/<kbd>全角</kbd>/<kbd>漢字</kbd> + /// (hankaku/zenkaku/kanji) key on Japanese keyboards + Backquote, + /// Used for both the US <kbd>\\</kbd> (on the 101-key layout) and also for the key + /// located between the <kbd>"</kbd> and <kbd>Enter</kbd> keys on row C of the 102-, + /// 104- and 106-key layouts. + /// Labeled <kbd>#</kbd> on a UK (102) keyboard. + Backslash, + /// <kbd>[</kbd> on a US keyboard. + BracketLeft, + /// <kbd>]</kbd> on a US keyboard. + BracketRight, + /// <kbd>,</kbd> on a US keyboard. + Comma, + /// <kbd>0</kbd> on a US keyboard. + Digit0, + /// <kbd>1</kbd> on a US keyboard. + Digit1, + /// <kbd>2</kbd> on a US keyboard. + Digit2, + /// <kbd>3</kbd> on a US keyboard. + Digit3, + /// <kbd>4</kbd> on a US keyboard. + Digit4, + /// <kbd>5</kbd> on a US keyboard. + Digit5, + /// <kbd>6</kbd> on a US keyboard. + Digit6, + /// <kbd>7</kbd> on a US keyboard. + Digit7, + /// <kbd>8</kbd> on a US keyboard. + Digit8, + /// <kbd>9</kbd> on a US keyboard. + Digit9, + /// <kbd>=</kbd> on a US keyboard. + Equal, + /// Located between the left <kbd>Shift</kbd> and <kbd>Z</kbd> keys. + /// Labeled <kbd>\\</kbd> on a UK keyboard. + IntlBackslash, + /// Located between the <kbd>/</kbd> and right <kbd>Shift</kbd> keys. + /// Labeled <kbd>\\</kbd> (ro) on a Japanese keyboard. + IntlRo, + /// Located between the <kbd>=</kbd> and <kbd>Backspace</kbd> keys. + /// Labeled <kbd>¥</kbd> (yen) on a Japanese keyboard. <kbd>\\</kbd> on a + /// Russian keyboard. + IntlYen, + /// <kbd>a</kbd> on a US keyboard. + /// Labeled <kbd>q</kbd> on an AZERTY (e.g., French) keyboard. + A, + /// <kbd>b</kbd> on a US keyboard. + B, + /// <kbd>c</kbd> on a US keyboard. + C, + /// <kbd>d</kbd> on a US keyboard. + D, + /// <kbd>e</kbd> on a US keyboard. + E, + /// <kbd>f</kbd> on a US keyboard. + F, + /// <kbd>g</kbd> on a US keyboard. + G, + /// <kbd>h</kbd> on a US keyboard. + H, + /// <kbd>i</kbd> on a US keyboard. + I, + /// <kbd>j</kbd> on a US keyboard. + J, + /// <kbd>k</kbd> on a US keyboard. + K, + /// <kbd>l</kbd> on a US keyboard. + L, + /// <kbd>m</kbd> on a US keyboard. + M, + /// <kbd>n</kbd> on a US keyboard. + N, + /// <kbd>o</kbd> on a US keyboard. + O, + /// <kbd>p</kbd> on a US keyboard. + P, + /// <kbd>q</kbd> on a US keyboard. + /// Labeled <kbd>a</kbd> on an AZERTY (e.g., French) keyboard. + Q, + /// <kbd>r</kbd> on a US keyboard. + R, + /// <kbd>s</kbd> on a US keyboard. + S, + /// <kbd>t</kbd> on a US keyboard. + T, + /// <kbd>u</kbd> on a US keyboard. + U, + /// <kbd>v</kbd> on a US keyboard. + V, + /// <kbd>w</kbd> on a US keyboard. + /// Labeled <kbd>z</kbd> on an AZERTY (e.g., French) keyboard. + W, + /// <kbd>x</kbd> on a US keyboard. + X, + /// <kbd>y</kbd> on a US keyboard. + /// Labeled <kbd>z</kbd> on a QWERTZ (e.g., German) keyboard. + Y, + /// <kbd>z</kbd> on a US keyboard. + /// Labeled <kbd>w</kbd> on an AZERTY (e.g., French) keyboard, and <kbd>y</kbd> on a + /// QWERTZ (e.g., German) keyboard. + Z, + /// <kbd>-</kbd> on a US keyboard. + Minus, + /// <kbd>.</kbd> on a US keyboard. + Period, + /// <kbd>'</kbd> on a US keyboard. + Quote, + /// <kbd>;</kbd> on a US keyboard. + Semicolon, + /// <kbd>/</kbd> on a US keyboard. + Slash, + /// <kbd>Alt</kbd>, <kbd>Option</kbd>, or <kbd>⌥</kbd>. + AltLeft, + /// <kbd>Alt</kbd>, <kbd>Option</kbd>, or <kbd>⌥</kbd>. + /// This is labeled <kbd>AltGr</kbd> on many keyboard layouts. + AltRight, + /// <kbd>Backspace</kbd> or <kbd>⌫</kbd>. + /// Labeled <kbd>Delete</kbd> on Apple keyboards. + Backspace, + /// <kbd>CapsLock</kbd> or <kbd>⇪</kbd> + CapsLock, + /// The application context menu key, which is typically found between the right + /// <kbd>Super</kbd> key and the right <kbd>Control</kbd> key. + ContextMenu, + /// <kbd>Control</kbd> or <kbd>⌃</kbd> + ControlLeft, + /// <kbd>Control</kbd> or <kbd>⌃</kbd> + ControlRight, + /// <kbd>Enter</kbd> or <kbd>↵</kbd>. Labeled <kbd>Return</kbd> on Apple keyboards. + Enter, + /// The Windows, <kbd>⌘</kbd>, <kbd>Command</kbd>, or other OS symbol key. + SuperLeft, + /// The Windows, <kbd>⌘</kbd>, <kbd>Command</kbd>, or other OS symbol key. + SuperRight, + /// <kbd>Shift</kbd> or <kbd>⇧</kbd> + ShiftLeft, + /// <kbd>Shift</kbd> or <kbd>⇧</kbd> + ShiftRight, + /// <kbd> </kbd> (space) + Space, + /// <kbd>Tab</kbd> or <kbd>⇥</kbd> + Tab, + /// Japanese: <kbd>変</kbd> (henkan) + Convert, + /// Japanese: <kbd>カタカナ</kbd>/<kbd>ひらがな</kbd>/<kbd>ローマ字</kbd> + /// (katakana/hiragana/romaji) + KanaMode, + /// Korean: HangulMode <kbd>한/영</kbd> (han/yeong) + /// + /// Japanese (Mac keyboard): <kbd>か</kbd> (kana) + Lang1, + /// Korean: Hanja <kbd>한</kbd> (hanja) + /// + /// Japanese (Mac keyboard): <kbd>英</kbd> (eisu) + Lang2, + /// Japanese (word-processing keyboard): Katakana + Lang3, + /// Japanese (word-processing keyboard): Hiragana + Lang4, + /// Japanese (word-processing keyboard): Zenkaku/Hankaku + Lang5, + /// Japanese: <kbd>無変換</kbd> (muhenkan) + NonConvert, + /// <kbd>⌦</kbd>. The forward delete key. + /// Note that on Apple keyboards, the key labelled <kbd>Delete</kbd> on the main part + /// of the keyboard is encoded as [`Backspace`]. + /// + /// [`Backspace`]: Self::Backspace + Delete, + /// <kbd>Page Down</kbd>, <kbd>End</kbd>, or <kbd>↘</kbd> + End, + /// <kbd>Help</kbd>. Not present on standard PC keyboards. + Help, + /// <kbd>Home</kbd> or <kbd>↖</kbd> + Home, + /// <kbd>Insert</kbd> or <kbd>Ins</kbd>. Not present on Apple keyboards. + Insert, + /// <kbd>Page Down</kbd>, <kbd>PgDn</kbd>, or <kbd>⇟</kbd> + PageDown, + /// <kbd>Page Up</kbd>, <kbd>PgUp</kbd>, or <kbd>⇞</kbd> + PageUp, + /// <kbd>↓</kbd> + ArrowDown, + /// <kbd>←</kbd> + ArrowLeft, + /// <kbd>→</kbd> + ArrowRight, + /// <kbd>↑</kbd> + ArrowUp, + /// On the Mac, this is used for the numpad <kbd>Clear</kbd> key. + NumLock, + /// <kbd>0 Ins</kbd> on a keyboard. <kbd>0</kbd> on a phone or remote control + Numpad0, + /// <kbd>1 End</kbd> on a keyboard. <kbd>1</kbd> or <kbd>1 QZ</kbd> on a phone or + /// remote control + Numpad1, + /// <kbd>2 ↓</kbd> on a keyboard. <kbd>2 ABC</kbd> on a phone or remote control + Numpad2, + /// <kbd>3 PgDn</kbd> on a keyboard. <kbd>3 DEF</kbd> on a phone or remote control + Numpad3, + /// <kbd>4 ←</kbd> on a keyboard. <kbd>4 GHI</kbd> on a phone or remote control + Numpad4, + /// <kbd>5</kbd> on a keyboard. <kbd>5 JKL</kbd> on a phone or remote control + Numpad5, + /// <kbd>6 →</kbd> on a keyboard. <kbd>6 MNO</kbd> on a phone or remote control + Numpad6, + /// <kbd>7 Home</kbd> on a keyboard. <kbd>7 PQRS</kbd> or <kbd>7 PRS</kbd> on a phone + /// or remote control + Numpad7, + /// <kbd>8 ↑</kbd> on a keyboard. <kbd>8 TUV</kbd> on a phone or remote control + Numpad8, + /// <kbd>9 PgUp</kbd> on a keyboard. <kbd>9 WXYZ</kbd> or <kbd>9 WXY</kbd> on a phone + /// or remote control + Numpad9, + /// <kbd>+</kbd> + NumpadAdd, + /// Found on the Microsoft Natural Keyboard. + NumpadBackspace, + /// <kbd>C</kbd> or <kbd>A</kbd> (All Clear). Also for use with numpads that have a + /// <kbd>Clear</kbd> key that is separate from the <kbd>NumLock</kbd> key. On the + /// Mac, the numpad <kbd>Clear</kbd> key is encoded as [`NumLock`]. + /// + /// [`NumLock`]: Self::NumLock + NumpadClear, + /// <kbd>C</kbd> (Clear Entry) + NumpadClearEntry, + /// <kbd>,</kbd> (thousands separator). For locales where the thousands separator + /// is a "." (e.g., Brazil), this key may generate a <kbd>.</kbd>. + NumpadComma, + /// <kbd>. Del</kbd>. For locales where the decimal separator is "," (e.g., + /// Brazil), this key may generate a <kbd>,</kbd>. + NumpadDecimal, + /// <kbd>/</kbd> + NumpadDivide, + NumpadEnter, + /// <kbd>=</kbd> + NumpadEqual, + /// <kbd>#</kbd> on a phone or remote control device. This key is typically found + /// below the <kbd>9</kbd> key and to the right of the <kbd>0</kbd> key. + NumpadHash, + /// <kbd>M</kbd> Add current entry to the value stored in memory. + NumpadMemoryAdd, + /// <kbd>M</kbd> Clear the value stored in memory. + NumpadMemoryClear, + /// <kbd>M</kbd> Replace the current entry with the value stored in memory. + NumpadMemoryRecall, + /// <kbd>M</kbd> Replace the value stored in memory with the current entry. + NumpadMemoryStore, + /// <kbd>M</kbd> Subtract current entry from the value stored in memory. + NumpadMemorySubtract, + /// <kbd>*</kbd> on a keyboard. For use with numpads that provide mathematical + /// operations (<kbd>+</kbd>, <kbd>-</kbd> <kbd>*</kbd> and <kbd>/</kbd>). + /// + /// Use `NumpadStar` for the <kbd>*</kbd> key on phones and remote controls. + NumpadMultiply, + /// <kbd>(</kbd> Found on the Microsoft Natural Keyboard. + NumpadParenLeft, + /// <kbd>)</kbd> Found on the Microsoft Natural Keyboard. + NumpadParenRight, + /// <kbd>*</kbd> on a phone or remote control device. + /// + /// This key is typically found below the <kbd>7</kbd> key and to the left of + /// the <kbd>0</kbd> key. + /// + /// Use <kbd>"NumpadMultiply"</kbd> for the <kbd>*</kbd> key on + /// numeric keypads. + NumpadStar, + /// <kbd>-</kbd> + NumpadSubtract, + /// <kbd>Esc</kbd> or <kbd>⎋</kbd> + Escape, + /// <kbd>Fn</kbd> This is typically a hardware key that does not generate a separate + /// code. + Fn, + /// <kbd>FLock</kbd> or <kbd>FnLock</kbd>. Function Lock key. Found on the Microsoft + /// Natural Keyboard. + FnLock, + /// <kbd>PrtScr SysRq</kbd> or <kbd>Print Screen</kbd> + PrintScreen, + /// <kbd>Scroll Lock</kbd> + ScrollLock, + /// <kbd>Pause Break</kbd> + Pause, + /// Some laptops place this key to the left of the <kbd>↑</kbd> key. + /// + /// This also the "back" button (triangle) on Android. + BrowserBack, + BrowserFavorites, + /// Some laptops place this key to the right of the <kbd>↑</kbd> key. + BrowserForward, + /// The "home" button on Android. + BrowserHome, + BrowserRefresh, + BrowserSearch, + BrowserStop, + /// <kbd>Eject</kbd> or <kbd>⏏</kbd>. This key is placed in the function section on + /// some Apple keyboards. + Eject, + /// Sometimes labelled <kbd>My Computer</kbd> on the keyboard + LaunchApp1, + /// Sometimes labelled <kbd>Calculator</kbd> on the keyboard + LaunchApp2, + LaunchMail, + MediaPlayPause, + MediaSelect, + MediaStop, + MediaTrackNext, + MediaTrackPrevious, + /// This key is placed in the function section on some Apple keyboards, replacing the + /// <kbd>Eject</kbd> key. + Power, + Sleep, + AudioVolumeDown, + AudioVolumeMute, + AudioVolumeUp, + WakeUp, + // Legacy modifier key. Also called "Super" in certain places. + Meta, + // Legacy modifier key. + Hyper, + Turbo, + Abort, + Resume, + Suspend, + /// Found on Sun’s USB keyboard. + Again, + /// Found on Sun’s USB keyboard. + Copy, + /// Found on Sun’s USB keyboard. + Cut, + /// Found on Sun’s USB keyboard. + Find, + /// Found on Sun’s USB keyboard. + Open, + /// Found on Sun’s USB keyboard. + Paste, + /// Found on Sun’s USB keyboard. + Props, + /// Found on Sun’s USB keyboard. + Select, + /// Found on Sun’s USB keyboard. + Undo, + /// Use for dedicated <kbd>ひらがな</kbd> key found on some Japanese word processing + /// keyboards. + Hiragana, + /// Use for dedicated <kbd>カタカナ</kbd> key found on some Japanese word processing + /// keyboards. + Katakana, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F1, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F2, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F3, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F4, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F5, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F6, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F7, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F8, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F9, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F10, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F11, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F12, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F13, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F14, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F15, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F16, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F17, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F18, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F19, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F20, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F21, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F22, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F23, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F24, + /// General-purpose function key. + F25, + /// General-purpose function key. + F26, + /// General-purpose function key. + F27, + /// General-purpose function key. + F28, + /// General-purpose function key. + F29, + /// General-purpose function key. + F30, + /// General-purpose function key. + F31, + /// General-purpose function key. + F32, + /// General-purpose function key. + F33, + /// General-purpose function key. + F34, + /// General-purpose function key. + F35, +} + +impl TryFrom<winit::keyboard::KeyCode> for Key +{ + type Error = UnknownKeyCodeError; + + fn try_from(key_code: winit::keyboard::KeyCode) -> Result<Self, Self::Error> + { + match key_code { + winit::keyboard::KeyCode::Backquote => Ok(Self::Backquote), + winit::keyboard::KeyCode::Backslash => Ok(Self::Backslash), + winit::keyboard::KeyCode::BracketLeft => Ok(Self::BracketLeft), + winit::keyboard::KeyCode::BracketRight => Ok(Self::BracketRight), + winit::keyboard::KeyCode::Comma => Ok(Self::Comma), + winit::keyboard::KeyCode::Digit0 => Ok(Self::Digit0), + winit::keyboard::KeyCode::Digit1 => Ok(Self::Digit1), + winit::keyboard::KeyCode::Digit2 => Ok(Self::Digit2), + winit::keyboard::KeyCode::Digit3 => Ok(Self::Digit3), + winit::keyboard::KeyCode::Digit4 => Ok(Self::Digit4), + winit::keyboard::KeyCode::Digit5 => Ok(Self::Digit5), + winit::keyboard::KeyCode::Digit6 => Ok(Self::Digit6), + winit::keyboard::KeyCode::Digit7 => Ok(Self::Digit7), + winit::keyboard::KeyCode::Digit8 => Ok(Self::Digit8), + winit::keyboard::KeyCode::Digit9 => Ok(Self::Digit9), + winit::keyboard::KeyCode::Equal => Ok(Self::Equal), + winit::keyboard::KeyCode::IntlBackslash => Ok(Self::IntlBackslash), + winit::keyboard::KeyCode::IntlRo => Ok(Self::IntlRo), + winit::keyboard::KeyCode::IntlYen => Ok(Self::IntlYen), + winit::keyboard::KeyCode::KeyA => Ok(Self::A), + winit::keyboard::KeyCode::KeyB => Ok(Self::B), + winit::keyboard::KeyCode::KeyC => Ok(Self::C), + winit::keyboard::KeyCode::KeyD => Ok(Self::D), + winit::keyboard::KeyCode::KeyE => Ok(Self::E), + winit::keyboard::KeyCode::KeyF => Ok(Self::F), + winit::keyboard::KeyCode::KeyG => Ok(Self::G), + winit::keyboard::KeyCode::KeyH => Ok(Self::H), + winit::keyboard::KeyCode::KeyI => Ok(Self::I), + winit::keyboard::KeyCode::KeyJ => Ok(Self::J), + winit::keyboard::KeyCode::KeyK => Ok(Self::K), + winit::keyboard::KeyCode::KeyL => Ok(Self::L), + winit::keyboard::KeyCode::KeyM => Ok(Self::M), + winit::keyboard::KeyCode::KeyN => Ok(Self::N), + winit::keyboard::KeyCode::KeyO => Ok(Self::O), + winit::keyboard::KeyCode::KeyP => Ok(Self::P), + winit::keyboard::KeyCode::KeyQ => Ok(Self::Q), + winit::keyboard::KeyCode::KeyR => Ok(Self::R), + winit::keyboard::KeyCode::KeyS => Ok(Self::S), + winit::keyboard::KeyCode::KeyT => Ok(Self::T), + winit::keyboard::KeyCode::KeyU => Ok(Self::U), + winit::keyboard::KeyCode::KeyV => Ok(Self::V), + winit::keyboard::KeyCode::KeyW => Ok(Self::W), + winit::keyboard::KeyCode::KeyX => Ok(Self::X), + winit::keyboard::KeyCode::KeyY => Ok(Self::Y), + winit::keyboard::KeyCode::KeyZ => Ok(Self::Z), + winit::keyboard::KeyCode::Minus => Ok(Self::Minus), + winit::keyboard::KeyCode::Period => Ok(Self::Period), + winit::keyboard::KeyCode::Quote => Ok(Self::Quote), + winit::keyboard::KeyCode::Semicolon => Ok(Self::Semicolon), + winit::keyboard::KeyCode::Slash => Ok(Self::Slash), + winit::keyboard::KeyCode::AltLeft => Ok(Self::AltLeft), + winit::keyboard::KeyCode::AltRight => Ok(Self::AltRight), + winit::keyboard::KeyCode::Backspace => Ok(Self::Backspace), + winit::keyboard::KeyCode::CapsLock => Ok(Self::CapsLock), + winit::keyboard::KeyCode::ContextMenu => Ok(Self::ContextMenu), + winit::keyboard::KeyCode::ControlLeft => Ok(Self::ControlLeft), + winit::keyboard::KeyCode::ControlRight => Ok(Self::ControlRight), + winit::keyboard::KeyCode::Enter => Ok(Self::Enter), + winit::keyboard::KeyCode::SuperLeft => Ok(Self::SuperLeft), + winit::keyboard::KeyCode::SuperRight => Ok(Self::SuperRight), + winit::keyboard::KeyCode::ShiftLeft => Ok(Self::ShiftLeft), + winit::keyboard::KeyCode::ShiftRight => Ok(Self::ShiftRight), + winit::keyboard::KeyCode::Space => Ok(Self::Space), + winit::keyboard::KeyCode::Tab => Ok(Self::Tab), + winit::keyboard::KeyCode::Convert => Ok(Self::Convert), + winit::keyboard::KeyCode::KanaMode => Ok(Self::KanaMode), + winit::keyboard::KeyCode::Lang1 => Ok(Self::Lang1), + winit::keyboard::KeyCode::Lang2 => Ok(Self::Lang2), + winit::keyboard::KeyCode::Lang3 => Ok(Self::Lang3), + winit::keyboard::KeyCode::Lang4 => Ok(Self::Lang4), + winit::keyboard::KeyCode::Lang5 => Ok(Self::Lang5), + winit::keyboard::KeyCode::NonConvert => Ok(Self::NonConvert), + winit::keyboard::KeyCode::Delete => Ok(Self::Delete), + winit::keyboard::KeyCode::End => Ok(Self::End), + winit::keyboard::KeyCode::Help => Ok(Self::Help), + winit::keyboard::KeyCode::Home => Ok(Self::Home), + winit::keyboard::KeyCode::Insert => Ok(Self::Insert), + winit::keyboard::KeyCode::PageDown => Ok(Self::PageDown), + winit::keyboard::KeyCode::PageUp => Ok(Self::PageUp), + winit::keyboard::KeyCode::ArrowDown => Ok(Self::ArrowDown), + winit::keyboard::KeyCode::ArrowLeft => Ok(Self::ArrowLeft), + winit::keyboard::KeyCode::ArrowRight => Ok(Self::ArrowRight), + winit::keyboard::KeyCode::ArrowUp => Ok(Self::ArrowUp), + winit::keyboard::KeyCode::NumLock => Ok(Self::NumLock), + winit::keyboard::KeyCode::Numpad0 => Ok(Self::Numpad0), + winit::keyboard::KeyCode::Numpad1 => Ok(Self::Numpad1), + winit::keyboard::KeyCode::Numpad2 => Ok(Self::Numpad2), + winit::keyboard::KeyCode::Numpad3 => Ok(Self::Numpad3), + winit::keyboard::KeyCode::Numpad4 => Ok(Self::Numpad4), + winit::keyboard::KeyCode::Numpad5 => Ok(Self::Numpad5), + winit::keyboard::KeyCode::Numpad6 => Ok(Self::Numpad6), + winit::keyboard::KeyCode::Numpad7 => Ok(Self::Numpad7), + winit::keyboard::KeyCode::Numpad8 => Ok(Self::Numpad8), + winit::keyboard::KeyCode::Numpad9 => Ok(Self::Numpad9), + winit::keyboard::KeyCode::NumpadAdd => Ok(Self::NumpadAdd), + winit::keyboard::KeyCode::NumpadBackspace => Ok(Self::NumpadBackspace), + winit::keyboard::KeyCode::NumpadClear => Ok(Self::NumpadClear), + winit::keyboard::KeyCode::NumpadClearEntry => Ok(Self::NumpadClearEntry), + winit::keyboard::KeyCode::NumpadComma => Ok(Self::NumpadComma), + winit::keyboard::KeyCode::NumpadDecimal => Ok(Self::NumpadDecimal), + winit::keyboard::KeyCode::NumpadDivide => Ok(Self::NumpadDivide), + winit::keyboard::KeyCode::NumpadEnter => Ok(Self::NumpadEnter), + winit::keyboard::KeyCode::NumpadEqual => Ok(Self::NumpadEqual), + winit::keyboard::KeyCode::NumpadHash => Ok(Self::NumpadHash), + winit::keyboard::KeyCode::NumpadMemoryAdd => Ok(Self::NumpadMemoryAdd), + winit::keyboard::KeyCode::NumpadMemoryClear => Ok(Self::NumpadMemoryClear), + winit::keyboard::KeyCode::NumpadMemoryRecall => Ok(Self::NumpadMemoryRecall), + winit::keyboard::KeyCode::NumpadMemoryStore => Ok(Self::NumpadMemoryStore), + winit::keyboard::KeyCode::NumpadMemorySubtract => { + Ok(Self::NumpadMemorySubtract) + } + winit::keyboard::KeyCode::NumpadMultiply => Ok(Self::NumpadMultiply), + winit::keyboard::KeyCode::NumpadParenLeft => Ok(Self::NumpadParenLeft), + winit::keyboard::KeyCode::NumpadParenRight => Ok(Self::NumpadParenRight), + winit::keyboard::KeyCode::NumpadStar => Ok(Self::NumpadStar), + winit::keyboard::KeyCode::NumpadSubtract => Ok(Self::NumpadSubtract), + winit::keyboard::KeyCode::Escape => Ok(Self::Escape), + winit::keyboard::KeyCode::Fn => Ok(Self::Fn), + winit::keyboard::KeyCode::FnLock => Ok(Self::FnLock), + winit::keyboard::KeyCode::PrintScreen => Ok(Self::PrintScreen), + winit::keyboard::KeyCode::ScrollLock => Ok(Self::ScrollLock), + winit::keyboard::KeyCode::Pause => Ok(Self::Pause), + winit::keyboard::KeyCode::BrowserBack => Ok(Self::BrowserBack), + winit::keyboard::KeyCode::BrowserFavorites => Ok(Self::BrowserFavorites), + winit::keyboard::KeyCode::BrowserForward => Ok(Self::BrowserForward), + winit::keyboard::KeyCode::BrowserHome => Ok(Self::BrowserHome), + winit::keyboard::KeyCode::BrowserRefresh => Ok(Self::BrowserRefresh), + winit::keyboard::KeyCode::BrowserSearch => Ok(Self::BrowserSearch), + winit::keyboard::KeyCode::BrowserStop => Ok(Self::BrowserStop), + winit::keyboard::KeyCode::Eject => Ok(Self::Eject), + winit::keyboard::KeyCode::LaunchApp1 => Ok(Self::LaunchApp1), + winit::keyboard::KeyCode::LaunchApp2 => Ok(Self::LaunchApp2), + winit::keyboard::KeyCode::LaunchMail => Ok(Self::LaunchMail), + winit::keyboard::KeyCode::MediaPlayPause => Ok(Self::MediaPlayPause), + winit::keyboard::KeyCode::MediaSelect => Ok(Self::MediaSelect), + winit::keyboard::KeyCode::MediaStop => Ok(Self::MediaStop), + winit::keyboard::KeyCode::MediaTrackNext => Ok(Self::MediaTrackNext), + winit::keyboard::KeyCode::MediaTrackPrevious => Ok(Self::MediaTrackPrevious), + winit::keyboard::KeyCode::Power => Ok(Self::Power), + winit::keyboard::KeyCode::Sleep => Ok(Self::Sleep), + winit::keyboard::KeyCode::AudioVolumeDown => Ok(Self::AudioVolumeDown), + winit::keyboard::KeyCode::AudioVolumeMute => Ok(Self::AudioVolumeMute), + winit::keyboard::KeyCode::AudioVolumeUp => Ok(Self::AudioVolumeUp), + winit::keyboard::KeyCode::WakeUp => Ok(Self::WakeUp), + winit::keyboard::KeyCode::Meta => Ok(Self::Meta), + winit::keyboard::KeyCode::Hyper => Ok(Self::Hyper), + winit::keyboard::KeyCode::Turbo => Ok(Self::Turbo), + winit::keyboard::KeyCode::Abort => Ok(Self::Abort), + winit::keyboard::KeyCode::Resume => Ok(Self::Resume), + winit::keyboard::KeyCode::Suspend => Ok(Self::Suspend), + winit::keyboard::KeyCode::Again => Ok(Self::Again), + winit::keyboard::KeyCode::Copy => Ok(Self::Copy), + winit::keyboard::KeyCode::Cut => Ok(Self::Cut), + winit::keyboard::KeyCode::Find => Ok(Self::Find), + winit::keyboard::KeyCode::Open => Ok(Self::Open), + winit::keyboard::KeyCode::Paste => Ok(Self::Paste), + winit::keyboard::KeyCode::Props => Ok(Self::Props), + winit::keyboard::KeyCode::Select => Ok(Self::Select), + winit::keyboard::KeyCode::Undo => Ok(Self::Undo), + winit::keyboard::KeyCode::Hiragana => Ok(Self::Hiragana), + winit::keyboard::KeyCode::Katakana => Ok(Self::Katakana), + winit::keyboard::KeyCode::F1 => Ok(Self::F1), + winit::keyboard::KeyCode::F2 => Ok(Self::F2), + winit::keyboard::KeyCode::F3 => Ok(Self::F3), + winit::keyboard::KeyCode::F4 => Ok(Self::F4), + winit::keyboard::KeyCode::F5 => Ok(Self::F5), + winit::keyboard::KeyCode::F6 => Ok(Self::F6), + winit::keyboard::KeyCode::F7 => Ok(Self::F7), + winit::keyboard::KeyCode::F8 => Ok(Self::F8), + winit::keyboard::KeyCode::F9 => Ok(Self::F9), + winit::keyboard::KeyCode::F10 => Ok(Self::F10), + winit::keyboard::KeyCode::F11 => Ok(Self::F11), + winit::keyboard::KeyCode::F12 => Ok(Self::F12), + winit::keyboard::KeyCode::F13 => Ok(Self::F13), + winit::keyboard::KeyCode::F14 => Ok(Self::F14), + winit::keyboard::KeyCode::F15 => Ok(Self::F15), + winit::keyboard::KeyCode::F16 => Ok(Self::F16), + winit::keyboard::KeyCode::F17 => Ok(Self::F17), + winit::keyboard::KeyCode::F18 => Ok(Self::F18), + winit::keyboard::KeyCode::F19 => Ok(Self::F19), + winit::keyboard::KeyCode::F20 => Ok(Self::F20), + winit::keyboard::KeyCode::F21 => Ok(Self::F21), + winit::keyboard::KeyCode::F22 => Ok(Self::F22), + winit::keyboard::KeyCode::F23 => Ok(Self::F23), + winit::keyboard::KeyCode::F24 => Ok(Self::F24), + winit::keyboard::KeyCode::F25 => Ok(Self::F25), + winit::keyboard::KeyCode::F26 => Ok(Self::F26), + winit::keyboard::KeyCode::F27 => Ok(Self::F27), + winit::keyboard::KeyCode::F28 => Ok(Self::F28), + winit::keyboard::KeyCode::F29 => Ok(Self::F29), + winit::keyboard::KeyCode::F30 => Ok(Self::F30), + winit::keyboard::KeyCode::F31 => Ok(Self::F31), + winit::keyboard::KeyCode::F32 => Ok(Self::F32), + winit::keyboard::KeyCode::F33 => Ok(Self::F33), + winit::keyboard::KeyCode::F34 => Ok(Self::F34), + winit::keyboard::KeyCode::F35 => Ok(Self::F35), + _ => Err(UnknownKeyCodeError), + } + } +} + +#[derive(Debug, thiserror::Error)] +#[error("Unknown key code")] +pub struct UnknownKeyCodeError; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum KeyState +{ + Pressed, + Released, +} + +impl KeyState +{ + #[must_use] + #[inline] + pub fn is_pressed(&self) -> bool + { + matches!(self, Self::Pressed) + } + + #[must_use] + #[inline] + pub fn is_released(&self) -> bool + { + matches!(self, Self::Released) + } +} + +impl From<winit::event::ElementState> for KeyState +{ + fn from(element_state: winit::event::ElementState) -> Self + { + match element_state { + winit::event::ElementState::Pressed => Self::Pressed, + winit::event::ElementState::Released => Self::Released, + } + } +} + +const BITS_PER_KEY: usize = 2; + +const KEY_CURR_PRESSED_BITS: u8 = 0b10; +const KEY_PREV_PRESSED_BITS: u8 = 0b01; diff --git a/engine/src/windowing/monitor.rs b/engine/src/windowing/monitor.rs new file mode 100644 index 0000000..894448a --- /dev/null +++ b/engine/src/windowing/monitor.rs @@ -0,0 +1,50 @@ +use crate::windowing::dpi::{PhysicalPosition, PhysicalSize}; + +/// Handle to a monitor that may or may not exist any longer. +#[derive(Debug, Clone)] +pub struct Handle +{ + inner: winit::monitor::MonitorHandle, +} + +impl Handle +{ + /// Returns a human-readable name of the monitor. + #[inline] + pub fn name(&self) -> Option<String> + { + self.inner.name() + } + + /// Returns the monitor's resolution. + #[inline] + pub fn size(&self) -> PhysicalSize<u32> + { + self.inner.size().into() + } + + /// Returns the top-left corner position of the monitor relative to the larger full + /// screen area. + #[inline] + pub fn position(&self) -> PhysicalPosition<i32> + { + self.inner.position().into() + } + + /// Returns the scale factor of the underlying monitor. + #[inline] + pub fn scale_factor(&self) -> f64 + { + self.inner.scale_factor() + } +} + +impl Handle +{ + pub(super) fn from_winit_monitor_handle( + monitor: winit::monitor::MonitorHandle, + ) -> Self + { + Self { inner: monitor } + } +} diff --git a/engine/src/windowing/mouse.rs b/engine/src/windowing/mouse.rs new file mode 100644 index 0000000..3a43e79 --- /dev/null +++ b/engine/src/windowing/mouse.rs @@ -0,0 +1,197 @@ +use std::collections::HashMap; + +use crate::ecs::Sole; +use crate::reflection::Reflection; +use crate::vector::Vec2; +use crate::windowing::dpi::PhysicalPosition; + +#[derive(Debug, Default, Clone, Sole, Reflection)] +#[non_exhaustive] +pub struct Mouse +{ + /// Change in coordinates this tick. Unit is unspecified and platforms may use + /// different units. Updated automatically by the [`windowing extension`]. + /// + /// [`windowing extension`]: crate::windowing + pub curr_tick_position_delta: Vec2<f64>, + + /// Coordinates in pixels relative to the top-left corner of the window. May have + /// been affected by cursor acceleration + pub position: PhysicalPosition<f64>, + + pub curr_tick_scroll_delta: ScrollDelta, +} + +#[derive(Debug, Clone, Default, Reflection)] +pub struct ScrollDelta +{ + pub vert_lines: f32, + pub hor_lines: f32, +} + +impl ScrollDelta +{ + pub fn is_zero(&self) -> bool + { + self.vert_lines == 0.0 && self.hor_lines == 0.0 + } +} + +/// Mouse buttons. +#[derive(Debug, Default, Sole)] +pub struct Buttons +{ + map: HashMap<Button, ButtonData>, +} + +impl Buttons +{ + pub fn get(&self, button: Button) -> ButtonState + { + let Some(button_data) = self.map.get(&button) else { + return ButtonState::Released; + }; + + button_data.current_state + } + + pub fn get_previous(&self, button: Button) -> ButtonState + { + let Some(button_data) = self.map.get(&button) else { + return ButtonState::Released; + }; + + button_data.previous_state + } + + /// Returns a iterator that yields buttons and their current states. Only buttons with + /// states is included. + pub fn all_current(&self) -> impl Iterator<Item = (Button, ButtonState)> + use<'_> + { + self.map + .iter() + .map(|(button, button_data)| (button.clone(), button_data.current_state)) + } + + pub fn set(&mut self, button: Button, button_state: ButtonState) + { + let button_data = self.map.entry(button).or_default(); + + button_data.current_state = button_state; + } + + pub fn set_previous(&mut self, button: Button, button_state: ButtonState) + { + let button_data = self.map.entry(button).or_default(); + + button_data.previous_state = button_state; + } + + pub fn set_previous_to_current(&mut self, button: Button) + { + let button_data = self.map.entry(button).or_default(); + + button_data.previous_state = button_data.current_state; + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Button +{ + Left, + Right, + Middle, + Back, + Forward, + Other(u16), +} + +impl intmap::IntKey for Button +{ + type Int = u32; + + const PRIME: Self::Int = <u32 as intmap::IntKey>::PRIME; + + fn into_int(self) -> Self::Int + { + match &self { + Self::Left => 0, + Self::Right => 1, + Self::Middle => 2, + Self::Back => 3, + Self::Forward => 4, + Self::Other(other) => 5 + *other as u32, + } + } +} + +impl From<winit::event::MouseButton> for Button +{ + fn from(mouse_button: winit::event::MouseButton) -> Self + { + match mouse_button { + winit::event::MouseButton::Left => Self::Left, + winit::event::MouseButton::Right => Self::Right, + winit::event::MouseButton::Middle => Self::Middle, + winit::event::MouseButton::Back => Self::Back, + winit::event::MouseButton::Forward => Self::Forward, + winit::event::MouseButton::Other(other_mouse_button) => { + Self::Other(other_mouse_button) + } + } + } +} + +/// Mouse button state. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum ButtonState +{ + Pressed, + Released, +} + +impl ButtonState +{ + #[must_use] + #[inline] + pub fn is_pressed(&self) -> bool + { + matches!(self, Self::Pressed) + } + + #[must_use] + #[inline] + pub fn is_released(&self) -> bool + { + matches!(self, Self::Released) + } +} + +impl From<winit::event::ElementState> for ButtonState +{ + fn from(element_state: winit::event::ElementState) -> Self + { + match element_state { + winit::event::ElementState::Pressed => Self::Pressed, + winit::event::ElementState::Released => Self::Released, + } + } +} + +#[derive(Debug)] +struct ButtonData +{ + current_state: ButtonState, + previous_state: ButtonState, +} + +impl Default for ButtonData +{ + fn default() -> Self + { + Self { + current_state: ButtonState::Released, + previous_state: ButtonState::Released, + } + } +} diff --git a/engine/src/windowing/window.rs b/engine/src/windowing/window.rs new file mode 100644 index 0000000..6d7a464 --- /dev/null +++ b/engine/src/windowing/window.rs @@ -0,0 +1,291 @@ +use std::borrow::Cow; + +use crate::ecs::Component; +use crate::image::Image; +use crate::reflection::Reflection; +use crate::windowing::dpi::{PhysicalSize, Position, Size}; + +pub mod platform; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Id +{ + inner: winit::window::WindowId, +} + +impl intmap::IntKey for Id +{ + type Int = u64; + + const PRIME: Self::Int = <u64 as intmap::IntKey>::PRIME; + + fn into_int(self) -> Self::Int + { + self.inner.into() + } +} + +impl Id +{ + pub(crate) fn from_inner(inner: winit::window::WindowId) -> Self + { + Self { inner } + } +} + +#[derive(Debug, Component, Clone, Reflection)] +#[non_exhaustive] +pub struct CreationAttributes +{ + pub title: Cow<'static, str>, + pub transparent: bool, + pub maximized: bool, + pub fullscreen: Option<Fullscreen>, + pub visible: bool, + pub resizable: bool, + pub position: Option<Position>, + pub inner_size: Option<Size>, + pub icon: Option<Icon>, + + pub x_visual_id: Option<XVisualID>, + + // These do not have equivalents in winit::window::WindowAttributes + pub cursor_visible: bool, + pub cursor_grab_mode: CursorGrabMode, +} + +macro_rules! gen_creation_attrs_with_fn { + ($field: ident, $field_type: ty) => { + paste::paste! { + impl CreationAttributes + { + pub fn [<with_ $field>](mut self, new: impl Into<$field_type>) -> Self + { + self.$field = new.into(); + self + } + } + } + }; +} + +gen_creation_attrs_with_fn!(title, Cow<'static, str>); +gen_creation_attrs_with_fn!(transparent, bool); +gen_creation_attrs_with_fn!(maximized, bool); +gen_creation_attrs_with_fn!(fullscreen, Option<Fullscreen>); +gen_creation_attrs_with_fn!(visible, bool); +gen_creation_attrs_with_fn!(resizable, bool); +gen_creation_attrs_with_fn!(position, Option<Position>); +gen_creation_attrs_with_fn!(inner_size, Option<Size>); +gen_creation_attrs_with_fn!(icon, Option<Icon>); + +gen_creation_attrs_with_fn!(x_visual_id, Option<XVisualID>); + +gen_creation_attrs_with_fn!(cursor_visible, bool); +gen_creation_attrs_with_fn!(cursor_grab_mode, CursorGrabMode); + +impl CreationAttributes +{ + #[tracing::instrument(skip_all)] + pub(crate) fn into_window_attrs(self) -> winit::window::WindowAttributes + { + let mut window_attrs = winit::window::WindowAttributes::default() + .with_title(self.title.into_owned()) + .with_transparent(self.transparent) + .with_maximized(self.maximized) + .with_fullscreen(match self.fullscreen { + Some(Fullscreen::Borderless) => { + Some(winit::window::Fullscreen::Borderless(None)) + } + None => None, + }) + .with_visible(self.visible) + .with_resizable(self.resizable) + .with_window_icon(match self.icon { + Some(Icon::Image(image)) => { + let image_dimens = image.dimensions(); + + let result = winit::window::Icon::from_rgba( + image.into_rgba8().into_bytes(), + image_dimens.width, + image_dimens.height, + ); + + if let Err(err) = &result { + tracing::error!("Invalid icon image: {err}"); + } + + result.ok() + } + #[cfg(windows)] + Some(Icon::WindowsResource(resource_name)) => { + use winit::platform::windows::IconExtWindows; + + let result = winit::window::Icon::from_resource_name( + resource_name.as_ref(), + None, + ); + + if let Err(err) = &result { + tracing::error!( + "Invalid icon image in resource {resource_name}: {err}" + ); + } + + result.ok() + } + None => None, + }); + + window_attrs.position = self.position.map(Into::into); + window_attrs.inner_size = self.inner_size.map(Into::into); + + #[cfg(target_os = "linux")] + if let Some(visual_id) = self.x_visual_id { + use winit::platform::x11::WindowAttributesExtX11; + + return window_attrs.with_x11_visual(visual_id); + } + + window_attrs + } + + pub(crate) fn apply_extra_attrs_to_window(&self, window: &winit::window::Window) + { + window.set_cursor_visible(self.cursor_visible); + } +} + +impl Default for CreationAttributes +{ + fn default() -> Self + { + Self { + title: "Unnamed window".into(), + transparent: false, + maximized: false, + fullscreen: None, + visible: true, + resizable: true, + position: None, + inner_size: None, + icon: None, + x_visual_id: None, + cursor_visible: true, + cursor_grab_mode: CursorGrabMode::None, + } + } +} + +#[derive(Debug, Clone, Reflection)] +#[non_exhaustive] +pub enum Fullscreen +{ + Borderless, +} + +/// Window icon +#[derive(Debug, Clone, Reflection)] +#[non_exhaustive] +pub enum Icon +{ + Image(Image), + + #[cfg(windows)] + WindowsResource(Cow<'static, str>), +} + +#[derive(Debug, Default, Component, Clone, Copy, Reflection)] +pub struct CreationReady; + +#[derive(Debug, Component, Reflection)] +#[non_exhaustive] +pub struct Window +{ + pub title: String, + pub cursor_visible: bool, + pub cursor_grab_mode: CursorGrabMode, + pub inner_size: PhysicalSize<u32>, + wid: Id, + scale_factor: f64, +} + +impl Window +{ + pub fn wid(&self) -> Id + { + self.wid + } + + pub fn scale_factor(&self) -> f64 + { + self.scale_factor + } + + pub(crate) fn new( + winit_window: &winit::window::Window, + creation_attrs: &CreationAttributes, + ) -> Self + { + Self { + title: creation_attrs.title.clone().into_owned(), + cursor_visible: creation_attrs.cursor_visible, + cursor_grab_mode: creation_attrs.cursor_grab_mode, + wid: Id::from_inner(winit_window.id()), + inner_size: winit_window.inner_size().into(), + scale_factor: winit_window.scale_factor(), + } + } + + #[must_use] + pub(crate) fn apply(&self, winit_window: &winit::window::Window) -> ApplyResults + { + winit_window.set_title(&self.title); + winit_window.set_cursor_visible(self.cursor_visible); + + let curr_inner_size = winit_window.inner_size().clone().into(); + + let inner_size_request_result = match winit_window.request_inner_size( + winit::dpi::Size::Physical(self.inner_size.clone().into()), + ) { + // The comparison of curr_inner_size is in case the user's windowing system + // lies about using the requested inner size + None if curr_inner_size == self.inner_size => Ok(()), + None => Err(curr_inner_size), + Some(inner_size) => Err(inner_size.into()), + }; + + ApplyResults { + inner_size_request: inner_size_request_result, + } + } + + pub(crate) fn set_scale_factor(&mut self, scale_factor: f64) + { + self.scale_factor = scale_factor; + } +} + +#[derive(Debug, Component, Reflection)] +pub struct Closed; + +#[derive( + Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Reflection, +)] +pub enum CursorGrabMode +{ + #[default] + None, + + /// The cursor is locked to a specific position in the window. + Locked, +} + +/// A unique identifier for an X11 visual. +pub type XVisualID = u32; + +#[derive(Debug)] +pub(crate) struct ApplyResults +{ + pub inner_size_request: Result<(), PhysicalSize<u32>>, +} diff --git a/engine/src/windowing/window/platform.rs b/engine/src/windowing/window/platform.rs new file mode 100644 index 0000000..1f38f3b --- /dev/null +++ b/engine/src/windowing/window/platform.rs @@ -0,0 +1,12 @@ +#[cfg(target_os = "linux")] +pub mod x11 +{ + use std::ffi::c_void; + + pub type XlibErrorHook = Box<dyn Fn(*mut c_void, *mut c_void) -> bool + Send + Sync>; + + pub fn register_xlib_error_hook(hook: XlibErrorHook) + { + winit::platform::x11::register_xlib_error_hook(hook); + } +} |
