1#![cfg_attr(not(test), warn(unused_crate_dependencies))]
13#![warn(clippy::print_stdout, clippy::print_stderr)]
15#![cfg_attr(target_pointer_width = "64", warn(clippy::trivially_copy_pass_by_ref))]
17#![cfg_attr(docsrs, feature(doc_auto_cfg))]
19#![no_std]
20
21pub mod keyboard;
22pub mod pointer;
23
24extern crate alloc;
25use alloc::{vec, vec::Vec};
26
27extern crate std;
28use std::time::Instant;
29
30use ui_events::{
31 keyboard::KeyboardEvent,
32 pointer::{PointerEvent, PointerId, PointerInfo, PointerState, PointerType, PointerUpdate},
33 ScrollDelta,
34};
35use winit::{
36 event::{ElementState, Force, MouseScrollDelta, Touch, TouchPhase, WindowEvent},
37 keyboard::ModifiersState,
38};
39
40#[derive(Debug, Default)]
56pub struct WindowEventReducer {
57 modifiers: ModifiersState,
59 primary_state: PointerState,
61 counter: TapCounter,
63 first_instant: Option<Instant>,
65}
66
67#[allow(clippy::cast_possible_truncation)]
68impl WindowEventReducer {
69 pub fn reduce(&mut self, we: &WindowEvent) -> Option<WindowEventTranslation> {
71 const PRIMARY_MOUSE: PointerInfo = PointerInfo {
72 pointer_id: Some(PointerId::PRIMARY),
73 persistent_device_id: None,
75 pointer_type: PointerType::Mouse,
76 };
77
78 let time = Instant::now()
79 .duration_since(*self.first_instant.get_or_insert_with(Instant::now))
80 .as_nanos() as u64;
81
82 self.primary_state.time = time;
83
84 match we {
85 WindowEvent::ModifiersChanged(m) => {
86 self.modifiers = m.state();
87 self.primary_state.modifiers = keyboard::from_winit_modifier_state(self.modifiers);
88 None
89 }
90 WindowEvent::KeyboardInput { event, .. } => Some(WindowEventTranslation::Keyboard(
91 keyboard::from_winit_keyboard_event(event.clone(), self.modifiers),
92 )),
93 WindowEvent::CursorEntered { .. } => Some(WindowEventTranslation::Pointer(
94 PointerEvent::Enter(PRIMARY_MOUSE),
95 )),
96 WindowEvent::CursorLeft { .. } => Some(WindowEventTranslation::Pointer(
97 PointerEvent::Leave(PRIMARY_MOUSE),
98 )),
99 WindowEvent::CursorMoved { position, .. } => {
100 self.primary_state.position = *position;
101
102 Some(WindowEventTranslation::Pointer(self.counter.attach_count(
103 PointerEvent::Move(PointerUpdate {
104 pointer: PRIMARY_MOUSE,
105 current: self.primary_state.clone(),
106 coalesced: vec![],
107 predicted: vec![],
108 }),
109 )))
110 }
111 WindowEvent::MouseInput {
112 state: ElementState::Pressed,
113 button,
114 ..
115 } => {
116 let button = pointer::try_from_winit_button(*button);
117 if let Some(button) = button {
118 self.primary_state.buttons.insert(button);
119 }
120
121 Some(WindowEventTranslation::Pointer(self.counter.attach_count(
122 PointerEvent::Down {
123 pointer: PRIMARY_MOUSE,
124 button,
125 state: self.primary_state.clone(),
126 },
127 )))
128 }
129 WindowEvent::MouseInput {
130 state: ElementState::Released,
131 button,
132 ..
133 } => {
134 let button = pointer::try_from_winit_button(*button);
135 if let Some(button) = button {
136 self.primary_state.buttons.remove(button);
137 }
138
139 Some(WindowEventTranslation::Pointer(self.counter.attach_count(
140 PointerEvent::Up {
141 pointer: PRIMARY_MOUSE,
142 button,
143 state: self.primary_state.clone(),
144 },
145 )))
146 }
147 WindowEvent::MouseWheel { delta, .. } => {
148 Some(WindowEventTranslation::Pointer(PointerEvent::Scroll {
149 pointer: PRIMARY_MOUSE,
150 delta: match *delta {
151 MouseScrollDelta::LineDelta(x, y) => ScrollDelta::LineDelta(x, y),
152 MouseScrollDelta::PixelDelta(p) => ScrollDelta::PixelDelta(p),
153 },
154 state: self.primary_state.clone(),
155 }))
156 }
157 WindowEvent::Touch(Touch {
158 phase,
159 id,
160 location,
161 force,
162 ..
163 }) => {
164 let pointer = PointerInfo {
165 pointer_id: PointerId::new(id.saturating_add(1)),
166 pointer_type: PointerType::Touch,
167 persistent_device_id: None,
168 };
169
170 use TouchPhase::*;
171
172 let state = PointerState {
173 time,
174 position: *location,
175 modifiers: self.primary_state.modifiers,
176 pressure: if matches!(phase, Ended | Cancelled) {
177 0.0
178 } else {
179 match force {
180 Some(Force::Calibrated { force, .. }) => (force * 0.5) as f32,
181 Some(Force::Normalized(q)) => *q as f32,
182 _ => 0.5,
183 }
184 },
185 ..Default::default()
186 };
187
188 Some(WindowEventTranslation::Pointer(self.counter.attach_count(
189 match phase {
190 Started => PointerEvent::Down {
191 pointer,
192 button: None,
193 state,
194 },
195 Moved => PointerEvent::Move(PointerUpdate {
196 pointer,
197 current: state,
198 coalesced: vec![],
199 predicted: vec![],
200 }),
201 Cancelled => PointerEvent::Cancel(pointer),
202 Ended => PointerEvent::Up {
203 pointer,
204 button: None,
205 state,
206 },
207 },
208 )))
209 }
210 _ => None,
211 }
212 }
213}
214
215#[derive(Debug)]
217pub enum WindowEventTranslation {
218 Keyboard(KeyboardEvent),
220 Pointer(PointerEvent),
222}
223
224#[derive(Clone, Debug)]
225struct TapState {
226 pointer_id: Option<PointerId>,
228 down_time: u64,
230 up_time: u64,
234 count: u8,
236 x: f64,
238 y: f64,
240}
241
242#[derive(Debug, Default)]
243struct TapCounter {
244 taps: Vec<TapState>,
245}
246
247impl TapCounter {
248 fn attach_count(&mut self, e: PointerEvent) -> PointerEvent {
250 match e {
251 PointerEvent::Down {
252 button,
253 pointer,
254 state,
255 } => {
256 let e = if let Some(i) =
257 self.taps.iter().position(|TapState { x, y, up_time, .. }| {
258 let dx = (x - state.position.x).abs();
259 let dy = (y - state.position.y).abs();
260 (dx * dx + dy * dy).sqrt() < 4.0 && (up_time + 500_000_000) > state.time
261 }) {
262 let count = self.taps[i].count + 1;
263 self.taps[i].count = count;
264 self.taps[i].pointer_id = pointer.pointer_id;
265 self.taps[i].down_time = state.time;
266 self.taps[i].up_time = state.time;
267 self.taps[i].x = state.position.x;
268 self.taps[i].y = state.position.y;
269
270 PointerEvent::Down {
271 button,
272 pointer,
273 state: PointerState { count, ..state },
274 }
275 } else {
276 let s = TapState {
277 pointer_id: pointer.pointer_id,
278 down_time: state.time,
279 up_time: state.time,
280 count: 1,
281 x: state.position.x,
282 y: state.position.y,
283 };
284 self.taps.push(s);
285 PointerEvent::Down {
286 button,
287 pointer,
288 state: PointerState { count: 1, ..state },
289 }
290 };
291 self.clear_expired(state.time);
292 e
293 }
294 PointerEvent::Up {
295 button,
296 pointer,
297 ref state,
298 } => {
299 if let Some(i) = self
300 .taps
301 .iter()
302 .position(|TapState { pointer_id, .. }| *pointer_id == pointer.pointer_id)
303 {
304 self.taps[i].up_time = state.time;
305 PointerEvent::Up {
306 button,
307 pointer,
308 state: PointerState {
309 count: self.taps[i].count,
310 ..state.clone()
311 },
312 }
313 } else {
314 e.clone()
315 }
316 }
317 PointerEvent::Move(PointerUpdate {
318 pointer,
319 ref current,
320 ref coalesced,
321 ref predicted,
322 }) => {
323 if let Some(TapState { count, .. }) = self
324 .taps
325 .iter()
326 .find(
327 |TapState {
328 pointer_id,
329 down_time,
330 up_time,
331 ..
332 }| {
333 *pointer_id == pointer.pointer_id && down_time == up_time
334 },
335 )
336 .cloned()
337 {
338 PointerEvent::Move(PointerUpdate {
339 pointer,
340 current: PointerState {
341 count,
342 ..current.clone()
343 },
344 coalesced: coalesced
345 .iter()
346 .cloned()
347 .map(|u| PointerState { count, ..u })
348 .collect(),
349 predicted: predicted
350 .iter()
351 .cloned()
352 .map(|u| PointerState { count, ..u })
353 .collect(),
354 })
355 } else {
356 e
357 }
358 }
359 PointerEvent::Cancel(p) | PointerEvent::Leave(p) => {
360 self.taps
361 .retain(|TapState { pointer_id, .. }| *pointer_id != p.pointer_id);
362 e.clone()
363 }
364 PointerEvent::Enter(..) | PointerEvent::Scroll { .. } => e.clone(),
365 }
366 }
367
368 fn clear_expired(&mut self, t: u64) {
373 self.taps.retain(
374 |TapState {
375 down_time, up_time, ..
376 }| { down_time == up_time || (up_time + 500_000_000) > t },
377 );
378 }
379}
380
381#[cfg(test)]
382mod tests {
383 #[test]
386 fn dummy_test_until_we_have_a_real_test() {}
387}