1extern crate alloc;
5use alloc::vec::Vec;
6
7use ui_events::pointer::{
8 PointerButton, PointerButtonEvent, PointerButtons, PointerEvent, PointerState, PointerUpdate,
9};
10
11use dpi::{LogicalPosition, PhysicalPosition};
12
13#[derive(Clone, Debug, Default)]
15pub struct PrimaryPointerState {
16 just_pressed: PointerButtons,
18 just_released: PointerButtons,
20 current: PointerState,
22 coalesced: Vec<PointerState>,
24 predicted: Vec<PointerState>,
26}
27
28impl PrimaryPointerState {
29 pub fn is_just_pressed(&self, button: PointerButton) -> bool {
34 self.just_pressed.contains(button)
35 }
36
37 pub fn is_just_released(&self, button: PointerButton) -> bool {
42 self.just_released.contains(button)
43 }
44
45 pub fn is_auxiliary_just_pressed(&self) -> bool {
48 self.is_just_pressed(PointerButton::Auxiliary)
49 }
50
51 pub fn is_auxiliary_just_released(&self) -> bool {
53 self.is_just_released(PointerButton::Auxiliary)
54 }
55
56 pub fn is_primary_just_pressed(&self) -> bool {
59 self.is_just_pressed(PointerButton::Primary)
60 }
61
62 pub fn is_primary_just_released(&self) -> bool {
64 self.is_just_released(PointerButton::Primary)
65 }
66
67 pub fn is_secondary_just_pressed(&self) -> bool {
70 self.is_just_pressed(PointerButton::Secondary)
71 }
72
73 pub fn is_secondary_just_released(&self) -> bool {
75 self.is_just_released(PointerButton::Secondary)
76 }
77
78 pub fn is_any_down(&self) -> bool {
80 !self.current.buttons.is_empty()
81 }
82
83 pub fn is_down(&self, button: PointerButton) -> bool {
85 self.current.buttons.contains(button)
86 }
87
88 pub fn clear_frame(&mut self) {
90 self.just_pressed.clear();
91 self.just_released.clear();
92 self.coalesced.clear();
93 self.predicted.clear();
95 }
96
97 pub fn current_position(&self) -> PhysicalPosition<f64> {
101 self.current.position
102 }
103
104 pub fn current_logical_position(&self) -> LogicalPosition<f64> {
108 self.current.logical_position()
109 }
110
111 pub fn motion(&self) -> PhysicalPosition<f64> {
113 let current = self.current.position;
114 let first = self
115 .coalesced
116 .first()
117 .map(|s| s.position)
118 .unwrap_or(current);
119 PhysicalPosition {
120 x: current.x - first.x,
121 y: current.y - first.y,
122 }
123 }
124
125 pub fn logical_motion(&self) -> LogicalPosition<f64> {
127 let current = self.current.logical_position();
128 let first = self
129 .coalesced
130 .first()
131 .map(|s| s.logical_position())
132 .unwrap_or(current);
133 LogicalPosition {
134 x: current.x - first.x,
135 y: current.y - first.y,
136 }
137 }
138
139 fn push_state(&mut self, state: PointerState) {
141 if state.time != 0 {
142 self.coalesced.push(state);
144 }
145 }
146
147 pub fn process_pointer_event(&mut self, event: PointerEvent) {
152 if !event.is_primary_pointer() {
153 return;
154 }
155
156 match event {
157 PointerEvent::Down(PointerButtonEvent {
158 button: Some(b),
159 state,
160 ..
161 }) => {
162 self.just_pressed.insert(b);
163 let mut state = state.clone();
164 core::mem::swap(&mut self.current, &mut state);
165 self.push_state(state);
166 self.predicted.clear();
168 }
169 PointerEvent::Up(PointerButtonEvent {
170 button: Some(b),
171 state,
172 ..
173 }) => {
174 self.just_released.insert(b);
175 let mut state = state.clone();
176 core::mem::swap(&mut self.current, &mut state);
177 self.push_state(state);
178 self.predicted.clear();
180 }
181 PointerEvent::Move(PointerUpdate {
182 current,
183 coalesced,
184 predicted,
185 ..
186 }) => {
187 self.coalesced.push(self.current.clone());
188 self.current = current.clone();
189 self.coalesced.extend(coalesced);
190 self.predicted.clear();
191 self.predicted.extend(predicted);
192 }
193 PointerEvent::Cancel(_) | PointerEvent::Leave(_) => {
194 self.predicted.clear();
196 self.coalesced.clear();
197 self.current.buttons.clear();
198 }
199 _ => {}
200 }
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use alloc::vec;
208 use ui_events::pointer::{
209 PointerButtonEvent, PointerEvent, PointerId, PointerInfo, PointerState, PointerType,
210 };
211
212 fn phony_time() -> u64 {
214 use core::sync::atomic::AtomicU64;
215 use core::sync::atomic::Ordering;
216 static TIME: AtomicU64 = AtomicU64::new(0);
217 TIME.fetch_add(1, Ordering::SeqCst)
218 }
219
220 fn make_down_event(button: PointerButton) -> PointerEvent {
221 PointerEvent::Down(PointerButtonEvent {
222 button: Some(button),
223 pointer: PointerInfo {
224 pointer_id: Some(PointerId::PRIMARY),
225 persistent_device_id: None,
226 pointer_type: PointerType::Mouse,
227 },
228 state: PointerState {
229 time: phony_time(),
230 buttons: button.into(),
231 ..Default::default()
232 },
233 })
234 }
235
236 fn make_up_event(button: PointerButton) -> PointerEvent {
237 PointerEvent::Up(PointerButtonEvent {
238 button: Some(button),
239 pointer: PointerInfo {
240 pointer_id: Some(PointerId::PRIMARY),
241 persistent_device_id: None,
242 pointer_type: PointerType::Mouse,
243 },
244 state: PointerState {
245 time: phony_time(),
246 ..Default::default()
247 },
248 })
249 }
250
251 #[test]
252 fn press_and_hold_primary() {
253 let mut state = PrimaryPointerState::default();
254 state.process_pointer_event(make_down_event(PointerButton::Primary));
255
256 assert!(state.is_primary_just_pressed());
257 assert!(state.is_down(PointerButton::Primary));
258 assert!(!state.is_primary_just_released());
259
260 state.clear_frame();
261
262 assert!(!state.is_primary_just_pressed());
263 assert!(state.is_down(PointerButton::Primary));
264 }
265
266 #[test]
267 fn press_and_release_primary_same_frame() {
268 let mut state = PrimaryPointerState::default();
269 state.process_pointer_event(make_down_event(PointerButton::Primary));
270 state.process_pointer_event(make_up_event(PointerButton::Primary));
271
272 assert!(state.is_primary_just_pressed());
273 assert!(state.is_primary_just_released());
274 assert!(!state.is_down(PointerButton::Primary));
275 }
276
277 #[test]
278 fn release_after_hold() {
279 let mut state = PrimaryPointerState::default();
280 state.process_pointer_event(make_down_event(PointerButton::Primary));
281 state.clear_frame();
282 state.process_pointer_event(make_up_event(PointerButton::Primary));
283
284 assert!(!state.is_primary_just_pressed());
285 assert!(state.is_primary_just_released());
286 assert!(!state.is_down(PointerButton::Primary));
287 }
288
289 fn make_move_event(
290 position: PhysicalPosition<f64>,
291 coalesced: Vec<PhysicalPosition<f64>>,
292 predicted: Vec<PhysicalPosition<f64>>,
293 ) -> PointerEvent {
294 let coalesced = coalesced
295 .iter()
296 .copied()
297 .map(|position| PointerState {
298 time: phony_time(),
299 position,
300 ..Default::default()
301 })
302 .collect();
303
304 let current = PointerState {
305 time: phony_time(),
306 position,
307 ..Default::default()
308 };
309
310 let predicted = predicted
311 .iter()
312 .copied()
313 .map(|position| PointerState {
314 time: phony_time(),
315 position,
316 ..Default::default()
317 })
318 .collect();
319
320 PointerEvent::Move(PointerUpdate {
321 pointer: PointerInfo {
322 pointer_id: Some(PointerId::PRIMARY),
323 persistent_device_id: None,
324 pointer_type: PointerType::Mouse,
325 },
326 current,
327 coalesced,
328 predicted,
329 })
330 }
331
332 fn make_cancel_event() -> PointerEvent {
333 PointerEvent::Cancel(PointerInfo {
334 pointer_id: Some(PointerId::PRIMARY),
335 persistent_device_id: None,
336 pointer_type: PointerType::Mouse,
337 })
338 }
339
340 fn make_leave_event() -> PointerEvent {
341 PointerEvent::Leave(PointerInfo {
342 pointer_id: Some(PointerId::PRIMARY),
343 persistent_device_id: None,
344 pointer_type: PointerType::Mouse,
345 })
346 }
347
348 #[test]
349 fn down_updates_current_buttons() {
350 let mut state = PrimaryPointerState::default();
351 state.process_pointer_event(make_down_event(PointerButton::Primary));
352
353 assert!(state.current.buttons.contains(PointerButton::Primary));
354 assert!(state.is_down(PointerButton::Primary));
355 assert!(state.is_any_down());
356 }
357
358 #[test]
359 fn up_updates_current_buttons() {
360 let mut state = PrimaryPointerState::default();
361 state.process_pointer_event(make_down_event(PointerButton::Primary));
362 state.process_pointer_event(make_up_event(PointerButton::Primary));
363
364 assert!(!state.current.buttons.contains(PointerButton::Primary));
365 assert!(!state.is_down(PointerButton::Primary));
366 assert!(!state.is_any_down());
367 }
368
369 #[test]
370 fn move_appends_to_coalesced_if_down() {
371 let mut state = PrimaryPointerState::default();
372 state.process_pointer_event(make_down_event(PointerButton::Primary));
373
374 state.process_pointer_event(make_move_event(
375 PhysicalPosition { x: 10.0, y: 10.0 },
376 vec![PhysicalPosition { x: 5.0, y: 5.0 }],
377 vec![],
378 ));
379
380 assert_eq!(state.coalesced.len(), 2);
381 assert_eq!(
382 state.coalesced[1].position,
383 PhysicalPosition { x: 5.0, y: 5.0 }
384 );
385 assert_eq!(
386 state.current.position,
387 PhysicalPosition { x: 10.0, y: 10.0 }
388 );
389 }
390
391 #[test]
392 fn move_appends_to_coalesced_even_if_not_down() {
393 let mut state = PrimaryPointerState::default();
394
395 state.process_pointer_event(make_move_event(
396 PhysicalPosition { x: 10.0, y: 10.0 },
397 vec![PhysicalPosition { x: 5.0, y: 5.0 }],
398 vec![],
399 ));
400
401 assert_eq!(state.coalesced.len(), 2);
402 assert_eq!(state.coalesced[0].position, PhysicalPosition::default());
403 assert_eq!(
404 state.coalesced[1].position,
405 PhysicalPosition { x: 5.0, y: 5.0 }
406 );
407 assert_eq!(
408 state.current.position,
409 PhysicalPosition { x: 10.0, y: 10.0 }
410 );
411 }
412
413 #[test]
414 fn move_sets_predicted() {
415 let mut state = PrimaryPointerState::default();
416
417 state.process_pointer_event(make_move_event(
418 PhysicalPosition { x: 10.0, y: 10.0 },
419 vec![],
420 vec![PhysicalPosition { x: 15.0, y: 15.0 }],
421 ));
422
423 assert_eq!(state.predicted.len(), 1);
424 assert_eq!(
425 state.predicted[0].position,
426 PhysicalPosition { x: 15.0, y: 15.0 }
427 );
428 }
429
430 #[test]
431 fn down_clears_predicted() {
432 let mut state = PrimaryPointerState::default();
433
434 state.process_pointer_event(make_move_event(
435 PhysicalPosition { x: 10.0, y: 10.0 },
436 vec![],
437 vec![PhysicalPosition { x: 15.0, y: 15.0 }],
438 ));
439
440 assert!(!state.predicted.is_empty());
441
442 state.process_pointer_event(make_down_event(PointerButton::Primary));
443
444 assert!(state.predicted.is_empty());
445 }
446
447 #[test]
448 fn up_clears_predicted() {
449 let mut state = PrimaryPointerState::default();
450 state.process_pointer_event(make_down_event(PointerButton::Primary));
451
452 state.process_pointer_event(make_move_event(
453 PhysicalPosition { x: 10.0, y: 10.0 },
454 vec![],
455 vec![PhysicalPosition { x: 15.0, y: 15.0 }],
456 ));
457
458 assert!(!state.predicted.is_empty());
459
460 state.process_pointer_event(make_up_event(PointerButton::Primary));
461
462 assert!(state.predicted.is_empty());
463 }
464
465 #[test]
466 fn cancel_clears_states() {
467 let mut state = PrimaryPointerState::default();
468 state.process_pointer_event(make_down_event(PointerButton::Primary));
469
470 assert!(state.predicted.is_empty());
471 assert!(!state.current.buttons.is_empty());
472
473 state.process_pointer_event(make_cancel_event());
474
475 assert!(state.coalesced.is_empty());
476 assert!(state.predicted.is_empty());
477 assert!(state.current.buttons.is_empty());
478 }
479
480 #[test]
481 fn leave_clears_states() {
482 let mut state = PrimaryPointerState::default();
483 state.process_pointer_event(make_down_event(PointerButton::Primary));
484
485 assert!(state.predicted.is_empty());
486 assert!(!state.current.buttons.is_empty());
487
488 state.process_pointer_event(make_leave_event());
489
490 assert!(state.coalesced.is_empty());
491 assert!(state.predicted.is_empty());
492 assert!(state.current.buttons.is_empty());
493 }
494
495 #[test]
496 fn clear_frame_clears_coalesced_and_predicted() {
497 let mut state = PrimaryPointerState::default();
498
499 state.process_pointer_event(make_move_event(
500 PhysicalPosition { x: 10.0, y: 10.0 },
501 vec![PhysicalPosition { x: 5.0, y: 5.0 }],
502 vec![PhysicalPosition { x: 15.0, y: 15.0 }],
503 ));
504
505 assert!(!state.coalesced.is_empty());
506 assert!(!state.predicted.is_empty());
507
508 state.clear_frame();
509
510 assert!(state.coalesced.is_empty());
511 assert!(state.predicted.is_empty());
512 }
513
514 #[test]
515 fn current_position_and_logical() {
516 let mut state = PrimaryPointerState::default();
517 let position = PhysicalPosition { x: 100.0, y: 200.0 };
518 let scale_factor = 2.0;
519
520 let current = PointerState {
521 time: phony_time(),
522 position,
523 scale_factor,
524 ..Default::default()
525 };
526
527 state.process_pointer_event(PointerEvent::Move(PointerUpdate {
528 pointer: PointerInfo {
529 pointer_id: Some(PointerId::PRIMARY),
530 persistent_device_id: None,
531 pointer_type: PointerType::Mouse,
532 },
533 current,
534 coalesced: vec![],
535 predicted: vec![],
536 }));
537
538 assert_eq!(state.current_position(), position);
539 assert_eq!(
540 state.current_logical_position(),
541 position.to_logical(scale_factor)
542 );
543 }
544
545 #[test]
546 fn motion_with_coalesced() {
547 let mut state = PrimaryPointerState::default();
548
549 state.process_pointer_event(make_move_event(
550 PhysicalPosition { x: 10.0, y: 20.0 },
551 vec![],
552 vec![],
553 ));
554
555 state.process_pointer_event(make_move_event(
556 PhysicalPosition { x: 30.0, y: 40.0 },
557 vec![PhysicalPosition { x: 15.0, y: 25.0 }],
558 vec![],
559 ));
560
561 assert_eq!(state.motion(), PhysicalPosition { x: 30.0, y: 40.0 });
562 assert_eq!(state.logical_motion(), LogicalPosition { x: 30.0, y: 40.0 });
563 }
564
565 #[test]
566 fn motion_without_coalesced() {
567 let mut state = PrimaryPointerState::default();
568
569 state.process_pointer_event(make_move_event(
570 PhysicalPosition { x: 30.0, y: 40.0 },
571 vec![],
572 vec![],
573 ));
574
575 assert_eq!(state.motion(), PhysicalPosition { x: 30.0, y: 40.0 });
576 assert_eq!(state.logical_motion(), LogicalPosition { x: 30.0, y: 40.0 });
577 }
578}