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)"
307 } else {
308 "Super"
310 }
311 },
312 Key(RWin) => {
314 if cfg!(target_family = "windows") {
315 "Right Win"
316 } else if cfg!(target_os = "macos") {
317 "Right Cmd (Super)"
319 } else {
320 "Right Super"
322 }
323 },
324 Key(Semicolon) => ";",
325 Key(Slash) => "/",
326 Key(Sleep) => "Sleep",
327 Key(Stop) => "Media Stop",
328 Key(NumpadSubtract) => "Num -",
329 Key(Sysrq) => "Sysrq",
330 Key(Tab) => "Tab",
331 Key(Underline) => "_",
332 Key(Unlabeled) => "No Name",
333 Key(VolumeDown) => "Volume Down",
334 Key(VolumeUp) => "Volume Up",
335 Key(Wake) => "Wake",
336 Key(WebBack) => "Browser Back",
337 Key(WebFavorites) => "Browser Favorites",
338 Key(WebForward) => "Browser Forward",
339 Key(WebHome) => "Browser Home",
340 Key(WebRefresh) => "Browser Refresh",
341 Key(WebSearch) => "Browser Search",
342 Key(WebStop) => "Browser Stop",
343 Key(Yen) => "Yen",
344 Key(Copy) => "Copy",
345 Key(Paste) => "Paste",
346 Key(Cut) => "Cut",
347 Key(Asterisk) => "*",
348 Mouse(MouseButton::Left) => "Left Click",
349 Mouse(MouseButton::Right) => "Right Click",
350 Mouse(MouseButton::Middle) => "Middle Click",
351 Mouse(MouseButton::Other(button)) => {
352 return format!("Mouse {}", button + 3);
354 },
355 ScanKey(scancode) => {
356 return if let Some(layout) = key_layout {
357 layout.get_key_as_string(*scancode)
358 } else {
359 format!("Unknown (0x{:X})", scancode)
360 };
361 },
362 };
363
364 key_string.to_owned()
365 }
366
367 pub fn try_shortened(&self, _key_layout: &Option<KeyLayout>) -> Option<String> {
370 use self::KeyMouse::*;
371 use winit::event::{MouseButton, VirtualKeyCode::*};
372 let key_string = match self {
373 Mouse(MouseButton::Left) => "M1",
374 Mouse(MouseButton::Right) => "M2",
375 Mouse(MouseButton::Middle) => "M3",
376 Mouse(MouseButton::Other(button)) => {
377 return Some(format!("M{}", button + 3));
379 },
380 Key(Back) => "Back",
381 Key(LShift) => "LShft",
382 Key(RShift) => "RShft",
383 _ => return None,
384 };
385
386 Some(key_string.to_owned())
387 }
388
389 pub fn display_shortest(&self, key_layout: &Option<KeyLayout>) -> String {
394 self.try_shortened(key_layout)
395 .unwrap_or_else(|| self.display_string(key_layout))
396 }
397}
398
399pub struct Window {
400 renderer: Renderer,
401 window: winit::window::Window,
402 cursor_grabbed: bool,
403 pub pan_sensitivity: u32,
404 pub zoom_sensitivity: u32,
405 pub zoom_inversion: bool,
406 pub mouse_y_inversion: bool,
407 fullscreen: FullScreenSettings,
408 modifiers: winit::event::ModifiersState,
409 resized: bool,
412 scale_factor: f64,
413 needs_refresh_resize: bool,
414 keypress_map: HashMap<GameInput, winit::event::ElementState>,
415 pub remapping_keybindings: Option<GameInput>,
416 events: Vec<Event>,
417 pub focused: bool,
418 gilrs: Option<Gilrs>,
419 pub controller_settings: ControllerSettings,
420 pub controller_modifiers: Vec<Button>,
421 cursor_position: winit::dpi::PhysicalPosition<f64>,
422 mouse_emulation_vec: Vec2<f32>,
423 message_sender: channel::Sender<String>,
425 message_receiver: channel::Receiver<String>,
426 take_screenshot: bool,
428 toggle_fullscreen: bool,
429 pub key_layout: Option<KeyLayout>,
430}
431
432impl Window {
433 pub fn new(
434 settings: &Settings,
435 runtime: &tokio::runtime::Runtime,
436 ) -> Result<(Window, EventLoop), Error> {
437 let event_loop = EventLoop::new();
438
439 let window = settings.graphics.window;
440
441 let win_builder = winit::window::WindowBuilder::new()
442 .with_title("Veloren")
443 .with_inner_size(winit::dpi::LogicalSize::new(
444 window.size[0] as f64,
445 window.size[1] as f64,
446 ))
447 .with_maximized(window.maximised);
448
449 #[cfg(target_family = "windows")]
452 let win_builder = winit::platform::windows::WindowBuilderExtWindows::with_drag_and_drop(
453 win_builder,
454 false,
455 );
456
457 let window = win_builder.build(&event_loop).unwrap();
458
459 let renderer = Renderer::new(&window, settings.graphics.render_mode.clone(), runtime)?;
460
461 let keypress_map = HashMap::new();
462
463 let gilrs = match Gilrs::new() {
464 Ok(gilrs) => Some(gilrs),
465 Err(gilrs::Error::NotImplemented(_dummy)) => {
466 warn!("Controller input is unsupported on this platform.");
467 None
468 },
469 Err(gilrs::Error::InvalidAxisToBtn) => {
470 error!(
471 "Invalid AxisToBtn controller mapping. Falling back to no controller support."
472 );
473 None
474 },
475 Err(gilrs::Error::Other(e)) => {
476 error!(
477 ?e,
478 "Platform-specific error when creating a Gilrs instance. Falling back to no \
479 controller support."
480 );
481 None
482 },
483 Err(e) => {
484 error!(
485 ?e,
486 "Unspecified error when creating a Gilrs instance. Falling back to no \
487 controller support."
488 );
489 None
490 },
491 };
492
493 let controller_settings = ControllerSettings::from(&settings.controller);
494
495 let (message_sender, message_receiver): (
496 channel::Sender<String>,
497 channel::Receiver<String>,
498 ) = channel::unbounded::<String>();
499
500 let scale_factor = window.scale_factor();
501
502 let key_layout = match KeyLayout::new_from_window(&window) {
503 Ok(kl) => Some(kl),
504 Err(err) => {
505 warn!(
506 ?err,
507 "Failed to construct the scancode to keyname mapper, falling back to \
508 displaying Unknown(<scancode>)."
509 );
510 None
511 },
512 };
513
514 let mut this = Self {
515 renderer,
516 window,
517 cursor_grabbed: false,
518 pan_sensitivity: settings.gameplay.pan_sensitivity,
519 zoom_sensitivity: settings.gameplay.zoom_sensitivity,
520 zoom_inversion: settings.gameplay.zoom_inversion,
521 mouse_y_inversion: settings.gameplay.mouse_y_inversion,
522 fullscreen: FullScreenSettings::default(),
523 modifiers: Default::default(),
524 scale_factor,
525 resized: false,
526 needs_refresh_resize: false,
527 keypress_map,
528 remapping_keybindings: None,
529 events: Vec::new(),
530 focused: true,
531 gilrs,
532 controller_settings,
533 controller_modifiers: Vec::new(),
534 cursor_position: winit::dpi::PhysicalPosition::new(0.0, 0.0),
535 mouse_emulation_vec: Vec2::zero(),
536 message_sender,
538 message_receiver,
539 take_screenshot: false,
540 toggle_fullscreen: false,
541 key_layout,
542 };
543
544 this.set_fullscreen_mode(settings.graphics.fullscreen);
545
546 Ok((this, event_loop))
547 }
548
549 pub fn renderer(&self) -> &Renderer { &self.renderer }
550
551 pub fn renderer_mut(&mut self) -> &mut Renderer { &mut self.renderer }
552
553 pub fn resolve_deduplicated_events(
554 &mut self,
555 settings: &mut Settings,
556 config_dir: &std::path::Path,
557 ) {
558 if self.take_screenshot {
560 self.take_screenshot = false;
561 self.take_screenshot(settings);
562 }
563 if self.toggle_fullscreen {
564 self.toggle_fullscreen = false;
565 self.toggle_fullscreen(settings, config_dir);
566 }
567 }
568
569 #[expect(clippy::get_first)]
570 pub fn fetch_events(&mut self, settings: &mut Settings) -> Vec<Event> {
571 span!(_guard, "fetch_events", "Window::fetch_events");
572 if self.needs_refresh_resize {
574 let scale_factor = self.window.scale_factor();
575 let physical = self.window.inner_size();
576
577 let logical_size =
578 Vec2::from(<(f64, f64)>::from(physical.to_logical::<f64>(scale_factor)));
579 self.events
580 .push(Event::Ui(ui::Event::new_resize(logical_size)));
581 self.events.push(Event::IcedUi(iced::Event::Window(
582 iced::window::Event::Resized {
583 width: logical_size.x as u32,
584 height: logical_size.y as u32,
585 },
586 )));
587 self.events.push(Event::ScaleFactorChanged(scale_factor));
588 self.needs_refresh_resize = false;
589 }
590
591 if self.resized {
593 self.resized = false;
594 let physical = self.window.inner_size();
598 let scale_factor = self.window.scale_factor();
599 let is_maximized = self.window.is_maximized();
600
601 self.renderer
602 .on_resize(Vec2::new(physical.width, physical.height));
603 self.events
604 .push(Event::Resize(Vec2::new(physical.width, physical.height)));
605
606 let logical_size =
607 Vec2::from(<(f64, f64)>::from(physical.to_logical::<f64>(scale_factor)));
608
609 self.events
611 .push(Event::Ui(ui::Event::new_resize(logical_size)));
612 self.events.push(Event::IcedUi(iced::Event::Window(
613 iced::window::Event::Resized {
614 width: logical_size.x as u32,
615 height: logical_size.y as u32,
616 },
617 )));
618
619 if logical_size.x >= 1.0 && logical_size.y >= 1.0 {
624 settings.graphics.window.size = [logical_size.x as u32, logical_size.y as u32];
625 }
626 settings.graphics.window.maximised = is_maximized;
627 }
628
629 for message in self.message_receiver.try_iter() {
631 self.events.push(Event::ScreenshotMessage(message))
632 }
633
634 if let Some(gilrs) = &mut self.gilrs {
635 while let Some(event) = gilrs.next_event() {
636 fn handle_buttons(
637 settings: &ControllerSettings,
638 modifiers: &mut Vec<Button>,
639 events: &mut Vec<Event>,
640 button: &Button,
641 is_pressed: bool,
642 ) {
643 if settings.modifier_buttons.contains(button) {
644 if is_pressed {
645 modifiers.push(*button);
646 } else if let Some(index) =
652 modifiers.iter().position(|modifier| modifier == button)
653 {
654 modifiers.remove(index);
655 }
656 }
657
658 let l_entry1 = LayerEntry {
660 button: *button,
661 mod1: modifiers.get(0).copied().unwrap_or_default(),
662 mod2: modifiers.get(1).copied().unwrap_or_default(),
663 };
664 let l_entry2 = LayerEntry {
665 button: *button,
666 mod1: modifiers.get(1).copied().unwrap_or_default(),
667 mod2: modifiers.get(0).copied().unwrap_or_default(),
668 };
669
670 if let Some(evs) = settings.inverse_layer_button_map.get(&l_entry1) {
673 for ev in evs {
674 events.push(Event::InputUpdate(*ev, is_pressed));
675 }
676 } else if let Some(evs) = settings.inverse_layer_button_map.get(&l_entry2) {
677 for ev in evs {
678 events.push(Event::InputUpdate(*ev, is_pressed));
679 }
680 }
681 if let Some(evs) = settings.inverse_game_button_map.get(button) {
682 for ev in evs {
683 events.push(Event::InputUpdate(*ev, is_pressed));
684 }
685 }
686 if let Some(evs) = settings.inverse_menu_button_map.get(button) {
687 for ev in evs {
688 events.push(Event::MenuInput(*ev, is_pressed));
689 }
690 }
691 }
692
693 match event.event {
694 EventType::ButtonPressed(button, code)
695 | EventType::ButtonRepeated(button, code) => {
696 handle_buttons(
697 &self.controller_settings,
698 &mut self.controller_modifiers,
699 &mut self.events,
700 &Button::from((button, code)),
701 true,
702 );
703 },
704 EventType::ButtonReleased(button, code) => {
705 handle_buttons(
706 &self.controller_settings,
707 &mut self.controller_modifiers,
708 &mut self.events,
709 &Button::from((button, code)),
710 false,
711 );
712 },
713 EventType::ButtonChanged(button, _value, code) => {
714 if let Some(actions) = self
715 .controller_settings
716 .inverse_game_analog_button_map
717 .get(&AnalogButton::from((button, code)))
718 {
719 #[expect(clippy::never_loop)]
720 for action in actions {
721 match *action {}
722 }
723 }
724 if let Some(actions) = self
725 .controller_settings
726 .inverse_menu_analog_button_map
727 .get(&AnalogButton::from((button, code)))
728 {
729 #[expect(clippy::never_loop)]
730 for action in actions {
731 match *action {}
732 }
733 }
734 },
735
736 EventType::AxisChanged(axis, value, code) => {
737 let value = if self
738 .controller_settings
739 .inverted_axes
740 .contains(&Axis::from((axis, code)))
741 {
742 -value
743 } else {
744 value
745 };
746
747 let value = self
748 .controller_settings
749 .apply_axis_deadzone(&Axis::from((axis, code)), value);
750
751 if self.cursor_grabbed {
752 if let Some(actions) = self
753 .controller_settings
754 .inverse_game_axis_map
755 .get(&Axis::from((axis, code)))
756 {
757 for action in actions {
758 match *action {
759 AxisGameAction::MovementX => {
760 self.events.push(Event::AnalogGameInput(
761 AnalogGameInput::MovementX(value),
762 ));
763 },
764 AxisGameAction::MovementY => {
765 self.events.push(Event::AnalogGameInput(
766 AnalogGameInput::MovementY(value),
767 ));
768 },
769 AxisGameAction::CameraX => {
770 self.events.push(Event::AnalogGameInput(
771 AnalogGameInput::CameraX(
772 value
773 * self.controller_settings.pan_sensitivity
774 as f32
775 / 100.0,
776 ),
777 ));
778 },
779 AxisGameAction::CameraY => {
780 let pan_invert_y =
781 match self.controller_settings.pan_invert_y {
782 true => -1.0,
783 false => 1.0,
784 };
785
786 self.events.push(Event::AnalogGameInput(
787 AnalogGameInput::CameraY(
788 -value
789 * self.controller_settings.pan_sensitivity
790 as f32
791 * pan_invert_y
792 / 100.0,
793 ),
794 ));
795 },
796 }
797 }
798 }
799 } else if let Some(actions) = self
800 .controller_settings
801 .inverse_menu_axis_map
802 .get(&Axis::from((axis, code)))
803 {
804 for action in actions {
806 match *action {
807 AxisMenuAction::MoveX => {
808 self.events.push(Event::AnalogMenuInput(
809 AnalogMenuInput::MoveX(value),
810 ));
811 },
812 AxisMenuAction::MoveY => {
813 self.events.push(Event::AnalogMenuInput(
814 AnalogMenuInput::MoveY(value),
815 ));
816 },
817 AxisMenuAction::ScrollX => {
818 self.events.push(Event::AnalogMenuInput(
819 AnalogMenuInput::ScrollX(value),
820 ));
821 },
822 AxisMenuAction::ScrollY => {
823 self.events.push(Event::AnalogMenuInput(
824 AnalogMenuInput::ScrollY(value),
825 ));
826 },
827 }
828 }
829 }
830 },
831 _ => {},
832 }
833 }
834 }
835
836 let mut events = std::mem::take(&mut self.events);
837 if !self.cursor_grabbed {
840 events = events
841 .into_iter()
842 .filter_map(|event| match event {
843 Event::AnalogMenuInput(input) => match input {
844 AnalogMenuInput::MoveX(d) => {
845 self.mouse_emulation_vec.x = d;
846 None
847 },
848 AnalogMenuInput::MoveY(d) => {
849 self.mouse_emulation_vec.y = d * -1.0;
851 None
852 },
853 input => Some(Event::AnalogMenuInput(input)),
854 },
855 Event::MenuInput(MenuInput::Apply, state) => Some(match state {
856 true => Event::Ui(ui::Event(conrod_core::event::Input::Press(
857 conrod_core::input::Button::Mouse(
858 conrod_core::input::state::mouse::Button::Left,
859 ),
860 ))),
861 false => Event::Ui(ui::Event(conrod_core::event::Input::Release(
862 conrod_core::input::Button::Mouse(
863 conrod_core::input::state::mouse::Button::Left,
864 ),
865 ))),
866 }),
867 _ => Some(event),
868 })
869 .collect();
870
871 let sensitivity = self.controller_settings.mouse_emulation_sensitivity;
872 self.offset_cursor(self.mouse_emulation_vec * sensitivity as f32);
875 }
876
877 events
878 }
879
880 pub fn handle_device_event(&mut self, event: winit::event::DeviceEvent) {
881 use winit::event::DeviceEvent;
882
883 let mouse_y_inversion = match self.mouse_y_inversion {
884 true => -1.0,
885 false => 1.0,
886 };
887
888 match event {
889 DeviceEvent::MouseMotion {
890 delta: (dx, dy), ..
891 } if self.focused => {
892 let delta = Vec2::new(
893 dx as f32 * (self.pan_sensitivity as f32 / 100.0),
894 dy as f32 * (self.pan_sensitivity as f32 * mouse_y_inversion / 100.0),
895 );
896
897 if self.cursor_grabbed {
898 self.events.push(Event::CursorPan(delta));
899 } else {
900 self.events.push(Event::CursorMove(delta));
901 }
902 },
903 _ => {},
904 }
905 }
906
907 pub fn handle_window_event(
908 &mut self,
909 event: winit::event::WindowEvent,
910 settings: &mut Settings,
911 ) {
912 use winit::event::WindowEvent;
913
914 let controls = &mut settings.controls;
915
916 match event {
917 WindowEvent::CloseRequested => self.events.push(Event::Close),
918 WindowEvent::Resized(_) => {
919 self.resized = true;
920 },
921 WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
922 self.scale_factor = scale_factor;
924 self.events.push(Event::ScaleFactorChanged(scale_factor));
925 },
926 WindowEvent::Moved(winit::dpi::PhysicalPosition { x, y }) => {
927 self.events
928 .push(Event::Moved(Vec2::new(x as u32, y as u32)));
929 },
930 WindowEvent::ReceivedCharacter(c) => self.events.push(Event::Char(c)),
931 WindowEvent::MouseInput { button, state, .. } => {
932 if let (true, Some(game_inputs)) =
933 (
935 self.cursor_grabbed,
936 Window::map_input(
937 KeyMouse::Mouse(button),
938 controls,
939 &mut self.remapping_keybindings,
940 ),
941 )
942 {
943 for game_input in game_inputs {
944 self.events.push(Event::InputUpdate(
945 *game_input,
946 state == winit::event::ElementState::Pressed,
947 ));
948 }
949 }
950 self.events.push(Event::MouseButton(button, state));
951 },
952 WindowEvent::ModifiersChanged(modifiers) => self.modifiers = modifiers,
953 WindowEvent::KeyboardInput {
954 input,
955 is_synthetic,
956 ..
957 } => {
958 if matches!(
961 input.virtual_keycode,
962 Some(winit::event::VirtualKeyCode::Tab)
963 ) && is_synthetic
964 {
965 return;
966 }
967 if matches!(input, winit::event::KeyboardInput {
970 state: winit::event::ElementState::Pressed,
971 virtual_keycode: Some(winit::event::VirtualKeyCode::F4),
972 ..
973 }) && self.modifiers.alt()
974 {
975 return;
976 }
977
978 let input_key = match input.virtual_keycode {
979 Some(key) => KeyMouse::Key(key),
980 None => KeyMouse::ScanKey(input.scancode),
981 };
982
983 if let Some(game_inputs) =
984 Window::map_input(input_key, controls, &mut self.remapping_keybindings)
985 {
986 for game_input in game_inputs {
987 match game_input {
988 GameInput::Fullscreen => {
989 if input.state == winit::event::ElementState::Pressed
990 && !Self::is_pressed(
991 &mut self.keypress_map,
992 GameInput::Fullscreen,
993 )
994 {
995 self.toggle_fullscreen = !self.toggle_fullscreen;
996 }
997 Self::set_pressed(
998 &mut self.keypress_map,
999 GameInput::Fullscreen,
1000 input.state,
1001 );
1002 },
1003 GameInput::Screenshot => {
1004 self.take_screenshot = input.state
1005 == winit::event::ElementState::Pressed
1006 && !Self::is_pressed(
1007 &mut self.keypress_map,
1008 GameInput::Screenshot,
1009 );
1010 Self::set_pressed(
1011 &mut self.keypress_map,
1012 GameInput::Screenshot,
1013 input.state,
1014 );
1015 },
1016 _ => self.events.push(Event::InputUpdate(
1017 *game_input,
1018 input.state == winit::event::ElementState::Pressed,
1019 )),
1020 }
1021 }
1022 }
1023 },
1024 WindowEvent::Focused(state) => {
1025 self.focused = state;
1026 self.events.push(Event::Focused(state));
1027 },
1028 WindowEvent::CursorMoved { position, .. } => {
1029 if self.cursor_grabbed {
1030 self.reset_cursor_position();
1031 } else {
1032 self.cursor_position = position;
1033 }
1034 },
1035 WindowEvent::MouseWheel { delta, .. } if self.cursor_grabbed && self.focused => {
1036 const DIFFERENCE_FROM_DEVICE_EVENT_ON_X11: f32 = -15.0;
1037 self.events.push(Event::Zoom({
1038 let y = match delta {
1039 winit::event::MouseScrollDelta::LineDelta(_x, y) => y,
1040 winit::event::MouseScrollDelta::PixelDelta(pos) => (pos.y / 16.0) as f32,
1046 };
1047 y * (self.zoom_sensitivity as f32 / 100.0)
1048 * if self.zoom_inversion { -1.0 } else { 1.0 }
1049 * DIFFERENCE_FROM_DEVICE_EVENT_ON_X11
1050 }))
1051 },
1052 _ => {},
1053 }
1054 }
1055
1056 pub fn offset_cursor(&self, d: Vec2<f32>) {
1058 if d != Vec2::zero() {
1059 if let Err(err) = self
1060 .window
1061 .set_cursor_position(winit::dpi::LogicalPosition::new(
1062 d.x as f64 + self.cursor_position.x,
1063 d.y as f64 + self.cursor_position.y,
1064 ))
1065 {
1066 static SPAM_GUARD: std::sync::Once = std::sync::Once::new();
1068 SPAM_GUARD.call_once(|| {
1069 error!("Error setting cursor position: {:?}", err);
1070 })
1071 }
1072 }
1073 }
1074
1075 pub fn is_cursor_grabbed(&self) -> bool { self.cursor_grabbed }
1076
1077 pub fn grab_cursor(&mut self, grab: bool) {
1078 use winit::window::CursorGrabMode;
1079
1080 self.cursor_grabbed = grab;
1081 self.window.set_cursor_visible(!grab);
1082 let res = if grab {
1083 self.window
1084 .set_cursor_grab(CursorGrabMode::Locked)
1085 .or_else(|_e| self.window.set_cursor_grab(CursorGrabMode::Confined))
1086 } else {
1087 self.window.set_cursor_grab(CursorGrabMode::None)
1088 };
1089
1090 if let Err(e) = res {
1091 error!(?e, ?grab, "Failed to toggle cursor grab");
1092 }
1093 }
1094
1095 fn reset_cursor_position(&self) {
1099 if let Err(err) = self.window.set_cursor_position(self.cursor_position) {
1100 static SPAM_GUARD: std::sync::Once = std::sync::Once::new();
1102 SPAM_GUARD.call_once(|| {
1103 error!("Error resetting cursor position: {:?}", err);
1104 })
1105 }
1106 }
1107
1108 pub fn toggle_fullscreen(&mut self, settings: &mut Settings, config_dir: &std::path::Path) {
1109 let fullscreen = FullScreenSettings {
1110 enabled: !self.is_fullscreen(),
1111 ..settings.graphics.fullscreen
1112 };
1113
1114 self.set_fullscreen_mode(fullscreen);
1115 settings.graphics.fullscreen = fullscreen;
1116 settings.save_to_file_warn(config_dir);
1117 }
1118
1119 pub fn is_fullscreen(&self) -> bool { self.fullscreen.enabled }
1120
1121 fn select_video_mode_rec(
1125 &self,
1126 resolution: [u16; 2],
1127 bit_depth: Option<u16>,
1128 refresh_rate_millihertz: Option<u32>,
1129 correct_res: Option<Vec<VideoMode>>,
1130 correct_depth: Option<Option<VideoMode>>,
1131 correct_rate: Option<Option<VideoMode>>,
1132 ) -> Option<VideoMode> {
1133 let correct_res = match correct_res {
1137 Some(correct_res) => correct_res,
1138 None => self
1139 .window
1140 .current_monitor()?
1141 .video_modes()
1142 .filter(|mode| mode.size().width == resolution[0] as u32)
1143 .filter(|mode| mode.size().height == resolution[1] as u32)
1144 .collect(),
1145 };
1146
1147 match bit_depth {
1148 Some(depth) => {
1150 let correct_depth = correct_depth.unwrap_or_else(|| {
1152 correct_res
1153 .iter()
1154 .find(|mode| mode.bit_depth() == depth)
1155 .cloned()
1156 });
1157
1158 match refresh_rate_millihertz {
1159 Some(rate) => {
1161 let correct_rate = correct_rate.unwrap_or_else(|| {
1163 correct_res
1164 .iter()
1165 .find(|mode| mode.refresh_rate_millihertz() == rate)
1166 .cloned()
1167 });
1168
1169 correct_res
1174 .iter()
1175 .filter(|mode| mode.bit_depth() == depth)
1176 .find(|mode| mode.refresh_rate_millihertz() == rate)
1177 .cloned()
1178 .or_else(|| {
1179 if correct_depth.is_none() && correct_rate.is_none() {
1180 warn!(
1181 "Bit depth and refresh rate specified in settings are \
1182 incompatible with the monitor. Choosing highest bit \
1183 depth and refresh rate possible instead."
1184 );
1185 }
1186
1187 self.select_video_mode_rec(
1188 resolution,
1189 correct_depth.is_some().then_some(depth),
1190 correct_rate.is_some().then_some(rate),
1191 Some(correct_res),
1192 Some(correct_depth),
1193 Some(correct_rate),
1194 )
1195 })
1196 },
1197 None => match correct_depth {
1201 Some(mode) => Some(mode),
1202 None => {
1203 warn!(
1204 "Bit depth specified in settings is incompatible with the \
1205 monitor. Choosing highest bit depth possible instead."
1206 );
1207
1208 self.select_video_mode_rec(
1209 resolution,
1210 None,
1211 None,
1212 Some(correct_res),
1213 Some(correct_depth),
1214 None,
1215 )
1216 },
1217 },
1218 }
1219 },
1220 None => match refresh_rate_millihertz {
1222 Some(rate) => {
1224 let correct_rate = correct_rate.unwrap_or_else(|| {
1226 correct_res
1227 .iter()
1228 .find(|mode| mode.refresh_rate_millihertz() == rate)
1229 .cloned()
1230 });
1231
1232 match correct_rate {
1235 Some(mode) => Some(mode),
1236 None => {
1237 warn!(
1238 "Refresh rate specified in settings is incompatible with the \
1239 monitor. Choosing highest refresh rate possible instead."
1240 );
1241
1242 self.select_video_mode_rec(
1243 resolution,
1244 None,
1245 None,
1246 Some(correct_res),
1247 None,
1248 Some(correct_rate),
1249 )
1250 },
1251 }
1252 },
1253 None => correct_res
1257 .into_iter()
1258 .sorted_by_key(|mode| mode.bit_depth())
1260 .max_by_key(|mode| mode.refresh_rate_millihertz()),
1261 },
1262 }
1263 }
1264
1265 fn select_video_mode(
1266 &self,
1267 resolution: [u16; 2],
1268 bit_depth: Option<u16>,
1269 refresh_rate_millihertz: Option<u32>,
1270 ) -> Option<VideoMode> {
1271 match self.select_video_mode_rec(
1281 resolution,
1282 bit_depth,
1283 refresh_rate_millihertz,
1284 None,
1285 None,
1286 None,
1287 ) {
1288 Some(mode) => Some(mode),
1289 None => {
1292 warn!(
1293 "Resolution specified in settings is incompatible with the monitor. Choosing \
1294 highest resolution possible instead."
1295 );
1296 if let Some(monitor) = self.window.current_monitor() {
1297 let mode = monitor
1298 .video_modes()
1299 .sorted_by_key(|mode| mode.refresh_rate_millihertz())
1301 .sorted_by_key(|mode| mode.bit_depth())
1302 .max_by_key(|mode| mode.size().width);
1303
1304 if mode.is_none() {
1305 warn!("Failed to select video mode, no video modes available!!")
1306 }
1307
1308 mode
1309 } else {
1310 warn!("Failed to select video mode, can't get the current monitor!");
1311 None
1312 }
1313 },
1314 }
1315 }
1316
1317 pub fn set_fullscreen_mode(&mut self, fullscreen: FullScreenSettings) {
1318 let window = &self.window;
1319 self.fullscreen = fullscreen;
1320 window.set_fullscreen(fullscreen.enabled.then(|| match fullscreen.mode {
1321 FullscreenMode::Exclusive => {
1322 if let Some(video_mode) = self.select_video_mode(
1323 fullscreen.resolution,
1324 fullscreen.bit_depth,
1325 fullscreen.refresh_rate_millihertz,
1326 ) {
1327 winit::window::Fullscreen::Exclusive(video_mode)
1328 } else {
1329 warn!(
1330 "Failed to select a video mode for exclusive fullscreen. Falling back to \
1331 borderless fullscreen."
1332 );
1333 winit::window::Fullscreen::Borderless(None)
1334 }
1335 },
1336 FullscreenMode::Borderless => {
1337 winit::window::Fullscreen::Borderless(None)
1339 },
1340 }));
1341 }
1342
1343 pub fn needs_refresh_resize(&mut self) { self.needs_refresh_resize = true; }
1344
1345 pub fn set_size(&mut self, new_size: Vec2<u32>) {
1346 self.window.set_inner_size(winit::dpi::LogicalSize::new(
1347 new_size.x as f64,
1348 new_size.y as f64,
1349 ));
1350 }
1351
1352 pub fn send_event(&mut self, event: Event) { self.events.push(event) }
1353
1354 pub fn take_screenshot(&mut self, settings: &Settings) {
1355 let sender = self.message_sender.clone();
1356 let mut path = settings.screenshots_path.clone();
1357 self.renderer.create_screenshot(move |image| {
1358 use std::time::SystemTime;
1359
1360 let image = match image {
1362 Ok(i) => i,
1363 Err(e) => {
1364 warn!(?e, "Couldn't generate screenshot");
1365 let _result = sender.send(format!("Error when generating screenshot: {}", e));
1366 return;
1367 },
1368 };
1369
1370 if !path.exists() {
1372 if let Err(e) = std::fs::create_dir_all(&path) {
1373 warn!(?e, ?path, "Couldn't create folder for screenshot");
1374 let _result =
1375 sender.send(String::from("Couldn't create folder for screenshot"));
1376 }
1377 }
1378 path.push(format!(
1379 "screenshot_{}.png",
1380 SystemTime::now()
1381 .duration_since(SystemTime::UNIX_EPOCH)
1382 .map(|d| d.as_millis())
1383 .unwrap_or(0)
1384 ));
1385 if let Err(e) = image.save(&path) {
1387 warn!(?e, ?path, "Couldn't save screenshot");
1388 let _result = sender.send(String::from("Couldn't save screenshot"));
1389 } else {
1390 let _result =
1391 sender.send(format!("Screenshot saved to {}", path.to_string_lossy()));
1392 }
1393 });
1394 }
1395
1396 fn is_pressed(
1397 map: &mut HashMap<GameInput, winit::event::ElementState>,
1398 input: GameInput,
1399 ) -> bool {
1400 *(map
1401 .entry(input)
1402 .or_insert(winit::event::ElementState::Released))
1403 == winit::event::ElementState::Pressed
1404 }
1405
1406 fn set_pressed(
1407 map: &mut HashMap<GameInput, winit::event::ElementState>,
1408 input: GameInput,
1409 state: winit::event::ElementState,
1410 ) {
1411 map.insert(input, state);
1412 }
1413
1414 fn map_input<'a>(
1419 key_mouse: KeyMouse,
1420 controls: &'a mut ControlSettings,
1421 remapping: &mut Option<GameInput>,
1422 ) -> Option<impl Iterator<Item = &'a GameInput> + use<'a>> {
1423 match *remapping {
1424 Some(game_input) => {
1426 controls.modify_binding(game_input, key_mouse);
1427 *remapping = None;
1428 None
1429 },
1430 None => controls
1431 .get_associated_game_inputs(&key_mouse)
1432 .map(|game_inputs| game_inputs.iter()),
1433 }
1434 }
1435
1436 pub fn set_keybinding_mode(&mut self, game_input: GameInput) {
1437 self.remapping_keybindings = Some(game_input);
1438 }
1439
1440 pub fn window(&self) -> &winit::window::Window { &self.window }
1441
1442 pub fn modifiers(&self) -> winit::event::ModifiersState { self.modifiers }
1443
1444 pub fn scale_factor(&self) -> f64 { self.scale_factor }
1445}
1446
1447#[derive(Default, Copy, Clone, Hash, Eq, PartialEq, Debug, Serialize, Deserialize)]
1448pub enum FullscreenMode {
1449 Exclusive,
1450 #[serde(other)]
1451 #[default]
1452 Borderless,
1453}
1454
1455#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
1456#[serde(default)]
1457pub struct WindowSettings {
1458 pub size: [u32; 2],
1459 pub maximised: bool,
1460}
1461
1462impl Default for WindowSettings {
1463 fn default() -> Self {
1464 Self {
1465 size: [1280, 720],
1466 maximised: false,
1467 }
1468 }
1469}
1470
1471#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
1472#[serde(default)]
1473pub struct FullScreenSettings {
1474 pub enabled: bool,
1475 pub mode: FullscreenMode,
1476 pub resolution: [u16; 2],
1477 pub bit_depth: Option<u16>,
1478 pub refresh_rate_millihertz: Option<u32>,
1479}
1480
1481impl Default for FullScreenSettings {
1482 fn default() -> Self {
1483 Self {
1484 enabled: true,
1485 mode: FullscreenMode::Borderless,
1486 resolution: [1920, 1080],
1487 bit_depth: None,
1488 refresh_rate_millihertz: None,
1489 }
1490 }
1491}