glutin_window/
lib.rs

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