1use crate::{
2 controller::*,
3 error::Error,
4 game_input::GameInput,
5 render::Renderer,
6 settings::{ControlSettings, Settings, gamepad::con_settings::LayerEntry},
7 ui,
8};
9use common_base::span;
10use crossbeam_channel as channel;
11use gilrs::{EventType, Gilrs};
12use hashbrown::HashMap;
13use itertools::Itertools;
14use keyboard_keynames::key_layout::KeyLayout;
15use serde::{Deserialize, Serialize};
16use strum::{AsRefStr, EnumIter};
17use tracing::{error, warn};
18use vek::*;
19use winit::monitor::VideoMode;
20
21#[derive(
23 Clone,
24 Copy,
25 Debug,
26 PartialEq,
27 Eq,
28 PartialOrd,
29 Ord,
30 Hash,
31 Deserialize,
32 Serialize,
33 AsRefStr,
34 EnumIter,
35)]
36pub enum MenuInput {
37 Up,
38 Down,
39 Left,
40 Right,
41 ScrollUp,
42 ScrollDown,
43 ScrollLeft,
44 ScrollRight,
45 Home,
46 End,
47 Apply,
48 Back,
49 Exit,
50}
51
52impl MenuInput {
53 pub fn get_localization_key(&self) -> &str { self.as_ref() }
54}
55
56#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
57pub enum AnalogMenuInput {
58 MoveX(f32),
59 MoveY(f32),
60 ScrollX(f32),
61 ScrollY(f32),
62}
63
64#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
65pub enum AnalogGameInput {
66 MovementX(f32),
67 MovementY(f32),
68 CameraX(f32),
69 CameraY(f32),
70}
71
72#[derive(Clone, Debug)]
74pub enum Event {
75 Close,
77 Resize(Vec2<u32>),
79 ScaleFactorChanged(f64),
81 Moved(Vec2<u32>),
83 Char(char),
85 CursorPan(Vec2<f32>),
87 CursorMove(Vec2<f32>),
89 MouseButton(MouseButton, PressState),
91 Zoom(f32),
93 InputUpdate(GameInput, bool),
95 Ui(ui::Event),
97 IcedUi(ui::ice::Event),
99 ViewDistanceChanged(u32),
101 SettingsChanged,
103 Focused(bool),
105 MenuInput(MenuInput, bool),
108 AnalogMenuInput(AnalogMenuInput),
110 AnalogGameInput(AnalogGameInput),
112 ScreenshotMessage(String),
114}
115
116pub type MouseButton = winit::event::MouseButton;
117pub type PressState = winit::event::ElementState;
118pub type EventLoop = winit::event_loop::EventLoop<()>;
119
120#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
121pub enum KeyMouse {
122 Key(winit::event::VirtualKeyCode),
123 Mouse(winit::event::MouseButton),
124 ScanKey(winit::event::ScanCode),
125}
126
127impl KeyMouse {
128 pub fn display_string(&self, key_layout: &Option<KeyLayout>) -> String {
130 use self::KeyMouse::*;
131 use winit::event::{MouseButton, VirtualKeyCode::*};
132 let key_string = match self {
133 Key(Key1) => "1",
134 Key(Key2) => "2",
135 Key(Key3) => "3",
136 Key(Key4) => "4",
137 Key(Key5) => "5",
138 Key(Key6) => "6",
139 Key(Key7) => "7",
140 Key(Key8) => "8",
141 Key(Key9) => "9",
142 Key(Key0) => "0",
143 Key(A) => "A",
144 Key(B) => "B",
145 Key(C) => "C",
146 Key(D) => "D",
147 Key(E) => "E",
148 Key(F) => "F",
149 Key(G) => "G",
150 Key(H) => "H",
151 Key(I) => "I",
152 Key(J) => "J",
153 Key(K) => "K",
154 Key(L) => "L",
155 Key(M) => "M",
156 Key(N) => "N",
157 Key(O) => "O",
158 Key(P) => "P",
159 Key(Q) => "Q",
160 Key(R) => "R",
161 Key(S) => "S",
162 Key(T) => "T",
163 Key(U) => "U",
164 Key(V) => "V",
165 Key(W) => "W",
166 Key(X) => "X",
167 Key(Y) => "Y",
168 Key(Z) => "Z",
169 Key(Escape) => "ESC",
170 Key(F1) => "F1",
171 Key(F2) => "F2",
172 Key(F3) => "F3",
173 Key(F4) => "F4",
174 Key(F5) => "F5",
175 Key(F6) => "F6",
176 Key(F7) => "F7",
177 Key(F8) => "F8",
178 Key(F9) => "F9",
179 Key(F10) => "F10",
180 Key(F11) => "F11",
181 Key(F12) => "F12",
182 Key(F13) => "F13",
183 Key(F14) => "F14",
184 Key(F15) => "F15",
185 Key(F16) => "F16",
186 Key(F17) => "F17",
187 Key(F18) => "F18",
188 Key(F19) => "F19",
189 Key(F20) => "F20",
190 Key(F21) => "F21",
191 Key(F22) => "F22",
192 Key(F23) => "F23",
193 Key(F24) => "F24",
194 Key(Snapshot) => "Print Screen",
195 Key(Scroll) => "Scroll Lock",
196 Key(Pause) => "Pause/Break",
197 Key(Insert) => "Insert",
198 Key(Home) => "Home",
199 Key(Delete) => "Delete",
200 Key(End) => "End",
201 Key(PageDown) => "PageDown",
202 Key(PageUp) => "PageUp",
203 Key(Left) => "Left Arrow",
204 Key(Up) => "Up Arrow",
205 Key(Right) => "Right Arrow",
206 Key(Down) => "Down Arrow",
207 Key(Back) => "Backspace",
208 Key(Return) => "Enter",
209 Key(Space) => "Space",
210 Key(Compose) => "Compose",
211 Key(Caret) => "^",
212 Key(Numlock) => "Numlock",
213 Key(Numpad0) => "Numpad 0",
214 Key(Numpad1) => "Numpad 1",
215 Key(Numpad2) => "Numpad 2",
216 Key(Numpad3) => "Numpad 3",
217 Key(Numpad4) => "Numpad 4",
218 Key(Numpad5) => "Numpad 5",
219 Key(Numpad6) => "Numpad 6",
220 Key(Numpad7) => "Numpad 7",
221 Key(Numpad8) => "Numpad 8",
222 Key(Numpad9) => "Numpad 9",
223 Key(AbntC1) => "Abnt C1",
224 Key(AbntC2) => "Abnt C2",
225 Key(NumpadAdd) => "Numpad +",
226 Key(Apostrophe) => "'",
227 Key(Apps) => "Context Menu",
228 Key(At) => "@",
229 Key(Ax) => "Ax",
230 Key(Backslash) => "\\",
231 Key(Calculator) => "Calculator",
232 Key(Capital) => "Caps Lock",
233 Key(Colon) => ":",
234 Key(Comma) => ",",
235 Key(Convert) => "Convert",
236 Key(NumpadDecimal) => "Numpad .",
237 Key(NumpadDivide) => "Numpad /",
238 Key(Equals) => "=",
239 Key(Grave) => "`",
240 Key(Kana) => "Kana",
241 Key(Kanji) => "Kanji",
242 Key(LBracket) => "[",
243 Key(RBracket) => "]",
244 Key(Mail) => "Mail",
245 Key(MediaSelect) => "MediaSelect",
246 Key(MediaStop) => "MediaStop",
247 Key(Minus) => "-",
248 Key(Plus) => "+",
249 Key(NumpadMultiply) => "Numpad *",
250 Key(Mute) => "Mute",
251 Key(MyComputer) => "My Computer",
252 Key(NavigateBackward) => "Navigate Backward",
253 Key(NavigateForward) => "Navigate Forward",
254 Key(NoConvert) => "Non Convert",
255 Key(NumpadComma) => "Num ,",
256 Key(NumpadEnter) => "Num Enter",
257 Key(NumpadEquals) => "Num =",
258 Key(OEM102) => "<",
259 Key(Period) => ".",
260 Key(Power) => "Power",
261 Key(PlayPause) => "Play / Pause",
262 Key(PrevTrack) => "Prev Track",
263 Key(NextTrack) => "Next Track",
264 Key(LAlt) => {
265 if cfg!(target_os = "macos") {
266 "Left Option ⌥"
267 } else {
268 "Left Alt"
270 }
271 },
272 Key(RAlt) => {
273 if cfg!(target_os = "macos") {
274 "Right Option ⌥"
275 } else {
276 "Right Alt"
278 }
279 },
280 Key(LControl) => {
281 if cfg!(target_os = "macos") {
282 "Left Cmd ⌘"
283 } else {
284 "Left Ctrl"
286 }
287 },
288 Key(RControl) => {
289 if cfg!(target_os = "macos") {
290 "Right Cmd ⌘"
291 } else {
292 "Right Ctrl"
294 }
295 },
296 Key(LShift) => "Left Shift",
297 Key(RShift) => "Right Shift",
298 Key(LWin) => {
302 if cfg!(target_family = "windows") {
303 "Win ⊞"
304 } else if cfg!(target_os = "macos") {
305 "Left Cmd ⌘ (Super)" } else {
307 "Super"
309 }
310 },
311 Key(RWin) => {
313 if cfg!(target_family = "windows") {
314 "Right Win ⊞"
315 } else if cfg!(target_os = "macos") {
316 "Right Cmd ⌘ (Super)" } else {
318 "Right Super"
320 }
321 },
322 Key(Semicolon) => ";",
323 Key(Slash) => "/",
324 Key(Sleep) => "Sleep",
325 Key(Stop) => "Media Stop",
326 Key(NumpadSubtract) => "Num -",
327 Key(Sysrq) => "Sysrq",
328 Key(Tab) => "Tab",
329 Key(Underline) => "_",
330 Key(Unlabeled) => "No Name",
331 Key(VolumeDown) => "Volume Down",
332 Key(VolumeUp) => "Volume Up",
333 Key(Wake) => "Wake",
334 Key(WebBack) => "Browser Back",
335 Key(WebFavorites) => "Browser Favorites",
336 Key(WebForward) => "Browser Forward",
337 Key(WebHome) => "Browser Home",
338 Key(WebRefresh) => "Browser Refresh",
339 Key(WebSearch) => "Browser Search",
340 Key(WebStop) => "Browser Stop",
341 Key(Yen) => "Yen",
342 Key(Copy) => "Copy",
343 Key(Paste) => "Paste",
344 Key(Cut) => "Cut",
345 Key(Asterisk) => "*",
346 Mouse(MouseButton::Left) => "Left Click",
347 Mouse(MouseButton::Right) => "Right Click",
348 Mouse(MouseButton::Middle) => "Middle Click",
349 Mouse(MouseButton::Other(button)) => {
350 return format!("Mouse {}", button + 3);
352 },
353 ScanKey(scancode) => {
354 return if let Some(layout) = key_layout {
355 layout.get_key_as_string(*scancode)
356 } else {
357 format!("Unknown (0x{:X})", scancode)
358 };
359 },
360 };
361
362 key_string.to_owned()
363 }
364
365 pub fn try_shortened(&self, _key_layout: &Option<KeyLayout>) -> Option<String> {
368 use self::KeyMouse::*;
369 use winit::event::{MouseButton, VirtualKeyCode::*};
370 let key_string = match self {
371 Mouse(MouseButton::Left) => "M1",
372 Mouse(MouseButton::Right) => "M2",
373 Mouse(MouseButton::Middle) => "M3",
374 Mouse(MouseButton::Other(button)) => {
375 return Some(format!("M{}", button + 3));
377 },
378 Key(Back) => "Back",
379 Key(LShift) => "LShft",
380 Key(RShift) => "RShft",
381 _ => return None,
382 };
383
384 Some(key_string.to_owned())
385 }
386
387 pub fn display_shortest(&self, key_layout: &Option<KeyLayout>) -> String {
392 self.try_shortened(key_layout)
393 .unwrap_or_else(|| self.display_string(key_layout))
394 }
395}
396
397pub struct Window {
398 renderer: Renderer,
399 window: winit::window::Window,
400 cursor_grabbed: bool,
401 pub pan_sensitivity: u32,
402 pub zoom_sensitivity: u32,
403 pub zoom_inversion: bool,
404 pub mouse_y_inversion: bool,
405 fullscreen: FullScreenSettings,
406 modifiers: winit::event::ModifiersState,
407 resized: bool,
410 scale_factor: f64,
411 needs_refresh_resize: bool,
412 keypress_map: HashMap<GameInput, winit::event::ElementState>,
413 pub remapping_keybindings: Option<GameInput>,
414 events: Vec<Event>,
415 pub focused: bool,
416 gilrs: Option<Gilrs>,
417 pub controller_settings: ControllerSettings,
418 pub controller_modifiers: Vec<Button>,
419 cursor_position: winit::dpi::PhysicalPosition<f64>,
420 mouse_emulation_vec: Vec2<f32>,
421 message_sender: channel::Sender<String>,
423 message_receiver: channel::Receiver<String>,
424 take_screenshot: bool,
426 toggle_fullscreen: bool,
427 pub key_layout: Option<KeyLayout>,
428}
429
430impl Window {
431 pub fn new(
432 settings: &Settings,
433 runtime: &tokio::runtime::Runtime,
434 ) -> Result<(Window, EventLoop), Error> {
435 let event_loop = EventLoop::new();
436
437 let window = settings.graphics.window;
438
439 let win_builder = winit::window::WindowBuilder::new()
440 .with_title("Veloren")
441 .with_inner_size(winit::dpi::LogicalSize::new(
442 window.size[0] as f64,
443 window.size[1] as f64,
444 ))
445 .with_maximized(window.maximised);
446
447 #[cfg(target_family = "windows")]
450 let win_builder = winit::platform::windows::WindowBuilderExtWindows::with_drag_and_drop(
451 win_builder,
452 false,
453 );
454
455 let window = win_builder.build(&event_loop).unwrap();
456
457 let renderer = Renderer::new(&window, settings.graphics.render_mode.clone(), runtime)?;
458
459 let keypress_map = HashMap::new();
460
461 let gilrs = match Gilrs::new() {
462 Ok(gilrs) => Some(gilrs),
463 Err(gilrs::Error::NotImplemented(_dummy)) => {
464 warn!("Controller input is unsupported on this platform.");
465 None
466 },
467 Err(gilrs::Error::InvalidAxisToBtn) => {
468 error!(
469 "Invalid AxisToBtn controller mapping. Falling back to no controller support."
470 );
471 None
472 },
473 Err(gilrs::Error::Other(e)) => {
474 error!(
475 ?e,
476 "Platform-specific error when creating a Gilrs instance. Falling back to no \
477 controller support."
478 );
479 None
480 },
481 Err(e) => {
482 error!(
483 ?e,
484 "Unspecified error when creating a Gilrs instance. Falling back to no \
485 controller support."
486 );
487 None
488 },
489 };
490
491 let controller_settings = ControllerSettings::from(&settings.controller);
492
493 let (message_sender, message_receiver): (
494 channel::Sender<String>,
495 channel::Receiver<String>,
496 ) = channel::unbounded::<String>();
497
498 let scale_factor = window.scale_factor();
499
500 let key_layout = match KeyLayout::new_from_window(&window) {
501 Ok(kl) => Some(kl),
502 Err(err) => {
503 warn!(
504 ?err,
505 "Failed to construct the scancode to keyname mapper, falling back to \
506 displaying Unknown(<scancode>)."
507 );
508 None
509 },
510 };
511
512 let mut this = Self {
513 renderer,
514 window,
515 cursor_grabbed: false,
516 pan_sensitivity: settings.gameplay.pan_sensitivity,
517 zoom_sensitivity: settings.gameplay.zoom_sensitivity,
518 zoom_inversion: settings.gameplay.zoom_inversion,
519 mouse_y_inversion: settings.gameplay.mouse_y_inversion,
520 fullscreen: FullScreenSettings::default(),
521 modifiers: Default::default(),
522 scale_factor,
523 resized: false,
524 needs_refresh_resize: false,
525 keypress_map,
526 remapping_keybindings: None,
527 events: Vec::new(),
528 focused: true,
529 gilrs,
530 controller_settings,
531 controller_modifiers: Vec::new(),
532 cursor_position: winit::dpi::PhysicalPosition::new(0.0, 0.0),
533 mouse_emulation_vec: Vec2::zero(),
534 message_sender,
536 message_receiver,
537 take_screenshot: false,
538 toggle_fullscreen: false,
539 key_layout,
540 };
541
542 this.set_fullscreen_mode(settings.graphics.fullscreen);
543
544 Ok((this, event_loop))
545 }
546
547 pub fn renderer(&self) -> &Renderer { &self.renderer }
548
549 pub fn renderer_mut(&mut self) -> &mut Renderer { &mut self.renderer }
550
551 pub fn resolve_deduplicated_events(
552 &mut self,
553 settings: &mut Settings,
554 config_dir: &std::path::Path,
555 ) {
556 if self.take_screenshot {
558 self.take_screenshot = false;
559 self.take_screenshot(settings);
560 }
561 if self.toggle_fullscreen {
562 self.toggle_fullscreen = false;
563 self.toggle_fullscreen(settings, config_dir);
564 }
565 }
566
567 #[expect(clippy::get_first)]
568 pub fn fetch_events(&mut self, settings: &mut Settings) -> Vec<Event> {
569 span!(_guard, "fetch_events", "Window::fetch_events");
570 if self.needs_refresh_resize {
572 let scale_factor = self.window.scale_factor();
573 let physical = self.window.inner_size();
574
575 let logical_size =
576 Vec2::from(<(f64, f64)>::from(physical.to_logical::<f64>(scale_factor)));
577 self.events
578 .push(Event::Ui(ui::Event::new_resize(logical_size)));
579 self.events.push(Event::IcedUi(iced::Event::Window(
580 iced::window::Event::Resized {
581 width: logical_size.x as u32,
582 height: logical_size.y as u32,
583 },
584 )));
585 self.events.push(Event::ScaleFactorChanged(scale_factor));
586 self.needs_refresh_resize = false;
587 }
588
589 if self.resized {
591 self.resized = false;
592 let physical = self.window.inner_size();
596 let scale_factor = self.window.scale_factor();
597 let is_maximized = self.window.is_maximized();
598
599 self.renderer
600 .on_resize(Vec2::new(physical.width, physical.height));
601 self.events
602 .push(Event::Resize(Vec2::new(physical.width, physical.height)));
603
604 let logical_size =
605 Vec2::from(<(f64, f64)>::from(physical.to_logical::<f64>(scale_factor)));
606
607 self.events
609 .push(Event::Ui(ui::Event::new_resize(logical_size)));
610 self.events.push(Event::IcedUi(iced::Event::Window(
611 iced::window::Event::Resized {
612 width: logical_size.x as u32,
613 height: logical_size.y as u32,
614 },
615 )));
616
617 if logical_size.x >= 1.0 && logical_size.y >= 1.0 {
622 settings.graphics.window.size = [logical_size.x as u32, logical_size.y as u32];
623 }
624 settings.graphics.window.maximised = is_maximized;
625 }
626
627 for message in self.message_receiver.try_iter() {
629 self.events.push(Event::ScreenshotMessage(message))
630 }
631
632 if let Some(gilrs) = &mut self.gilrs {
633 while let Some(event) = gilrs.next_event() {
634 fn handle_buttons(
635 settings: &ControllerSettings,
636 modifiers: &mut Vec<Button>,
637 events: &mut Vec<Event>,
638 button: &Button,
639 is_pressed: bool,
640 ) {
641 if settings.modifier_buttons.contains(button) {
642 if is_pressed {
643 modifiers.push(*button);
644 } else if let Some(index) =
650 modifiers.iter().position(|modifier| modifier == button)
651 {
652 modifiers.remove(index);
653 }
654 }
655
656 let l_entry1 = LayerEntry {
658 button: *button,
659 mod1: modifiers.get(0).copied().unwrap_or_default(),
660 mod2: modifiers.get(1).copied().unwrap_or_default(),
661 };
662 let l_entry2 = LayerEntry {
663 button: *button,
664 mod1: modifiers.get(1).copied().unwrap_or_default(),
665 mod2: modifiers.get(0).copied().unwrap_or_default(),
666 };
667
668 if let Some(evs) = settings.inverse_layer_button_map.get(&l_entry1) {
671 for ev in evs {
672 events.push(Event::InputUpdate(*ev, is_pressed));
673 }
674 } else if let Some(evs) = settings.inverse_layer_button_map.get(&l_entry2) {
675 for ev in evs {
676 events.push(Event::InputUpdate(*ev, is_pressed));
677 }
678 }
679 if let Some(evs) = settings.inverse_game_button_map.get(button) {
680 for ev in evs {
681 events.push(Event::InputUpdate(*ev, is_pressed));
682 }
683 }
684 if let Some(evs) = settings.inverse_menu_button_map.get(button) {
685 for ev in evs {
686 events.push(Event::MenuInput(*ev, is_pressed));
687 }
688 }
689 }
690
691 match event.event {
692 EventType::ButtonPressed(button, code)
693 | EventType::ButtonRepeated(button, code) => {
694 handle_buttons(
695 &self.controller_settings,
696 &mut self.controller_modifiers,
697 &mut self.events,
698 &Button::from((button, code)),
699 true,
700 );
701 },
702 EventType::ButtonReleased(button, code) => {
703 handle_buttons(
704 &self.controller_settings,
705 &mut self.controller_modifiers,
706 &mut self.events,
707 &Button::from((button, code)),
708 false,
709 );
710 },
711 EventType::ButtonChanged(button, _value, code) => {
712 if let Some(actions) = self
713 .controller_settings
714 .inverse_game_analog_button_map
715 .get(&AnalogButton::from((button, code)))
716 {
717 #[expect(clippy::never_loop)]
718 for action in actions {
719 match *action {}
720 }
721 }
722 if let Some(actions) = self
723 .controller_settings
724 .inverse_menu_analog_button_map
725 .get(&AnalogButton::from((button, code)))
726 {
727 #[expect(clippy::never_loop)]
728 for action in actions {
729 match *action {}
730 }
731 }
732 },
733
734 EventType::AxisChanged(axis, value, code) => {
735 let value = if self
736 .controller_settings
737 .inverted_axes
738 .contains(&Axis::from((axis, code)))
739 {
740 -value
741 } else {
742 value
743 };
744
745 let value = self
746 .controller_settings
747 .apply_axis_deadzone(&Axis::from((axis, code)), value);
748
749 if self.cursor_grabbed {
750 if let Some(actions) = self
751 .controller_settings
752 .inverse_game_axis_map
753 .get(&Axis::from((axis, code)))
754 {
755 for action in actions {
756 match *action {
757 AxisGameAction::MovementX => {
758 self.events.push(Event::AnalogGameInput(
759 AnalogGameInput::MovementX(value),
760 ));
761 },
762 AxisGameAction::MovementY => {
763 self.events.push(Event::AnalogGameInput(
764 AnalogGameInput::MovementY(value),
765 ));
766 },
767 AxisGameAction::CameraX => {
768 self.events.push(Event::AnalogGameInput(
769 AnalogGameInput::CameraX(
770 value
771 * self.controller_settings.pan_sensitivity
772 as f32
773 / 100.0,
774 ),
775 ));
776 },
777 AxisGameAction::CameraY => {
778 let pan_invert_y =
779 match self.controller_settings.pan_invert_y {
780 true => -1.0,
781 false => 1.0,
782 };
783
784 self.events.push(Event::AnalogGameInput(
785 AnalogGameInput::CameraY(
786 -value
787 * self.controller_settings.pan_sensitivity
788 as f32
789 * pan_invert_y
790 / 100.0,
791 ),
792 ));
793 },
794 }
795 }
796 }
797 } else if let Some(actions) = self
798 .controller_settings
799 .inverse_menu_axis_map
800 .get(&Axis::from((axis, code)))
801 {
802 for action in actions {
804 match *action {
805 AxisMenuAction::MoveX => {
806 self.events.push(Event::AnalogMenuInput(
807 AnalogMenuInput::MoveX(value),
808 ));
809 },
810 AxisMenuAction::MoveY => {
811 self.events.push(Event::AnalogMenuInput(
812 AnalogMenuInput::MoveY(value),
813 ));
814 },
815 AxisMenuAction::ScrollX => {
816 self.events.push(Event::AnalogMenuInput(
817 AnalogMenuInput::ScrollX(value),
818 ));
819 },
820 AxisMenuAction::ScrollY => {
821 self.events.push(Event::AnalogMenuInput(
822 AnalogMenuInput::ScrollY(value),
823 ));
824 },
825 }
826 }
827 }
828 },
829 _ => {},
830 }
831 }
832 }
833
834 let mut events = std::mem::take(&mut self.events);
835 if !self.cursor_grabbed {
838 events = events
839 .into_iter()
840 .filter_map(|event| match event {
841 Event::AnalogMenuInput(input) => match input {
842 AnalogMenuInput::MoveX(d) => {
843 self.mouse_emulation_vec.x = d;
844 None
845 },
846 AnalogMenuInput::MoveY(d) => {
847 self.mouse_emulation_vec.y = d * -1.0;
849 None
850 },
851 input => Some(Event::AnalogMenuInput(input)),
852 },
853 Event::MenuInput(MenuInput::Apply, state) => Some(match state {
854 true => Event::Ui(ui::Event(conrod_core::event::Input::Press(
855 conrod_core::input::Button::Mouse(
856 conrod_core::input::state::mouse::Button::Left,
857 ),
858 ))),
859 false => Event::Ui(ui::Event(conrod_core::event::Input::Release(
860 conrod_core::input::Button::Mouse(
861 conrod_core::input::state::mouse::Button::Left,
862 ),
863 ))),
864 }),
865 _ => Some(event),
866 })
867 .collect();
868
869 let sensitivity = self.controller_settings.mouse_emulation_sensitivity;
870 self.offset_cursor(self.mouse_emulation_vec * sensitivity as f32);
873 }
874
875 events
876 }
877
878 pub fn handle_device_event(&mut self, event: winit::event::DeviceEvent) {
879 use winit::event::DeviceEvent;
880
881 let mouse_y_inversion = match self.mouse_y_inversion {
882 true => -1.0,
883 false => 1.0,
884 };
885
886 match event {
887 DeviceEvent::MouseMotion {
888 delta: (dx, dy), ..
889 } if self.focused => {
890 let delta = Vec2::new(
891 dx as f32 * (self.pan_sensitivity as f32 / 100.0),
892 dy as f32 * (self.pan_sensitivity as f32 * mouse_y_inversion / 100.0),
893 );
894
895 if self.cursor_grabbed {
896 self.events.push(Event::CursorPan(delta));
897 } else {
898 self.events.push(Event::CursorMove(delta));
899 }
900 },
901 _ => {},
902 }
903 }
904
905 pub fn handle_window_event(
906 &mut self,
907 event: winit::event::WindowEvent,
908 settings: &mut Settings,
909 ) {
910 use winit::event::WindowEvent;
911
912 let controls = &mut settings.controls;
913
914 match event {
915 WindowEvent::CloseRequested => self.events.push(Event::Close),
916 WindowEvent::Resized(_) => {
917 self.resized = true;
918 },
919 WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
920 self.scale_factor = scale_factor;
922 self.events.push(Event::ScaleFactorChanged(scale_factor));
923 },
924 WindowEvent::Moved(winit::dpi::PhysicalPosition { x, y }) => {
925 self.events
926 .push(Event::Moved(Vec2::new(x as u32, y as u32)));
927 },
928 WindowEvent::ReceivedCharacter(c) => self.events.push(Event::Char(c)),
929 WindowEvent::MouseInput { button, state, .. } => {
930 if let (true, Some(game_inputs)) =
931 (
933 self.cursor_grabbed,
934 Window::map_input(
935 KeyMouse::Mouse(button),
936 controls,
937 &mut self.remapping_keybindings,
938 ),
939 )
940 {
941 for game_input in game_inputs {
942 self.events.push(Event::InputUpdate(
943 *game_input,
944 state == winit::event::ElementState::Pressed,
945 ));
946 }
947 }
948 self.events.push(Event::MouseButton(button, state));
949 },
950 WindowEvent::ModifiersChanged(modifiers) => self.modifiers = modifiers,
951 WindowEvent::KeyboardInput {
952 input,
953 is_synthetic,
954 ..
955 } => {
956 if matches!(
959 input.virtual_keycode,
960 Some(winit::event::VirtualKeyCode::Tab)
961 ) && is_synthetic
962 {
963 return;
964 }
965 if matches!(input, winit::event::KeyboardInput {
968 state: winit::event::ElementState::Pressed,
969 virtual_keycode: Some(winit::event::VirtualKeyCode::F4),
970 ..
971 }) && self.modifiers.alt()
972 {
973 return;
974 }
975
976 let input_key = match input.virtual_keycode {
977 Some(key) => KeyMouse::Key(key),
978 None => KeyMouse::ScanKey(input.scancode),
979 };
980
981 if let Some(game_inputs) =
982 Window::map_input(input_key, controls, &mut self.remapping_keybindings)
983 {
984 for game_input in game_inputs {
985 match game_input {
986 GameInput::Fullscreen => {
987 if input.state == winit::event::ElementState::Pressed
988 && !Self::is_pressed(
989 &mut self.keypress_map,
990 GameInput::Fullscreen,
991 )
992 {
993 self.toggle_fullscreen = !self.toggle_fullscreen;
994 }
995 Self::set_pressed(
996 &mut self.keypress_map,
997 GameInput::Fullscreen,
998 input.state,
999 );
1000 },
1001 GameInput::Screenshot => {
1002 self.take_screenshot = input.state
1003 == winit::event::ElementState::Pressed
1004 && !Self::is_pressed(
1005 &mut self.keypress_map,
1006 GameInput::Screenshot,
1007 );
1008 Self::set_pressed(
1009 &mut self.keypress_map,
1010 GameInput::Screenshot,
1011 input.state,
1012 );
1013 },
1014 _ => self.events.push(Event::InputUpdate(
1015 *game_input,
1016 input.state == winit::event::ElementState::Pressed,
1017 )),
1018 }
1019 }
1020 }
1021 },
1022 WindowEvent::Focused(state) => {
1023 self.focused = state;
1024 self.events.push(Event::Focused(state));
1025 },
1026 WindowEvent::CursorMoved { position, .. } => {
1027 if self.cursor_grabbed {
1028 self.reset_cursor_position();
1029 } else {
1030 self.cursor_position = position;
1031 }
1032 },
1033 WindowEvent::MouseWheel { delta, .. } if self.cursor_grabbed && self.focused => {
1034 const DIFFERENCE_FROM_DEVICE_EVENT_ON_X11: f32 = -15.0;
1035 self.events.push(Event::Zoom({
1036 let y = match delta {
1037 winit::event::MouseScrollDelta::LineDelta(_x, y) => y,
1038 winit::event::MouseScrollDelta::PixelDelta(pos) => (pos.y / 16.0) as f32,
1044 };
1045 y * (self.zoom_sensitivity as f32 / 100.0)
1046 * if self.zoom_inversion { -1.0 } else { 1.0 }
1047 * DIFFERENCE_FROM_DEVICE_EVENT_ON_X11
1048 }))
1049 },
1050 _ => {},
1051 }
1052 }
1053
1054 pub fn offset_cursor(&self, d: Vec2<f32>) {
1056 if d != Vec2::zero() {
1057 if let Err(err) = self
1058 .window
1059 .set_cursor_position(winit::dpi::LogicalPosition::new(
1060 d.x as f64 + self.cursor_position.x,
1061 d.y as f64 + self.cursor_position.y,
1062 ))
1063 {
1064 static SPAM_GUARD: std::sync::Once = std::sync::Once::new();
1066 SPAM_GUARD.call_once(|| {
1067 error!("Error setting cursor position: {:?}", err);
1068 })
1069 }
1070 }
1071 }
1072
1073 pub fn is_cursor_grabbed(&self) -> bool { self.cursor_grabbed }
1074
1075 pub fn grab_cursor(&mut self, grab: bool) {
1076 use winit::window::CursorGrabMode;
1077
1078 self.cursor_grabbed = grab;
1079 self.window.set_cursor_visible(!grab);
1080 let res = if grab {
1081 self.window
1082 .set_cursor_grab(CursorGrabMode::Locked)
1083 .or_else(|_e| self.window.set_cursor_grab(CursorGrabMode::Confined))
1084 } else {
1085 self.window.set_cursor_grab(CursorGrabMode::None)
1086 };
1087
1088 if let Err(e) = res {
1089 error!(?e, ?grab, "Failed to toggle cursor grab");
1090 }
1091 }
1092
1093 fn reset_cursor_position(&self) {
1097 if let Err(err) = self.window.set_cursor_position(self.cursor_position) {
1098 static SPAM_GUARD: std::sync::Once = std::sync::Once::new();
1100 SPAM_GUARD.call_once(|| {
1101 error!("Error resetting cursor position: {:?}", err);
1102 })
1103 }
1104 }
1105
1106 pub fn toggle_fullscreen(&mut self, settings: &mut Settings, config_dir: &std::path::Path) {
1107 let fullscreen = FullScreenSettings {
1108 enabled: !self.is_fullscreen(),
1109 ..settings.graphics.fullscreen
1110 };
1111
1112 self.set_fullscreen_mode(fullscreen);
1113 settings.graphics.fullscreen = fullscreen;
1114 settings.save_to_file_warn(config_dir);
1115 }
1116
1117 pub fn is_fullscreen(&self) -> bool { self.fullscreen.enabled }
1118
1119 fn select_video_mode_rec(
1123 &self,
1124 resolution: [u16; 2],
1125 bit_depth: Option<u16>,
1126 refresh_rate_millihertz: Option<u32>,
1127 correct_res: Option<Vec<VideoMode>>,
1128 correct_depth: Option<Option<VideoMode>>,
1129 correct_rate: Option<Option<VideoMode>>,
1130 ) -> Option<VideoMode> {
1131 let correct_res = match correct_res {
1135 Some(correct_res) => correct_res,
1136 None => self
1137 .window
1138 .current_monitor()?
1139 .video_modes()
1140 .filter(|mode| mode.size().width == resolution[0] as u32)
1141 .filter(|mode| mode.size().height == resolution[1] as u32)
1142 .collect(),
1143 };
1144
1145 match bit_depth {
1146 Some(depth) => {
1148 let correct_depth = correct_depth.unwrap_or_else(|| {
1150 correct_res
1151 .iter()
1152 .find(|mode| mode.bit_depth() == depth)
1153 .cloned()
1154 });
1155
1156 match refresh_rate_millihertz {
1157 Some(rate) => {
1159 let correct_rate = correct_rate.unwrap_or_else(|| {
1161 correct_res
1162 .iter()
1163 .find(|mode| mode.refresh_rate_millihertz() == rate)
1164 .cloned()
1165 });
1166
1167 correct_res
1172 .iter()
1173 .filter(|mode| mode.bit_depth() == depth)
1174 .find(|mode| mode.refresh_rate_millihertz() == rate)
1175 .cloned()
1176 .or_else(|| {
1177 if correct_depth.is_none() && correct_rate.is_none() {
1178 warn!(
1179 "Bit depth and refresh rate specified in settings are \
1180 incompatible with the monitor. Choosing highest bit \
1181 depth and refresh rate possible instead."
1182 );
1183 }
1184
1185 self.select_video_mode_rec(
1186 resolution,
1187 correct_depth.is_some().then_some(depth),
1188 correct_rate.is_some().then_some(rate),
1189 Some(correct_res),
1190 Some(correct_depth),
1191 Some(correct_rate),
1192 )
1193 })
1194 },
1195 None => match correct_depth {
1199 Some(mode) => Some(mode),
1200 None => {
1201 warn!(
1202 "Bit depth specified in settings is incompatible with the \
1203 monitor. Choosing highest bit depth possible instead."
1204 );
1205
1206 self.select_video_mode_rec(
1207 resolution,
1208 None,
1209 None,
1210 Some(correct_res),
1211 Some(correct_depth),
1212 None,
1213 )
1214 },
1215 },
1216 }
1217 },
1218 None => match refresh_rate_millihertz {
1220 Some(rate) => {
1222 let correct_rate = correct_rate.unwrap_or_else(|| {
1224 correct_res
1225 .iter()
1226 .find(|mode| mode.refresh_rate_millihertz() == rate)
1227 .cloned()
1228 });
1229
1230 match correct_rate {
1233 Some(mode) => Some(mode),
1234 None => {
1235 warn!(
1236 "Refresh rate specified in settings is incompatible with the \
1237 monitor. Choosing highest refresh rate possible instead."
1238 );
1239
1240 self.select_video_mode_rec(
1241 resolution,
1242 None,
1243 None,
1244 Some(correct_res),
1245 None,
1246 Some(correct_rate),
1247 )
1248 },
1249 }
1250 },
1251 None => correct_res
1255 .into_iter()
1256 .sorted_by_key(|mode| mode.bit_depth())
1258 .max_by_key(|mode| mode.refresh_rate_millihertz()),
1259 },
1260 }
1261 }
1262
1263 fn select_video_mode(
1264 &self,
1265 resolution: [u16; 2],
1266 bit_depth: Option<u16>,
1267 refresh_rate_millihertz: Option<u32>,
1268 ) -> Option<VideoMode> {
1269 match self.select_video_mode_rec(
1279 resolution,
1280 bit_depth,
1281 refresh_rate_millihertz,
1282 None,
1283 None,
1284 None,
1285 ) {
1286 Some(mode) => Some(mode),
1287 None => {
1290 warn!(
1291 "Resolution specified in settings is incompatible with the monitor. Choosing \
1292 highest resolution possible instead."
1293 );
1294 if let Some(monitor) = self.window.current_monitor() {
1295 let mode = monitor
1296 .video_modes()
1297 .sorted_by_key(|mode| mode.refresh_rate_millihertz())
1299 .sorted_by_key(|mode| mode.bit_depth())
1300 .max_by_key(|mode| mode.size().width);
1301
1302 if mode.is_none() {
1303 warn!("Failed to select video mode, no video modes available!!")
1304 }
1305
1306 mode
1307 } else {
1308 warn!("Failed to select video mode, can't get the current monitor!");
1309 None
1310 }
1311 },
1312 }
1313 }
1314
1315 pub fn set_fullscreen_mode(&mut self, fullscreen: FullScreenSettings) {
1316 let window = &self.window;
1317 self.fullscreen = fullscreen;
1318 window.set_fullscreen(fullscreen.enabled.then(|| match fullscreen.mode {
1319 FullscreenMode::Exclusive => {
1320 if let Some(video_mode) = self.select_video_mode(
1321 fullscreen.resolution,
1322 fullscreen.bit_depth,
1323 fullscreen.refresh_rate_millihertz,
1324 ) {
1325 winit::window::Fullscreen::Exclusive(video_mode)
1326 } else {
1327 warn!(
1328 "Failed to select a video mode for exclusive fullscreen. Falling back to \
1329 borderless fullscreen."
1330 );
1331 winit::window::Fullscreen::Borderless(None)
1332 }
1333 },
1334 FullscreenMode::Borderless => {
1335 winit::window::Fullscreen::Borderless(None)
1337 },
1338 }));
1339 }
1340
1341 pub fn needs_refresh_resize(&mut self) { self.needs_refresh_resize = true; }
1342
1343 pub fn set_size(&mut self, new_size: Vec2<u32>) {
1344 self.window.set_inner_size(winit::dpi::LogicalSize::new(
1345 new_size.x as f64,
1346 new_size.y as f64,
1347 ));
1348 }
1349
1350 pub fn send_event(&mut self, event: Event) { self.events.push(event) }
1351
1352 pub fn take_screenshot(&mut self, settings: &Settings) {
1353 let sender = self.message_sender.clone();
1354 let mut path = settings.screenshots_path.clone();
1355 self.renderer.create_screenshot(move |image| {
1356 use std::time::SystemTime;
1357
1358 let image = match image {
1360 Ok(i) => i,
1361 Err(e) => {
1362 warn!(?e, "Couldn't generate screenshot");
1363 let _result = sender.send(format!("Error when generating screenshot: {}", e));
1364 return;
1365 },
1366 };
1367
1368 if !path.exists() {
1370 if let Err(e) = std::fs::create_dir_all(&path) {
1371 warn!(?e, ?path, "Couldn't create folder for screenshot");
1372 let _result =
1373 sender.send(String::from("Couldn't create folder for screenshot"));
1374 }
1375 }
1376 path.push(format!(
1377 "screenshot_{}.png",
1378 SystemTime::now()
1379 .duration_since(SystemTime::UNIX_EPOCH)
1380 .map(|d| d.as_millis())
1381 .unwrap_or(0)
1382 ));
1383 if let Err(e) = image.save(&path) {
1385 warn!(?e, ?path, "Couldn't save screenshot");
1386 let _result = sender.send(String::from("Couldn't save screenshot"));
1387 } else {
1388 let _result =
1389 sender.send(format!("Screenshot saved to {}", path.to_string_lossy()));
1390 }
1391 });
1392 }
1393
1394 fn is_pressed(
1395 map: &mut HashMap<GameInput, winit::event::ElementState>,
1396 input: GameInput,
1397 ) -> bool {
1398 *(map
1399 .entry(input)
1400 .or_insert(winit::event::ElementState::Released))
1401 == winit::event::ElementState::Pressed
1402 }
1403
1404 fn set_pressed(
1405 map: &mut HashMap<GameInput, winit::event::ElementState>,
1406 input: GameInput,
1407 state: winit::event::ElementState,
1408 ) {
1409 map.insert(input, state);
1410 }
1411
1412 fn map_input<'a>(
1417 key_mouse: KeyMouse,
1418 controls: &'a mut ControlSettings,
1419 remapping: &mut Option<GameInput>,
1420 ) -> Option<impl Iterator<Item = &'a GameInput> + use<'a>> {
1421 match *remapping {
1422 Some(game_input) => {
1424 controls.modify_binding(game_input, key_mouse);
1425 *remapping = None;
1426 None
1427 },
1428 None => controls
1429 .get_associated_game_inputs(&key_mouse)
1430 .map(|game_inputs| game_inputs.iter()),
1431 }
1432 }
1433
1434 pub fn set_keybinding_mode(&mut self, game_input: GameInput) {
1435 self.remapping_keybindings = Some(game_input);
1436 }
1437
1438 pub fn window(&self) -> &winit::window::Window { &self.window }
1439
1440 pub fn modifiers(&self) -> winit::event::ModifiersState { self.modifiers }
1441
1442 pub fn scale_factor(&self) -> f64 { self.scale_factor }
1443}
1444
1445#[derive(Default, Copy, Clone, Hash, Eq, PartialEq, Debug, Serialize, Deserialize)]
1446pub enum FullscreenMode {
1447 Exclusive,
1448 #[serde(other)]
1449 #[default]
1450 Borderless,
1451}
1452
1453#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
1454#[serde(default)]
1455pub struct WindowSettings {
1456 pub size: [u32; 2],
1457 pub maximised: bool,
1458}
1459
1460impl Default for WindowSettings {
1461 fn default() -> Self {
1462 Self {
1463 size: [1280, 720],
1464 maximised: false,
1465 }
1466 }
1467}
1468
1469#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
1470#[serde(default)]
1471pub struct FullScreenSettings {
1472 pub enabled: bool,
1473 pub mode: FullscreenMode,
1474 pub resolution: [u16; 2],
1475 pub bit_depth: Option<u16>,
1476 pub refresh_rate_millihertz: Option<u32>,
1477}
1478
1479impl Default for FullScreenSettings {
1480 fn default() -> Self {
1481 Self {
1482 enabled: true,
1483 mode: FullscreenMode::Borderless,
1484 resolution: [1920, 1080],
1485 bit_depth: None,
1486 refresh_rate_millihertz: None,
1487 }
1488 }
1489}