1use super::FrameInput;
2use crate::control::*;
3use crate::core::*;
4#[cfg(target_arch = "wasm32")]
5use instant::Instant;
6#[cfg(not(target_arch = "wasm32"))]
7use std::time::Instant;
8use winit::dpi::PhysicalSize;
9use winit::event::TouchPhase;
10use winit::event::WindowEvent;
11
12pub struct FrameInputGenerator {
17 last_time: Instant,
18 first_frame: bool,
19 events: Vec<Event>,
20 accumulated_time: f64,
21 viewport: Viewport,
22 window_width: u32,
23 window_height: u32,
24 device_pixel_ratio: f64,
25 cursor_pos: Option<LogicalPoint>,
26 finger_id: Option<u64>,
27 secondary_cursor_pos: Option<LogicalPoint>,
28 secondary_finger_id: Option<u64>,
29 modifiers: Modifiers,
30 mouse_pressed: Option<MouseButton>,
31}
32
33impl FrameInputGenerator {
34 fn new(size: PhysicalSize<u32>, device_pixel_ratio: f64) -> Self {
38 let (window_width, window_height): (u32, u32) =
39 size.to_logical::<f32>(device_pixel_ratio).into();
40 Self {
41 events: Vec::new(),
42 accumulated_time: 0.0,
43 viewport: Viewport::new_at_origo(size.width, size.height),
44 window_width,
45 window_height,
46 device_pixel_ratio,
47 first_frame: true,
48 last_time: Instant::now(),
49 cursor_pos: None,
50 finger_id: None,
51 secondary_cursor_pos: None,
52 secondary_finger_id: None,
53 modifiers: Modifiers::default(),
54 mouse_pressed: None,
55 }
56 }
57
58 pub fn from_winit_window(window: &winit::window::Window) -> Self {
62 Self::new(window.inner_size(), window.scale_factor())
63 }
64
65 pub fn generate(&mut self, context: &Context) -> FrameInput {
69 let now = Instant::now();
70 let duration = now.duration_since(self.last_time);
71 let elapsed_time =
72 duration.as_secs() as f64 * 1000.0 + duration.subsec_nanos() as f64 * 1e-6;
73 self.accumulated_time += elapsed_time;
74 self.last_time = now;
75
76 let frame_input = FrameInput {
77 events: self.events.drain(..).collect(),
78 elapsed_time,
79 accumulated_time: self.accumulated_time,
80 viewport: self.viewport,
81 window_width: self.window_width,
82 window_height: self.window_height,
83 device_pixel_ratio: self.device_pixel_ratio as f32,
84 first_frame: self.first_frame,
85 context: context.clone(),
86 };
87 self.first_frame = false;
88
89 #[cfg(not(target_arch = "wasm32"))]
90 if let Some(exit_time) = option_env!("THREE_D_EXIT").map(|v| v.parse::<f64>().unwrap()) {
91 if exit_time < frame_input.accumulated_time {
92 #[cfg(feature = "image")]
93 if let Some(path) = option_env!("THREE_D_SCREENSHOT") {
94 let pixels = frame_input.screen().read_color::<[u8; 4]>();
95 let img = image::DynamicImage::ImageRgba8(
96 image::ImageBuffer::from_raw(
97 frame_input.viewport.width,
98 frame_input.viewport.height,
99 pixels.into_iter().flatten().collect::<Vec<_>>(),
100 )
101 .unwrap(),
102 );
103 img.resize(
104 frame_input.window_width,
105 frame_input.window_height,
106 image::imageops::FilterType::Triangle,
107 )
108 .save(path)
109 .unwrap();
110 }
111 std::process::exit(0);
112 }
113 }
114 frame_input
115 }
116
117 pub fn handle_winit_window_event(&mut self, event: &WindowEvent) {
121 match event {
122 WindowEvent::Resized(physical_size) => {
123 self.viewport = Viewport::new_at_origo(physical_size.width, physical_size.height);
124 let logical_size = physical_size.to_logical(self.device_pixel_ratio);
125 self.window_width = logical_size.width;
126 self.window_height = logical_size.height;
127 }
128 WindowEvent::ScaleFactorChanged {
129 scale_factor,
130 new_inner_size,
131 } => {
132 self.device_pixel_ratio = *scale_factor;
133 self.viewport = Viewport::new_at_origo(new_inner_size.width, new_inner_size.height);
134 let logical_size = new_inner_size.to_logical(self.device_pixel_ratio);
135 self.window_width = logical_size.width;
136 self.window_height = logical_size.height;
137 }
138 WindowEvent::Occluded(false) => {
139 self.first_frame = true;
140 }
141 WindowEvent::KeyboardInput { input, .. } => {
142 if let Some(keycode) = input.virtual_keycode {
143 use winit::event::VirtualKeyCode;
144 let state = input.state == winit::event::ElementState::Pressed;
145 if let Some(kind) = translate_virtual_key_code(keycode) {
146 self.events.push(if state {
147 crate::Event::KeyPress {
148 kind,
149 modifiers: self.modifiers,
150 handled: false,
151 }
152 } else {
153 crate::Event::KeyRelease {
154 kind,
155 modifiers: self.modifiers,
156 handled: false,
157 }
158 });
159 } else if keycode == VirtualKeyCode::LControl
160 || keycode == VirtualKeyCode::RControl
161 {
162 self.modifiers.ctrl = state;
163 if !cfg!(target_os = "macos") {
164 self.modifiers.command = state;
165 }
166 self.events.push(crate::Event::ModifiersChange {
167 modifiers: self.modifiers,
168 });
169 } else if keycode == VirtualKeyCode::LAlt || keycode == VirtualKeyCode::RAlt {
170 self.modifiers.alt = state;
171 self.events.push(crate::Event::ModifiersChange {
172 modifiers: self.modifiers,
173 });
174 } else if keycode == VirtualKeyCode::LShift || keycode == VirtualKeyCode::RShift
175 {
176 self.modifiers.shift = state;
177 self.events.push(crate::Event::ModifiersChange {
178 modifiers: self.modifiers,
179 });
180 } else if (keycode == VirtualKeyCode::LWin || keycode == VirtualKeyCode::RWin)
181 && cfg!(target_os = "macos")
182 {
183 self.modifiers.command = state;
184 self.events.push(crate::Event::ModifiersChange {
185 modifiers: self.modifiers,
186 });
187 }
188 }
189 }
190 WindowEvent::MouseWheel { delta, .. } => {
191 if let Some(position) = self.cursor_pos {
192 match delta {
193 winit::event::MouseScrollDelta::LineDelta(x, y) => {
194 let line_height = 24.0; self.events.push(crate::Event::MouseWheel {
196 delta: (*x * line_height, *y * line_height),
197 position: position.into(),
198 modifiers: self.modifiers,
199 handled: false,
200 });
201 }
202 winit::event::MouseScrollDelta::PixelDelta(delta) => {
203 let d = delta.to_logical(self.device_pixel_ratio);
204 self.events.push(crate::Event::MouseWheel {
205 delta: (d.x, d.y),
206 position: position.into(),
207 modifiers: self.modifiers,
208 handled: false,
209 });
210 }
211 }
212 }
213 }
214 WindowEvent::TouchpadMagnify { delta, .. } => {
215 if let Some(position) = self.cursor_pos {
217 let d = *delta as f32;
218 self.events.push(crate::Event::PinchGesture {
219 delta: d,
220 position: position.into(),
221 modifiers: self.modifiers,
222 handled: false,
223 });
224 }
225 }
226 WindowEvent::TouchpadRotate { delta, .. } => {
227 if let Some(position) = self.cursor_pos {
229 let d = radians(*delta);
230 self.events.push(crate::Event::RotationGesture {
231 delta: d,
232 position: position.into(),
233 modifiers: self.modifiers,
234 handled: false,
235 });
236 }
237 }
238 WindowEvent::MouseInput { state, button, .. } => {
239 if let Some(position) = self.cursor_pos {
240 let button = match button {
241 winit::event::MouseButton::Left => Some(crate::MouseButton::Left),
242 winit::event::MouseButton::Middle => Some(crate::MouseButton::Middle),
243 winit::event::MouseButton::Right => Some(crate::MouseButton::Right),
244 _ => None,
245 };
246 if let Some(b) = button {
247 self.events
248 .push(if *state == winit::event::ElementState::Pressed {
249 self.mouse_pressed = Some(b);
250 crate::Event::MousePress {
251 button: b,
252 position: position.into(),
253 modifiers: self.modifiers,
254 handled: false,
255 }
256 } else {
257 self.mouse_pressed = None;
258 crate::Event::MouseRelease {
259 button: b,
260 position: position.into(),
261 modifiers: self.modifiers,
262 handled: false,
263 }
264 });
265 }
266 }
267 }
268 WindowEvent::CursorMoved { position, .. } => {
269 let p = position.to_logical(self.device_pixel_ratio);
270 let delta = if let Some(last_pos) = self.cursor_pos {
271 (p.x - last_pos.x, p.y - last_pos.y)
272 } else {
273 (0.0, 0.0)
274 };
275 let position = LogicalPoint {
276 x: p.x,
277 y: p.y,
278 device_pixel_ratio: self.device_pixel_ratio as f32,
279 height: self.viewport.height as f32,
280 };
281 self.events.push(crate::Event::MouseMotion {
282 button: self.mouse_pressed,
283 delta,
284 position: position.into(),
285 modifiers: self.modifiers,
286 handled: false,
287 });
288 self.cursor_pos = Some(position);
289 }
290 WindowEvent::ReceivedCharacter(ch) => {
291 if is_printable_char(*ch) && !self.modifiers.ctrl && !self.modifiers.command {
292 self.events.push(crate::Event::Text(ch.to_string()));
293 }
294 }
295 WindowEvent::CursorEntered { .. } => {
296 self.events.push(crate::Event::MouseEnter);
297 }
298 WindowEvent::CursorLeft { .. } => {
299 self.mouse_pressed = None;
300 self.events.push(crate::Event::MouseLeave);
301 }
302 WindowEvent::Touch(touch) => {
303 let position = touch.location.to_logical::<f32>(self.device_pixel_ratio);
304 let position = LogicalPoint {
305 x: position.x,
306 y: position.y,
307 device_pixel_ratio: self.device_pixel_ratio as f32,
308 height: self.viewport.height as f32,
309 };
310 match touch.phase {
311 TouchPhase::Started => {
312 if self.finger_id.is_none() {
313 self.events.push(crate::Event::MousePress {
314 button: MouseButton::Left,
315 position: position.into(),
316 modifiers: self.modifiers,
317 handled: false,
318 });
319 self.cursor_pos = Some(position);
320 self.finger_id = Some(touch.id);
321 } else if self.secondary_finger_id.is_none() {
322 self.secondary_cursor_pos = Some(position);
323 self.secondary_finger_id = Some(touch.id);
324 }
325 }
326 TouchPhase::Ended | TouchPhase::Cancelled => {
327 if self.finger_id.map(|id| id == touch.id).unwrap_or(false) {
328 self.events.push(crate::Event::MouseRelease {
329 button: MouseButton::Left,
330 position: position.into(),
331 modifiers: self.modifiers,
332 handled: false,
333 });
334 self.cursor_pos = None;
335 self.finger_id = None;
336 } else if self
337 .secondary_finger_id
338 .map(|id| id == touch.id)
339 .unwrap_or(false)
340 {
341 self.secondary_cursor_pos = None;
342 self.secondary_finger_id = None;
343 }
344 }
345 TouchPhase::Moved => {
346 if self.finger_id.map(|id| id == touch.id).unwrap_or(false) {
347 let last_pos = self.cursor_pos.unwrap();
348 if let Some(p) = self.secondary_cursor_pos {
349 self.events.push(crate::Event::MouseWheel {
350 position: position.into(),
351 modifiers: self.modifiers,
352 handled: false,
353 delta: (
354 (position.x - p.x).abs() - (last_pos.x - p.x).abs(),
355 (position.y - p.y).abs() - (last_pos.y - p.y).abs(),
356 ),
357 });
358 } else {
359 self.events.push(crate::Event::MouseMotion {
360 button: Some(MouseButton::Left),
361 position: position.into(),
362 modifiers: self.modifiers,
363 handled: false,
364 delta: (position.x - last_pos.x, position.y - last_pos.y),
365 });
366 }
367 self.cursor_pos = Some(position);
368 } else if self
369 .secondary_finger_id
370 .map(|id| id == touch.id)
371 .unwrap_or(false)
372 {
373 let last_pos = self.secondary_cursor_pos.unwrap();
374 if let Some(p) = self.cursor_pos {
375 self.events.push(crate::Event::MouseWheel {
376 position: p.into(),
377 modifiers: self.modifiers,
378 handled: false,
379 delta: (
380 (position.x - p.x).abs() - (last_pos.x - p.x).abs(),
381 (position.y - p.y).abs() - (last_pos.y - p.y).abs(),
382 ),
383 });
384 }
385 self.secondary_cursor_pos = Some(position);
386 }
387 }
388 }
389 }
390 _ => (),
391 }
392 }
393}
394
395fn is_printable_char(chr: char) -> bool {
396 let is_in_private_use_area = ('\u{e000}'..='\u{f8ff}').contains(&chr)
397 || ('\u{f0000}'..='\u{ffffd}').contains(&chr)
398 || ('\u{100000}'..='\u{10fffd}').contains(&chr);
399
400 !is_in_private_use_area && !chr.is_ascii_control()
401}
402
403fn translate_virtual_key_code(key: winit::event::VirtualKeyCode) -> Option<crate::Key> {
404 use winit::event::VirtualKeyCode::*;
405
406 Some(match key {
407 Down => Key::ArrowDown,
408 Left => Key::ArrowLeft,
409 Right => Key::ArrowRight,
410 Up => Key::ArrowUp,
411
412 Escape => Key::Escape,
413 Tab => Key::Tab,
414 Back => Key::Backspace,
415 Return => Key::Enter,
416 Space => Key::Space,
417
418 Insert => Key::Insert,
419 Delete => Key::Delete,
420 Home => Key::Home,
421 End => Key::End,
422 PageUp => Key::PageUp,
423 PageDown => Key::PageDown,
424
425 Key0 | Numpad0 => Key::Num0,
426 Key1 | Numpad1 => Key::Num1,
427 Key2 | Numpad2 => Key::Num2,
428 Key3 | Numpad3 => Key::Num3,
429 Key4 | Numpad4 => Key::Num4,
430 Key5 | Numpad5 => Key::Num5,
431 Key6 | Numpad6 => Key::Num6,
432 Key7 | Numpad7 => Key::Num7,
433 Key8 | Numpad8 => Key::Num8,
434 Key9 | Numpad9 => Key::Num9,
435
436 A => Key::A,
437 B => Key::B,
438 C => Key::C,
439 D => Key::D,
440 E => Key::E,
441 F => Key::F,
442 G => Key::G,
443 H => Key::H,
444 I => Key::I,
445 J => Key::J,
446 K => Key::K,
447 L => Key::L,
448 M => Key::M,
449 N => Key::N,
450 O => Key::O,
451 P => Key::P,
452 Q => Key::Q,
453 R => Key::R,
454 S => Key::S,
455 T => Key::T,
456 U => Key::U,
457 V => Key::V,
458 W => Key::W,
459 X => Key::X,
460 Y => Key::Y,
461 Z => Key::Z,
462
463 _ => {
464 return None;
465 }
466 })
467}
468
469#[derive(Debug, Copy, Clone, PartialEq)]
474struct LogicalPoint {
475 x: f32,
477 y: f32,
479 device_pixel_ratio: f32,
480 height: f32,
481}
482
483impl From<LogicalPoint> for PhysicalPoint {
484 fn from(value: LogicalPoint) -> Self {
485 Self {
486 x: value.x * value.device_pixel_ratio,
487 y: value.height - value.y * value.device_pixel_ratio,
488 }
489 }
490}