winit_window/
lib.rs

1#![deny(missing_docs)]
2#![doc = include_str!("../README.md")]
3
4extern crate input;
5extern crate window;
6extern crate winit;
7
8use std::sync::Arc;
9use rustc_hash::FxHashMap;
10
11use input::{
12    Button, ButtonArgs, ButtonState, CloseArgs, Event, Input, Key, Motion, MouseButton, ResizeArgs,
13};
14use std::{collections::VecDeque, error::Error, time::Duration};
15use window::{AdvancedWindow, BuildFromWindowSettings, Position, Size, Window, WindowSettings};
16use winit::{
17    application::ApplicationHandler,
18    dpi::{LogicalPosition, LogicalSize},
19    event::{
20        DeviceId,
21        ElementState,
22        MouseScrollDelta,
23        WindowEvent,
24    },
25    event_loop::{ActiveEventLoop, EventLoop},
26    window::WindowId,
27};
28
29/// Settings for whether to ignore modifiers and use standard keyboard layouts instead.
30///
31/// This does not affect `piston::input::TextEvent`.
32///
33/// Piston uses the same key codes as in SDL2.
34/// The problem is that without knowing the keyboard layout,
35/// there is no coherent way of generating key codes.
36///
37/// This option choose different tradeoffs depending on need.
38#[derive(Copy, Clone, PartialEq, Eq, Debug)]
39pub enum KeyboardIgnoreModifiers {
40    /// Keep the key codes that are affected by modifiers.
41    ///
42    /// This is a good default for most applications.
43    /// However, make sure to display understandable information to the user.
44    ///
45    /// If you experience user problems among gamers,
46    /// then you might consider allowing other options in your game engine.
47    /// Some gamers might be used to how stuff works in other traditional game engines
48    /// and struggle understanding this configuration, depending on how you use keyboard layout.
49    None,
50    /// Assume the user's keyboard layout is standard English ABC.
51    ///
52    /// In some non-English speaking countries, this might be more user friendly for some gamers.
53    ///
54    /// This might sound counter-intuitive at first, so here is the reason:
55    ///
56    /// Gamers can customize their keyboard layout without needing to understand scan codes.
57    /// When gamers want physically accuracy with good default options,
58    /// they can simply use standard English ABC.
59    ///
60    /// In other cases, this option displays understandable information for game instructions.
61    /// This information makes it easier for users to correct the problem themselves.
62    ///
63    /// Most gaming consoles use standard controllers.
64    /// Typically, the only device that might be problematic for users is the keyboard.
65    /// Instead of solving this problem in your game engine, let users do it in the OS.
66    ///
67    /// This option gives more control to users and is also better for user data privacy.
68    /// Detecting keyboard layout is usually not needed.
69    /// Instead, provide options for the user where they can modify the keys.
70    /// If users want to switch layout in the middle of a game, they can do it through the OS.
71    AbcKeyCode,
72}
73
74/// Winit window backend for Piston.
75pub struct WinitWindow {
76    /// The event loop of the window.
77    ///
78    /// This is optional because when pumping events using `ApplicationHandler`,
79    /// the event loop can not be owned by `WinitWindow`.
80    pub event_loop: Option<EventLoop<UserEvent>>,
81    /// Sets keyboard layout.
82    ///
83    /// When set, the key codes are
84    pub keyboard_ignore_modifiers: KeyboardIgnoreModifiers,
85    /// The Winit window.
86    ///
87    /// This is optional because when creating the window,
88    /// it is only accessible by `ActiveEventLoop::create_window`,
89    /// which in turn requires `ApplicationHandler`.
90    /// One call to `Window::pull_event` is needed to trigger
91    /// Winit to call `ApplicationHandler::request_redraw`,
92    /// which creates the window.
93    pub window: Option<Arc<winit::window::Window>>,
94    /// Keeps track of connected devices.
95    pub devices: u32,
96    /// Maps device id to a unique id used by Piston.
97    pub device_id_map: FxHashMap<DeviceId, u32>,
98    // The window settings that created the window.
99    settings: WindowSettings,
100    // The back-end does not remember the title.
101    title: String,
102    exit_on_esc: bool,
103    should_close: bool,
104    automatic_close: bool,
105    // Used to fake capturing of cursor,
106    // to get relative mouse events.
107    is_capturing_cursor: bool,
108    // Stores the last known cursor position.
109    last_cursor_pos: Option<[f64; 2]>,
110    // Stores relative coordinates to emit on next poll.
111    mouse_relative: Option<(f64, f64)>,
112    // Used to filter repeated key presses (does not affect text repeat).
113    last_key_pressed: Option<input::Key>,
114    // Stores list of events ready for processing.
115    events: VecDeque<Event>,
116}
117
118/// Custom events for the winit event loop
119#[derive(Debug, PartialEq, Eq)]
120pub enum UserEvent {
121    /// Do nothing, just spin the event loop
122    WakeUp,
123}
124
125impl WinitWindow {
126    /// Creates a new window with window settings.
127    pub fn new(settings: &WindowSettings) -> Self {
128        let event_loop = EventLoop::with_user_event().build().unwrap();
129
130        let mut w = WinitWindow {
131            event_loop: Some(event_loop),
132            keyboard_ignore_modifiers: KeyboardIgnoreModifiers::None,
133            window: None,
134
135            settings: settings.clone(),
136            should_close: false,
137            automatic_close: settings.get_automatic_close(),
138            events: VecDeque::new(),
139            last_key_pressed: None,
140            is_capturing_cursor: false,
141            last_cursor_pos: None,
142            mouse_relative: None,
143            title: settings.get_title(),
144            exit_on_esc: settings.get_exit_on_esc(),
145
146            devices: 0,
147            device_id_map: FxHashMap::default(),
148        };
149        // Causes the window to be created through `ApplicationHandler::request_redraw`.
150        if let Some(e) = w.poll_event() {w.events.push_front(e)}
151        w
152    }
153
154    /// Gets a reference to the window.
155    ///
156    /// This is faster than [get_window], but borrows self.
157    pub fn get_window_ref(&self) -> &winit::window::Window {
158        self.window.as_ref().unwrap()
159    }
160
161    /// Returns a cloned smart pointer to the underlying Winit window.
162    pub fn get_window(&self) -> Arc<winit::window::Window> {
163        self.window.as_ref().unwrap().clone()
164    }
165
166    // These events are emitted before popping a new event from the queue.
167    // This is because Piston handles some events separately.
168    fn pre_pop_front_event(&mut self) -> Option<Input> {
169        use input::Motion;
170
171        // Check for a pending relative mouse move event.
172        if let Some((x, y)) = self.mouse_relative {
173            self.mouse_relative = None;
174            return Some(Input::Move(Motion::MouseRelative([x, y])));
175        }
176
177        None
178    }
179
180    fn handle_event(
181        &mut self,
182        event: winit::event::WindowEvent,
183        unknown: &mut bool,
184    ) -> Option<Input> {
185        use winit::keyboard::{Key, NamedKey};
186
187        match event {
188            WindowEvent::KeyboardInput { event: ref ev, .. } => {
189                if self.exit_on_esc {
190                    if let Key::Named(NamedKey::Escape) = ev.logical_key {
191                        self.set_should_close(true);
192                        return None;
193                    }
194                }
195                if let Some(s) = &ev.text {
196                    let s = s.to_string();
197                    let repeat = ev.repeat;
198                    if !repeat {
199                        if let Some(input) = map_window_event(
200                            event,
201                            self.get_window_ref().scale_factor(),
202                            self.keyboard_ignore_modifiers,
203                            unknown,
204                            &mut self.last_key_pressed,
205                            &mut self.devices,
206                            &mut self.device_id_map,
207                        ) {
208                            self.events.push_back(Event::Input(input, None));
209                        }
210                    }
211
212                    return Some(Input::Text(s));
213                }
214            }
215            WindowEvent::CursorMoved { position, .. } => {
216                let scale = self.get_window_ref().scale_factor();
217                let position = position.to_logical::<f64>(scale);
218                let x = f64::from(position.x);
219                let y = f64::from(position.y);
220
221                let pre_event = self.pre_pop_front_event();
222                let mut input = || {
223                    if let Some(pos) = self.last_cursor_pos {
224                        let dx = x - pos[0];
225                        let dy = y - pos[1];
226                        if self.is_capturing_cursor {
227                            self.last_cursor_pos = Some([x, y]);
228                            self.fake_capture();
229                            // Skip normal mouse movement and emit relative motion only.
230                            return Some(Input::Move(Motion::MouseRelative([dx as f64, dy as f64])));
231                        }
232                        // Send relative mouse movement next time.
233                        self.mouse_relative = Some((dx as f64, dy as f64));
234                    } else if self.is_capturing_cursor {
235                        // Ignore this event since mouse positions
236                        // should not be emitted when capturing cursor.
237                        self.last_cursor_pos = Some([x, y]);
238                        return None;
239                    }
240
241                    self.last_cursor_pos = Some([x, y]);
242                    return Some(Input::Move(Motion::MouseCursor([x, y])))
243                };
244
245                let input = input();
246                return if pre_event.is_some() {
247                    if let Some(input) = input {
248                        self.events.push_back(Event::Input(input, None));
249                    }
250                    pre_event
251                } else {input}
252            }
253            _ => {}
254        }
255
256        // Usual events are handled here and passed to user.
257        let input = map_window_event(
258            event,
259            self.get_window_ref().scale_factor(),
260            self.keyboard_ignore_modifiers,
261            unknown,
262            &mut self.last_key_pressed,
263            &mut self.devices,
264            &mut self.device_id_map,
265        );
266
267        let pre_event = self.pre_pop_front_event();
268        if pre_event.is_some() {
269            if let Some(input) = input {
270                self.events.push_back(Event::Input(input, None));
271            }
272            pre_event
273        } else {input}
274    }
275
276    fn fake_capture(&mut self) {
277        if let Some(pos) = self.last_cursor_pos {
278            // Fake capturing of cursor.
279            let size = self.size();
280            let cx = size.width / 2.0;
281            let cy = size.height / 2.0;
282            let dx = cx - pos[0];
283            let dy = cy - pos[1];
284            if dx != 0.0 || dy != 0.0 {
285                let pos = winit::dpi::LogicalPosition::new(cx, cy);
286                if let Ok(_) = self.get_window_ref().set_cursor_position(pos) {
287                    self.last_cursor_pos = Some([cx, cy]);
288                }
289            }
290        }
291    }
292}
293
294impl Window for WinitWindow {
295    fn set_should_close(&mut self, value: bool) {
296        self.should_close = value;
297    }
298
299    fn should_close(&self) -> bool {
300        self.should_close
301    }
302
303    fn size(&self) -> Size {
304        let window = self.get_window_ref();
305        let (w, h): (u32, u32) = window.inner_size().into();
306        let hidpi = window.scale_factor();
307        ((w as f64 / hidpi) as u32, (h as f64 / hidpi) as u32).into()
308    }
309
310    fn swap_buffers(&mut self) {}
311
312    fn wait_event(&mut self) -> Event {
313        use winit::platform::pump_events::EventLoopExtPumpEvents;
314        use input::{IdleArgs, Loop};
315
316        // Add all events we got to the event queue, since winit only allows us to get all pending
317        //  events at once.
318        if let Some(mut event_loop) = std::mem::replace(&mut self.event_loop, None) {
319            let event_loop_proxy = event_loop.create_proxy();
320            event_loop_proxy
321                .send_event(UserEvent::WakeUp)
322                .expect("Event loop is closed before property handling all events.");
323            event_loop.pump_app_events(None, self);
324            self.event_loop = Some(event_loop);
325        }
326
327        // Get the first event in the queue
328        let event = self.events.pop_front();
329
330        // Check if we got a close event, if we did we need to mark ourselves as should-close
331        if let &Some(Event::Input(Input::Close(_), ..)) = &event {
332            self.set_should_close(true);
333        }
334
335        event.unwrap_or(Event::Loop(Loop::Idle(IdleArgs {dt: 0.0})))
336    }
337
338    fn wait_event_timeout(&mut self, timeout: Duration) -> Option<Event> {
339        use winit::platform::pump_events::EventLoopExtPumpEvents;
340
341        // Add all events we got to the event queue, since winit only allows us to get all pending
342        //  events at once.
343        if let Some(mut event_loop) = std::mem::replace(&mut self.event_loop, None) {
344            let event_loop_proxy = event_loop.create_proxy();
345            event_loop_proxy
346                .send_event(UserEvent::WakeUp)
347                .expect("Event loop is closed before property handling all events.");
348            event_loop.pump_app_events(Some(timeout), self);
349            self.event_loop = Some(event_loop);
350        }
351
352        // Get the first event in the queue
353        let event = self.events.pop_front();
354
355        // Check if we got a close event, if we did we need to mark ourselves as should-close
356        if let &Some(Event::Input(Input::Close(_), ..)) = &event {
357            self.set_should_close(true);
358        }
359
360        event
361    }
362
363    fn poll_event(&mut self) -> Option<Event> {
364        use winit::platform::pump_events::EventLoopExtPumpEvents;
365
366        // Add all events we got to the event queue, since winit only allows us to get all pending
367        //  events at once.
368        if let Some(mut event_loop) = std::mem::replace(&mut self.event_loop, None) {
369            let event_loop_proxy = event_loop.create_proxy();
370            event_loop_proxy
371                .send_event(UserEvent::WakeUp)
372                .expect("Event loop is closed before property handling all events.");
373            event_loop.pump_app_events(Some(Duration::ZERO), self);
374            self.event_loop = Some(event_loop);
375        }
376
377        // Get the first event in the queue
378        let event = self.events.pop_front();
379
380        // Check if we got a close event, if we did we need to mark ourselves as should-close
381        if let &Some(Event::Input(Input::Close(_), ..)) = &event {
382            self.set_should_close(true);
383        }
384
385        event
386    }
387
388    fn draw_size(&self) -> Size {
389        let size: (f64, f64) = self.get_window_ref().inner_size().into();
390        size.into()
391    }
392}
393
394impl ApplicationHandler<UserEvent> for WinitWindow {
395    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
396        let settings = &self.settings;
397        let window = event_loop.create_window(winit::window::Window::default_attributes()
398            .with_inner_size(LogicalSize::<f64>::new(
399                settings.get_size().width.into(),
400                settings.get_size().height.into(),
401            ))
402            .with_title(settings.get_title())
403        ).unwrap();
404        self.window = Some(Arc::new(window));
405    }
406
407    fn window_event(
408            &mut self,
409            event_loop: &ActiveEventLoop,
410            _window_id: WindowId,
411            event: WindowEvent,
412        ) {
413            let window =  &self.get_window_ref();
414
415            match event {
416                WindowEvent::CloseRequested => {
417                    if self.automatic_close {
418                        self.should_close = true;
419                        event_loop.exit();
420                    }
421                }
422                WindowEvent::RedrawRequested => {
423                    window.request_redraw();
424                },
425                event => {
426                    let mut unknown = false;
427                    if let Some(ev) = self.handle_event(event, &mut unknown) {
428                        if !unknown {
429                            self.events.push_back(Event::Input(ev, None));
430                        }
431                    }
432                }
433            }
434        }
435}
436
437impl AdvancedWindow for WinitWindow {
438    fn get_title(&self) -> String {
439        self.title.clone()
440    }
441
442    fn set_title(&mut self, value: String) {
443        self.get_window_ref().set_title(&value);
444        self.title = value;
445    }
446
447    fn get_exit_on_esc(&self) -> bool {
448        self.exit_on_esc
449    }
450
451    fn set_exit_on_esc(&mut self, value: bool) {
452        self.exit_on_esc = value
453    }
454
455    fn set_capture_cursor(&mut self, value: bool) {
456        // Normally we would call `.set_cursor_grab`
457        // but since relative mouse events does not work,
458        // because device deltas have unspecified coordinates,
459        // the capturing of cursor is faked by hiding the cursor
460        // and setting the position to the center of window.
461        self.is_capturing_cursor = value;
462        self.get_window_ref().set_cursor_visible(!value);
463        if value {
464            self.fake_capture();
465        }
466    }
467
468    fn get_automatic_close(&self) -> bool {self.automatic_close}
469
470    fn set_automatic_close(&mut self, value: bool) {self.automatic_close = value}
471
472    fn show(&mut self) {
473        self.get_window_ref().set_visible(true);
474    }
475
476    fn hide(&mut self) {
477        self.get_window_ref().set_visible(false);
478    }
479
480    fn get_position(&self) -> Option<Position> {
481        self.get_window_ref()
482            .outer_position()
483            .map(|p| Position { x: p.x, y: p.y })
484            .ok()
485    }
486
487    fn set_position<P: Into<Position>>(&mut self, val: P) {
488        let val = val.into();
489        self.get_window_ref()
490            .set_outer_position(LogicalPosition::new(val.x as f64, val.y as f64))
491    }
492
493    fn set_size<S: Into<Size>>(&mut self, size: S) {
494        let size: Size = size.into();
495        let w = self.get_window_ref();
496        let _ = w.request_inner_size(LogicalSize::new(
497            size.width as f64,
498            size.height as f64,
499        ));
500    }
501}
502
503impl BuildFromWindowSettings for WinitWindow {
504    fn build_from_window_settings(settings: &WindowSettings) -> Result<Self, Box<dyn Error>> {
505        Ok(Self::new(settings))
506    }
507}
508
509fn map_key(input: &winit::event::KeyEvent, kim: KeyboardIgnoreModifiers) -> Key {
510    use winit::keyboard::NamedKey::*;
511    use winit::keyboard::Key::*;
512    use KeyboardIgnoreModifiers as KIM;
513
514    match input.logical_key {
515        Character(ref ch) => match ch.as_str() {
516            "0" | ")" if kim == KIM::AbcKeyCode => Key::D0,
517            "0" => Key::D0,
518            ")" => Key::RightParen,
519            "1" | "!" if kim == KIM::AbcKeyCode => Key::D1,
520            "1" => Key::D1,
521            "!" => Key::NumPadExclam,
522            "2" | "@" if kim == KIM::AbcKeyCode => Key::D2,
523            "2" => Key::D2,
524            "@" => Key::At,
525            "3" | "#" if kim == KIM::AbcKeyCode => Key::D3,
526            "3" => Key::D3,
527            "#" => Key::Hash,
528            "4" | "$" if kim == KIM::AbcKeyCode => Key::D4,
529            "4" => Key::D4,
530            "$" => Key::Dollar,
531            "5" | "%" if kim == KIM::AbcKeyCode => Key::D5,
532            "5" => Key::D5,
533            "%" => Key::Percent,
534            "6" | "^" if kim == KIM::AbcKeyCode => Key::D6,
535            "6" => Key::D6,
536            "^" => Key::Caret,
537            "7" | "&" if kim == KIM::AbcKeyCode => Key::D7,
538            "7" => Key::D7,
539            "&" => Key::Ampersand,
540            "8" | "*" if kim == KIM::AbcKeyCode => Key::D8,
541            "8" => Key::D8,
542            "*" => Key::Asterisk,
543            "9" | "(" if kim == KIM::AbcKeyCode => Key::D9,
544            "9" => Key::D9,
545            "(" => Key::LeftParen,
546            "a" | "A" => Key::A,
547            "b" | "B" => Key::B,
548            "c" | "C" => Key::C,
549            "d" | "D" => Key::D,
550            "e" | "E" => Key::E,
551            "f" | "F" => Key::F,
552            "g" | "G" => Key::G,
553            "h" | "H" => Key::H,
554            "i" | "I" => Key::I,
555            "j" | "J" => Key::J,
556            "k" | "K" => Key::K,
557            "l" | "L" => Key::L,
558            "m" | "M" => Key::M,
559            "n" | "N" => Key::N,
560            "o" | "O" => Key::O,
561            "p" | "P" => Key::P,
562            "q" | "Q" => Key::Q,
563            "r" | "R" => Key::R,
564            "s" | "S" => Key::S,
565            "t" | "T" => Key::T,
566            "u" | "U" => Key::U,
567            "v" | "V" => Key::V,
568            "w" | "W" => Key::W,
569            "x" | "X" => Key::X,
570            "y" | "Y" => Key::Y,
571            "z" | "Z" => Key::Z,
572            "'" | "\"" if kim == KIM::AbcKeyCode => Key::Quote,
573            "'" => Key::Quote,
574            "\"" => Key::Quotedbl,
575            ";" | ":" if kim == KIM::AbcKeyCode => Key::Semicolon,
576            ";" => Key::Semicolon,
577            ":" => Key::Colon,
578            "[" | "{" if kim == KIM::AbcKeyCode => Key::LeftBracket,
579            "[" => Key::LeftBracket,
580            "{" => Key::NumPadLeftBrace,
581            "]" | "}" if kim == KIM::AbcKeyCode => Key::RightBracket,
582            "]" => Key::RightBracket,
583            "}" => Key::NumPadRightBrace,
584            "\\" | "|" if kim == KIM::AbcKeyCode => Key::Backslash,
585            "\\" => Key::Backslash,
586            "|" => Key::NumPadVerticalBar,
587            "," | "<" if kim == KIM::AbcKeyCode => Key::Comma,
588            "," => Key::Comma,
589            "<" => Key::Less,
590            "." | ">" if kim == KIM::AbcKeyCode => Key::Period,
591            "." => Key::Period,
592            ">" => Key::Greater,
593            "/" | "?" if kim == KIM::AbcKeyCode => Key::Slash,
594            "/" => Key::Slash,
595            "?" => Key::Question,
596            "`" | "~" if kim == KIM::AbcKeyCode => Key::Backquote,
597            "`" => Key::Backquote,
598            // Piston v1.0 does not support `~` using modifier.
599            // Use `KeyboardIgnoreModifiers::AbcKeyCode` on window to fix this issue.
600            // It will be mapped to `Key::Backquote`.
601            "~" => Key::Unknown,
602            _ => Key::Unknown,
603        }
604        Named(Escape) => Key::Escape,
605        Named(F1) => Key::F1,
606        Named(F2) => Key::F2,
607        Named(F3) => Key::F3,
608        Named(F4) => Key::F4,
609        Named(F5) => Key::F5,
610        Named(F6) => Key::F6,
611        Named(F7) => Key::F7,
612        Named(F8) => Key::F8,
613        Named(F9) => Key::F9,
614        Named(F10) => Key::F10,
615        Named(F11) => Key::F11,
616        Named(F12) => Key::F12,
617        Named(F13) => Key::F13,
618        Named(F14) => Key::F14,
619        Named(F15) => Key::F15,
620
621        Named(Delete) => Key::Delete,
622
623        Named(ArrowLeft) => Key::Left,
624        Named(ArrowUp) => Key::Up,
625        Named(ArrowRight) => Key::Right,
626        Named(ArrowDown) => Key::Down,
627
628        Named(Backspace) => Key::Backspace,
629        Named(Enter) => Key::Return,
630        Named(Space) => Key::Space,
631
632        Named(Alt) => Key::LAlt,
633        Named(AltGraph) => Key::RAlt,
634        Named(Control) => Key::LCtrl,
635        Named(Super) => Key::Menu,
636        Named(Shift) => Key::LShift,
637
638        Named(Tab) => Key::Tab,
639        _ => Key::Unknown,
640    }
641}
642
643fn map_keyboard_input(
644    input: &winit::event::KeyEvent,
645    kim: KeyboardIgnoreModifiers,
646    unknown: &mut bool,
647    last_key_pressed: &mut Option<Key>,
648) -> Option<Input> {
649    let key = map_key(input, kim);
650
651    let state = if input.state == ElementState::Pressed {
652        // Filter repeated key presses (does not affect text repeat when holding keys).
653        if let Some(last_key) = &*last_key_pressed {
654            if last_key == &key {
655                *unknown = true;
656                return None;
657            }
658        }
659        *last_key_pressed = Some(key);
660
661        ButtonState::Press
662    } else {
663        if let Some(last_key) = &*last_key_pressed {
664            if last_key == &key {
665                *last_key_pressed = None;
666            }
667        }
668        ButtonState::Release
669    };
670
671    Some(Input::Button(ButtonArgs {
672        state: state,
673        button: Button::Keyboard(key),
674        scancode: if let winit::keyboard::PhysicalKey::Code(code) = input.physical_key {
675                Some(code as i32)
676            } else {None},
677    }))
678}
679
680/// Maps Winit's mouse button to Piston's mouse button.
681pub fn map_mouse(mouse_button: winit::event::MouseButton) -> MouseButton {
682    use winit::event::MouseButton as M;
683
684    match mouse_button {
685        M::Left => MouseButton::Left,
686        M::Right => MouseButton::Right,
687        M::Middle => MouseButton::Middle,
688        M::Other(0) => MouseButton::X1,
689        M::Other(1) => MouseButton::X2,
690        M::Other(2) => MouseButton::Button6,
691        M::Other(3) => MouseButton::Button7,
692        M::Other(4) => MouseButton::Button8,
693        _ => MouseButton::Unknown
694    }
695}
696
697/// Converts a winit's [`WindowEvent`] into a piston's [`Input`].
698///
699/// For some events that will not be passed to the user, returns `None`.
700fn map_window_event(
701    window_event: WindowEvent,
702    scale_factor: f64,
703    kim: KeyboardIgnoreModifiers,
704    unknown: &mut bool,
705    last_key_pressed: &mut Option<Key>,
706    devices: &mut u32,
707    device_id_map: &mut FxHashMap<DeviceId, u32>,
708) -> Option<Input> {
709    use input::FileDrag;
710
711    match window_event {
712        WindowEvent::DroppedFile(path) =>
713            Some(Input::FileDrag(FileDrag::Drop(path))),
714        WindowEvent::HoveredFile(path) =>
715            Some(Input::FileDrag(FileDrag::Hover(path))),
716        WindowEvent::HoveredFileCancelled =>
717            Some(Input::FileDrag(FileDrag::Cancel)),
718        WindowEvent::Resized(size) => Some(Input::Resize(ResizeArgs {
719            window_size: [size.width as f64, size.height as f64],
720            draw_size: Size {
721                width: size.width as f64,
722                height: size.height as f64,
723            }
724            .into(),
725        })),
726        WindowEvent::CloseRequested => Some(Input::Close(CloseArgs)),
727        WindowEvent::Destroyed => Some(Input::Close(CloseArgs)),
728        WindowEvent::Focused(focused) => Some(Input::Focus(focused)),
729        WindowEvent::KeyboardInput { ref event, .. } => {
730            map_keyboard_input(event, kim, unknown, last_key_pressed)
731        }
732        WindowEvent::CursorMoved { position, .. } => {
733            let position = position.to_logical(scale_factor);
734            Some(Input::Move(Motion::MouseCursor([position.x, position.y])))
735        }
736        WindowEvent::CursorEntered { .. } => Some(Input::Cursor(true)),
737        WindowEvent::CursorLeft { .. } => Some(Input::Cursor(false)),
738        WindowEvent::MouseWheel { delta, .. } => match delta {
739            MouseScrollDelta::PixelDelta(position) => {
740                let position = position.to_logical(scale_factor);
741                Some(Input::Move(Motion::MouseScroll([position.x, position.y])))
742            }
743            MouseScrollDelta::LineDelta(x, y) =>
744                Some(Input::Move(Motion::MouseScroll([x as f64, y as f64]))),
745        },
746        WindowEvent::MouseInput { state, button, .. } => {
747            let button = map_mouse(button);
748            let state = match state {
749                ElementState::Pressed => ButtonState::Press,
750                ElementState::Released => ButtonState::Release,
751            };
752
753            Some(Input::Button(ButtonArgs {
754                state,
755                button: Button::Mouse(button),
756                scancode: None,
757            }))
758        }
759        WindowEvent::AxisMotion { device_id, axis, value } => {
760            use input::ControllerAxisArgs;
761
762            Some(Input::Move(Motion::ControllerAxis(ControllerAxisArgs::new(
763                {
764                    if let Some(id) = device_id_map.get(&device_id) {*id}
765                    else {
766                        let id = *devices;
767                        *devices += 1;
768                        device_id_map.insert(device_id, id);
769                        id
770                    }
771                },
772                axis as u8,
773                value,
774            ))))
775        }
776        WindowEvent::Touch(winit::event::Touch { phase, location, id, .. }) => {
777            use winit::event::TouchPhase;
778            use input::{Touch, TouchArgs};
779
780            let location = location.to_logical::<f64>(scale_factor);
781
782            Some(Input::Move(Motion::Touch(TouchArgs::new(
783                0, id as i64, [location.x, location.y], 1.0, match phase {
784                    TouchPhase::Started => Touch::Start,
785                    TouchPhase::Moved => Touch::Move,
786                    TouchPhase::Ended => Touch::End,
787                    TouchPhase::Cancelled => Touch::Cancel
788                }
789            ))))
790        }
791        // Events not built-in by Piston v1.0.
792        // It is possible to use Piston's `Event::Custom`.
793        // This might be added as a library in the future to Piston's ecosystem.
794        WindowEvent::TouchpadPressure { .. } |
795        WindowEvent::PinchGesture { .. } |
796        WindowEvent::RotationGesture { .. } |
797        WindowEvent::PanGesture { .. } |
798        WindowEvent::DoubleTapGesture { .. } => None,
799        WindowEvent::ScaleFactorChanged { .. } => None,
800        WindowEvent::ActivationTokenDone { .. } => None,
801        WindowEvent::ThemeChanged(_) => None,
802        WindowEvent::Ime(_) => None,
803        WindowEvent::Occluded(_) => None,
804        WindowEvent::RedrawRequested { .. } => None,
805        WindowEvent::Moved(_) => None,
806        WindowEvent::ModifiersChanged(_) => None,
807    }
808}