use crate::{ controller::*, error::Error, game_input::GameInput, render::Renderer, settings::{gamepad::con_settings::LayerEntry, ControlSettings, Settings}, ui, }; use common_base::span; use crossbeam_channel as channel; use gilrs::{EventType, Gilrs}; use hashbrown::HashMap; use itertools::Itertools; use keyboard_keynames::key_layout::KeyLayout; use serde::{Deserialize, Serialize}; use tracing::{error, warn}; use vek::*; use winit::monitor::VideoMode; /// Represents a key that the game menus recognise after input mapping #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub enum MenuInput { Up, Down, Left, Right, ScrollUp, ScrollDown, ScrollLeft, ScrollRight, Home, End, Apply, Back, Exit, } #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub enum AnalogMenuInput { MoveX(f32), MoveY(f32), ScrollX(f32), ScrollY(f32), } #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub enum AnalogGameInput { MovementX(f32), MovementY(f32), CameraX(f32), CameraY(f32), } /// Represents an incoming event from the window. #[derive(Clone, Debug)] pub enum Event { /// The window has been requested to close. Close, /// The window has been resized. Resize(Vec2), /// The window scale factor has been changed ScaleFactorChanged(f64), /// The window has been moved. Moved(Vec2), /// A key has been typed that corresponds to a specific character. Char(char), /// The cursor has been panned across the screen while grabbed. CursorPan(Vec2), /// The cursor has been moved across the screen while ungrabbed. CursorMove(Vec2), /// A mouse button has been pressed or released MouseButton(MouseButton, PressState), /// The camera has been requested to zoom. Zoom(f32), /// A key that the game recognises has been pressed or released. InputUpdate(GameInput, bool), /// Event that the ui uses. Ui(ui::Event), /// Event that the iced ui uses. IcedUi(ui::ice::Event), /// The view distance has changed. ViewDistanceChanged(u32), /// Game settings have changed. SettingsChanged, /// The window is (un)focused Focused(bool), /// A key that the game recognises for menu navigation has been pressed or /// released MenuInput(MenuInput, bool), /// Update of the analog inputs recognized by the menus AnalogMenuInput(AnalogMenuInput), /// Update of the analog inputs recognized by the game AnalogGameInput(AnalogGameInput), /// We tried to save a screenshot ScreenshotMessage(String), } pub type MouseButton = winit::event::MouseButton; pub type PressState = winit::event::ElementState; pub type EventLoop = winit::event_loop::EventLoop<()>; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub enum KeyMouse { Key(winit::event::VirtualKeyCode), Mouse(winit::event::MouseButton), ScanKey(winit::event::ScanCode), } impl KeyMouse { /// Returns key description (e.g Left Shift) pub fn display_string(&self, key_layout: &Option) -> String { use self::KeyMouse::*; use winit::event::{MouseButton, VirtualKeyCode::*}; let key_string = match self { Key(Key1) => "1", Key(Key2) => "2", Key(Key3) => "3", Key(Key4) => "4", Key(Key5) => "5", Key(Key6) => "6", Key(Key7) => "7", Key(Key8) => "8", Key(Key9) => "9", Key(Key0) => "0", Key(A) => "A", Key(B) => "B", Key(C) => "C", Key(D) => "D", Key(E) => "E", Key(F) => "F", Key(G) => "G", Key(H) => "H", Key(I) => "I", Key(J) => "J", Key(K) => "K", Key(L) => "L", Key(M) => "M", Key(N) => "N", Key(O) => "O", Key(P) => "P", Key(Q) => "Q", Key(R) => "R", Key(S) => "S", Key(T) => "T", Key(U) => "U", Key(V) => "V", Key(W) => "W", Key(X) => "X", Key(Y) => "Y", Key(Z) => "Z", Key(Escape) => "ESC", Key(F1) => "F1", Key(F2) => "F2", Key(F3) => "F3", Key(F4) => "F4", Key(F5) => "F5", Key(F6) => "F6", Key(F7) => "F7", Key(F8) => "F8", Key(F9) => "F9", Key(F10) => "F10", Key(F11) => "F11", Key(F12) => "F12", Key(F13) => "F13", Key(F14) => "F14", Key(F15) => "F15", Key(F16) => "F16", Key(F17) => "F17", Key(F18) => "F18", Key(F19) => "F19", Key(F20) => "F20", Key(F21) => "F21", Key(F22) => "F22", Key(F23) => "F23", Key(F24) => "F24", Key(Snapshot) => "Print Screen", Key(Scroll) => "Scroll Lock", Key(Pause) => "Pause/Break", Key(Insert) => "Insert", Key(Home) => "Home", Key(Delete) => "Delete", Key(End) => "End", Key(PageDown) => "PageDown", Key(PageUp) => "PageUp", Key(Left) => "Left Arrow", Key(Up) => "Up Arrow", Key(Right) => "Right Arrow", Key(Down) => "Down Arrow", Key(Back) => "Backspace", Key(Return) => "Enter", Key(Space) => "Space", Key(Compose) => "Compose", Key(Caret) => "^", Key(Numlock) => "Numlock", Key(Numpad0) => "Numpad 0", Key(Numpad1) => "Numpad 1", Key(Numpad2) => "Numpad 2", Key(Numpad3) => "Numpad 3", Key(Numpad4) => "Numpad 4", Key(Numpad5) => "Numpad 5", Key(Numpad6) => "Numpad 6", Key(Numpad7) => "Numpad 7", Key(Numpad8) => "Numpad 8", Key(Numpad9) => "Numpad 9", Key(AbntC1) => "Abnt C1", Key(AbntC2) => "Abnt C2", Key(NumpadAdd) => "Numpad +", Key(Apostrophe) => "'", Key(Apps) => "Context Menu", Key(At) => "@", Key(Ax) => "Ax", Key(Backslash) => "\\", Key(Calculator) => "Calculator", Key(Capital) => "Caps Lock", Key(Colon) => ":", Key(Comma) => ",", Key(Convert) => "Convert", Key(NumpadDecimal) => "Numpad .", Key(NumpadDivide) => "Numpad /", Key(Equals) => "=", Key(Grave) => "`", Key(Kana) => "Kana", Key(Kanji) => "Kanji", Key(LBracket) => "[", Key(RBracket) => "]", Key(Mail) => "Mail", Key(MediaSelect) => "MediaSelect", Key(MediaStop) => "MediaStop", Key(Minus) => "-", Key(Plus) => "+", Key(NumpadMultiply) => "Numpad *", Key(Mute) => "Mute", Key(MyComputer) => "My Computer", Key(NavigateBackward) => "Navigate Backward", Key(NavigateForward) => "Navigate Forward", Key(NoConvert) => "Non Convert", Key(NumpadComma) => "Num ,", Key(NumpadEnter) => "Num Enter", Key(NumpadEquals) => "Num =", Key(OEM102) => "<", Key(Period) => ".", Key(Power) => "Power", Key(PlayPause) => "Play / Pause", Key(PrevTrack) => "Prev Track", Key(NextTrack) => "Next Track", Key(LAlt) => { if cfg!(target_os = "macos") { "Left Option ⌥" } else { // Assume Windows, Linux, BSD, etc. "Left Alt" } }, Key(RAlt) => { if cfg!(target_os = "macos") { "Right Option ⌥" } else { // Assume Windows, Linux, BSD, etc. "Right Alt" } }, Key(LControl) => { if cfg!(target_os = "macos") { "Left Cmd ⌘" } else { // Assume Windows, Linux, BSD, etc. "Left Ctrl" } }, Key(RControl) => { if cfg!(target_os = "macos") { "Right Cmd ⌘" } else { // Assume Windows, Linux, BSD, etc. "Right Ctrl" } }, Key(LShift) => "Left Shift", Key(RShift) => "Right Shift", // Key doesn't usually have a right counterpart on modern keyboards, to omit the // qualifier. The exception to this is Mac OS which doesn't usually have // this key at all, so we keep the qualifier to minimise ambiguity. Key(LWin) => { if cfg!(target_family = "windows") { "Win ⊞" } else if cfg!(target_os = "macos") { "Left Cmd ⌘ (Super)" // Extra qualifier because both Ctrl and Win map to Cmd on Mac } else { // Assume Linux, BSD, etc. "Super" } }, // Most keyboards don't have this key, so throw in all the qualifiers Key(RWin) => { if cfg!(target_family = "windows") { "Right Win ⊞" } else if cfg!(target_os = "macos") { "Right Cmd ⌘ (Super)" // Extra qualifier because both Ctrl and Win map to Cmd on Mac } else { // Assume Linux, BSD, etc. "Right Super" } }, Key(Semicolon) => ";", Key(Slash) => "/", Key(Sleep) => "Sleep", Key(Stop) => "Media Stop", Key(NumpadSubtract) => "Num -", Key(Sysrq) => "Sysrq", Key(Tab) => "Tab", Key(Underline) => "_", Key(Unlabeled) => "No Name", Key(VolumeDown) => "Volume Down", Key(VolumeUp) => "Volume Up", Key(Wake) => "Wake", Key(WebBack) => "Browser Back", Key(WebFavorites) => "Browser Favorites", Key(WebForward) => "Browser Forward", Key(WebHome) => "Browser Home", Key(WebRefresh) => "Browser Refresh", Key(WebSearch) => "Browser Search", Key(WebStop) => "Browser Stop", Key(Yen) => "Yen", Key(Copy) => "Copy", Key(Paste) => "Paste", Key(Cut) => "Cut", Key(Asterisk) => "*", Mouse(MouseButton::Left) => "Left Click", Mouse(MouseButton::Right) => "Right Click", Mouse(MouseButton::Middle) => "Middle Click", Mouse(MouseButton::Other(button)) => { // Additional mouse buttons after middle click start at 1 return format!("Mouse {}", button + 3); }, ScanKey(scancode) => { return if let Some(layout) = key_layout { layout.get_key_as_string(*scancode) } else { format!("Unknown (0x{:X})", scancode) }; }, }; key_string.to_owned() } /// If it exists, returns the shortened version of a key name /// (e.g. Left Click -> M1) pub fn try_shortened(&self, _key_layout: &Option) -> Option { use self::KeyMouse::*; use winit::event::{MouseButton, VirtualKeyCode::*}; let key_string = match self { Mouse(MouseButton::Left) => "M1", Mouse(MouseButton::Right) => "M2", Mouse(MouseButton::Middle) => "M3", Mouse(MouseButton::Other(button)) => { // Additional mouse buttons after middle click start at 1 return Some(format!("M{}", button + 3)); }, Key(Back) => "Back", Key(LShift) => "LShft", Key(RShift) => "RShft", _ => return None, }; Some(key_string.to_owned()) } /// Returns shortest name of key (e.g. Left Click - M1) /// If key doesn't have shorter version, use regular one. /// /// Use it in case if space does really matter. pub fn display_shortest(&self, key_layout: &Option) -> String { self.try_shortened(key_layout) .unwrap_or_else(|| self.display_string(key_layout)) } } pub struct Window { renderer: Renderer, window: winit::window::Window, cursor_grabbed: bool, pub pan_sensitivity: u32, pub zoom_sensitivity: u32, pub zoom_inversion: bool, pub mouse_y_inversion: bool, fullscreen: FullScreenSettings, modifiers: winit::event::ModifiersState, // Track if at least one Resized event has occured since the last `fetch_events` call // Used for deduplication of resizes. resized: bool, scale_factor: f64, needs_refresh_resize: bool, keypress_map: HashMap, pub remapping_keybindings: Option, //true for remapping keybinds, false for clearing keybinds pub keybinding_mode: bool, events: Vec, pub focused: bool, gilrs: Option, pub controller_settings: ControllerSettings, pub controller_modifiers: Vec