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#[derive(Copy, Clone, PartialEq, Eq, Debug)]
39pub enum KeyboardIgnoreModifiers {
40 None,
50 AbcKeyCode,
72}
73
74pub struct WinitWindow {
76 pub event_loop: Option<EventLoop<UserEvent>>,
81 pub keyboard_ignore_modifiers: KeyboardIgnoreModifiers,
85 pub window: Option<Arc<winit::window::Window>>,
94 pub devices: u32,
96 pub device_id_map: FxHashMap<DeviceId, u32>,
98 settings: WindowSettings,
100 title: String,
102 exit_on_esc: bool,
103 should_close: bool,
104 automatic_close: bool,
105 is_capturing_cursor: bool,
108 last_cursor_pos: Option<[f64; 2]>,
110 mouse_relative: Option<(f64, f64)>,
112 last_key_pressed: Option<input::Key>,
114 events: VecDeque<Event>,
116}
117
118#[derive(Debug, PartialEq, Eq)]
120pub enum UserEvent {
121 WakeUp,
123}
124
125impl WinitWindow {
126 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 if let Some(e) = w.poll_event() {w.events.push_front(e)}
151 w
152 }
153
154 pub fn get_window_ref(&self) -> &winit::window::Window {
158 self.window.as_ref().unwrap()
159 }
160
161 pub fn get_window(&self) -> Arc<winit::window::Window> {
163 self.window.as_ref().unwrap().clone()
164 }
165
166 fn pre_pop_front_event(&mut self) -> Option<Input> {
169 use input::Motion;
170
171 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 return Some(Input::Move(Motion::MouseRelative([dx as f64, dy as f64])));
231 }
232 self.mouse_relative = Some((dx as f64, dy as f64));
234 } else if self.is_capturing_cursor {
235 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 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 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 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 let event = self.events.pop_front();
329
330 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 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 let event = self.events.pop_front();
354
355 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 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 let event = self.events.pop_front();
379
380 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 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 "~" => 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 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
680pub 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
697fn 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 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}