1use platform_core::{Event, PointerButton, PointerSource, ScrollDelta};
2use winit::event::{ElementState, MouseScrollDelta, Touch, TouchPhase, WindowEvent};
3use winit::event::{Modifiers, MouseButton as WinitMouseButton};
4use winit::keyboard::{Key as WinitKey, KeyLocation, NamedKey as WinitNamedKey};
5
6pub fn map_key(key: &WinitKey, location: KeyLocation) -> Option<platform_core::Key> {
12 if location == KeyLocation::Numpad
13 && let Some(nk) = map_numpad(key)
14 {
15 return Some(platform_core::Key::Named(nk));
16 }
17 match key {
18 WinitKey::Character(c) => c.as_str().chars().next().map(platform_core::Key::Char),
19 WinitKey::Named(named) => map_named_key(*named).map(platform_core::Key::Named),
20 _ => None,
21 }
22}
23
24fn map_numpad(key: &WinitKey) -> Option<platform_core::NamedKey> {
25 use platform_core::NamedKey as Nk;
26 let nk = match key {
27 WinitKey::Named(WinitNamedKey::Enter) => Nk::NumpadEnter,
28 WinitKey::Character(c) => match c.as_str() {
29 "0" => Nk::Numpad0,
30 "1" => Nk::Numpad1,
31 "2" => Nk::Numpad2,
32 "3" => Nk::Numpad3,
33 "4" => Nk::Numpad4,
34 "5" => Nk::Numpad5,
35 "6" => Nk::Numpad6,
36 "7" => Nk::Numpad7,
37 "8" => Nk::Numpad8,
38 "9" => Nk::Numpad9,
39 "+" => Nk::NumpadAdd,
40 "-" => Nk::NumpadSubtract,
41 "*" => Nk::NumpadMultiply,
42 "/" => Nk::NumpadDivide,
43 "." | "," => Nk::NumpadDecimal,
45 _ => return None,
46 },
47 _ => return None,
48 };
49 Some(nk)
50}
51
52pub fn map_named_key(key: WinitNamedKey) -> Option<platform_core::NamedKey> {
55 let nk = match key {
56 WinitNamedKey::Enter => platform_core::NamedKey::Enter,
57 WinitNamedKey::Backspace => platform_core::NamedKey::Backspace,
58 WinitNamedKey::Escape => platform_core::NamedKey::Escape,
59 WinitNamedKey::Tab => platform_core::NamedKey::Tab,
60 WinitNamedKey::Delete => platform_core::NamedKey::Delete,
61 WinitNamedKey::Home => platform_core::NamedKey::Home,
62 WinitNamedKey::End => platform_core::NamedKey::End,
63 WinitNamedKey::PageUp => platform_core::NamedKey::PageUp,
64 WinitNamedKey::PageDown => platform_core::NamedKey::PageDown,
65 WinitNamedKey::ArrowUp => platform_core::NamedKey::ArrowUp,
66 WinitNamedKey::ArrowDown => platform_core::NamedKey::ArrowDown,
67 WinitNamedKey::ArrowLeft => platform_core::NamedKey::ArrowLeft,
68 WinitNamedKey::ArrowRight => platform_core::NamedKey::ArrowRight,
69 WinitNamedKey::F1 => platform_core::NamedKey::F1,
70 WinitNamedKey::F2 => platform_core::NamedKey::F2,
71 WinitNamedKey::F3 => platform_core::NamedKey::F3,
72 WinitNamedKey::F4 => platform_core::NamedKey::F4,
73 WinitNamedKey::F5 => platform_core::NamedKey::F5,
74 WinitNamedKey::F6 => platform_core::NamedKey::F6,
75 WinitNamedKey::F7 => platform_core::NamedKey::F7,
76 WinitNamedKey::F8 => platform_core::NamedKey::F8,
77 WinitNamedKey::F9 => platform_core::NamedKey::F9,
78 WinitNamedKey::F10 => platform_core::NamedKey::F10,
79 WinitNamedKey::F11 => platform_core::NamedKey::F11,
80 WinitNamedKey::F12 => platform_core::NamedKey::F12,
81 WinitNamedKey::F13 => platform_core::NamedKey::F13,
82 WinitNamedKey::F14 => platform_core::NamedKey::F14,
83 WinitNamedKey::F15 => platform_core::NamedKey::F15,
84 WinitNamedKey::F16 => platform_core::NamedKey::F16,
85 WinitNamedKey::F17 => platform_core::NamedKey::F17,
86 WinitNamedKey::F18 => platform_core::NamedKey::F18,
87 WinitNamedKey::F19 => platform_core::NamedKey::F19,
88 WinitNamedKey::F20 => platform_core::NamedKey::F20,
89 WinitNamedKey::F21 => platform_core::NamedKey::F21,
90 WinitNamedKey::F22 => platform_core::NamedKey::F22,
91 WinitNamedKey::F23 => platform_core::NamedKey::F23,
92 WinitNamedKey::F24 => platform_core::NamedKey::F24,
93 WinitNamedKey::Space => platform_core::NamedKey::Space,
94 WinitNamedKey::Insert => platform_core::NamedKey::Insert,
95 WinitNamedKey::CapsLock => platform_core::NamedKey::CapsLock,
96 _ => return None,
97 };
98 Some(nk)
99}
100
101pub fn map_mouse_button(button: WinitMouseButton) -> Option<platform_core::PointerButton> {
102 match button {
103 WinitMouseButton::Left => Some(platform_core::PointerButton::Primary),
104 WinitMouseButton::Right => Some(platform_core::PointerButton::Secondary),
105 WinitMouseButton::Middle => Some(platform_core::PointerButton::Auxiliary),
106 _ => None,
107 }
108}
109
110pub fn map_modifiers(mods: &Modifiers) -> platform_core::ModifiersState {
111 platform_core::ModifiersState {
112 is_shift: mods.state().shift_key(),
113 is_ctrl: mods.state().control_key(),
114 is_alt: mods.state().alt_key(),
115 is_meta: mods.state().super_key(),
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use platform_core::{Key, NamedKey};
123 use winit::keyboard::SmolStr;
124
125 fn character(s: &str) -> WinitKey {
126 WinitKey::Character(SmolStr::new(s))
127 }
128
129 #[test]
132 fn the_keypad_is_its_own_set_of_keys() {
133 assert_eq!(
134 map_key(&character("7"), KeyLocation::Numpad),
135 Some(Key::Named(NamedKey::Numpad7))
136 );
137 assert_eq!(
138 map_key(&character("7"), KeyLocation::Standard),
139 Some(Key::Char('7'))
140 );
141 assert_eq!(
142 map_key(&WinitKey::Named(WinitNamedKey::Enter), KeyLocation::Numpad),
143 Some(Key::Named(NamedKey::NumpadEnter))
144 );
145 }
146
147 #[test]
150 fn a_keypad_key_without_num_lock_stays_what_the_os_calls_it() {
151 assert_eq!(
152 map_key(&WinitKey::Named(WinitNamedKey::End), KeyLocation::Numpad),
153 Some(Key::Named(NamedKey::End))
154 );
155 }
156}
157
158pub enum SurfaceIntent {
163 Event(Event),
165 Resized(Event),
167 Redraw,
169 Close(Event),
171 Ignore,
173}
174
175pub fn map_window_event(
178 event: WindowEvent,
179 cursor_position: &mut (f64, f64),
180 scale_factor: &mut f64,
181 modifiers: &mut platform_core::ModifiersState,
182) -> SurfaceIntent {
183 match event {
184 WindowEvent::CloseRequested => SurfaceIntent::Close(Event::WindowCloseRequested),
185 WindowEvent::Resized(size) => SurfaceIntent::Resized(Event::WindowResized {
186 width: (size.width as f64 / *scale_factor).round() as u32,
187 height: (size.height as f64 / *scale_factor).round() as u32,
188 }),
189 WindowEvent::RedrawRequested => SurfaceIntent::Redraw,
190 WindowEvent::CursorMoved { position, .. } => {
191 let lx = position.x / *scale_factor;
192 let ly = position.y / *scale_factor;
193 *cursor_position = (lx, ly);
194 SurfaceIntent::Event(Event::PointerMoved {
195 x: lx,
196 y: ly,
197 source: PointerSource::Mouse,
198 })
199 }
200 WindowEvent::MouseInput { state, button, .. } => {
201 let Some(btn) = crate::map_mouse_button(button) else {
202 return SurfaceIntent::Ignore;
203 };
204 let (x, y) = *cursor_position;
205 SurfaceIntent::Event(match state {
206 ElementState::Pressed => Event::PointerPressed {
207 x,
208 y,
209 button: btn,
210 source: PointerSource::Mouse,
211 },
212 ElementState::Released => Event::PointerReleased {
213 x,
214 y,
215 button: btn,
216 source: PointerSource::Mouse,
217 },
218 })
219 }
220 WindowEvent::Touch(Touch {
221 phase,
222 location,
223 id,
224 ..
225 }) => {
226 let x = location.x / *scale_factor;
227 let y = location.y / *scale_factor;
228 let source = PointerSource::Touch { id };
229 SurfaceIntent::Event(match phase {
230 TouchPhase::Started => Event::PointerPressed {
231 x,
232 y,
233 button: PointerButton::Primary,
234 source,
235 },
236 TouchPhase::Moved => Event::PointerMoved { x, y, source },
237 TouchPhase::Ended | TouchPhase::Cancelled => Event::PointerReleased {
238 x,
239 y,
240 button: PointerButton::Primary,
241 source,
242 },
243 })
244 }
245 WindowEvent::Focused(is_focused) => {
246 SurfaceIntent::Event(Event::FocusChanged { is_focused })
247 }
248 WindowEvent::CursorEntered { .. } => SurfaceIntent::Event(Event::CursorEntered),
249 WindowEvent::CursorLeft { .. } => SurfaceIntent::Event(Event::CursorLeft),
250 WindowEvent::ScaleFactorChanged {
251 scale_factor: new_scale,
252 ..
253 } => {
254 *scale_factor = new_scale;
255 SurfaceIntent::Event(Event::ScaleFactorChanged {
256 scale_factor: new_scale,
257 })
258 }
259 WindowEvent::MouseWheel { delta, .. } => {
260 let scroll_delta = match delta {
261 MouseScrollDelta::LineDelta(x, y) => ScrollDelta::Lines { x, y },
262 MouseScrollDelta::PixelDelta(pos) => ScrollDelta::Pixels {
263 x: (pos.x / *scale_factor) as f32,
264 y: (pos.y / *scale_factor) as f32,
265 },
266 };
267 let (x, y) = *cursor_position;
268 SurfaceIntent::Event(Event::Scrolled {
269 delta: scroll_delta,
270 x,
271 y,
272 })
273 }
274 WindowEvent::ModifiersChanged(mods) => {
275 *modifiers = crate::map_modifiers(&mods);
276 SurfaceIntent::Event(Event::ModifiersChanged {
277 modifiers: *modifiers,
278 })
279 }
280 WindowEvent::KeyboardInput { event, .. } => {
281 let Some(key) = crate::map_key(&event.logical_key, event.location) else {
282 return SurfaceIntent::Ignore;
283 };
284 let mods = *modifiers;
285 SurfaceIntent::Event(match event.state {
286 ElementState::Pressed => Event::KeyPressed {
287 key,
288 modifiers: mods,
289 },
290 ElementState::Released => Event::KeyReleased {
291 key,
292 modifiers: mods,
293 },
294 })
295 }
296 WindowEvent::ThemeChanged(theme) => SurfaceIntent::Event(Event::ColorSchemeChanged {
297 dark: theme == winit::window::Theme::Dark,
298 }),
299 _ => SurfaceIntent::Ignore,
300 }
301}