1use denise::{ElementState, InputEvent, KeyCode, Modifiers, Point, PointerButton, Size, TouchId};
22
23use crate::layout::{self, Composer, Layout};
24
25use crate::codes::{abs, btn, ev, key_value, rel, syn};
26use crate::keymap::key_code;
27
28pub const MAX_SLOTS: usize = 10;
30
31const SCROLL_STEP: f32 = 40.0;
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36pub struct RawEvent {
37 pub kind: u16,
39 pub code: u16,
41 pub value: i32,
43}
44
45impl RawEvent {
46 pub const fn new(kind: u16, code: u16, value: i32) -> Self {
48 Self { kind, code, value }
49 }
50
51 pub const SYN: Self = Self::new(ev::SYN, syn::REPORT, 0);
53}
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq)]
57pub struct AbsAxis {
58 pub min: i32,
60 pub max: i32,
62}
63
64impl AbsAxis {
65 pub const fn new(min: i32, max: i32) -> Self {
67 Self { min, max }
68 }
69
70 pub fn map(&self, value: i32, extent: u32) -> i32 {
75 if extent == 0 {
76 return 0;
77 }
78 let span = i64::from(self.max) - i64::from(self.min);
79 if span <= 0 {
80 return 0;
81 }
82 let offset = i64::from(value.clamp(self.min, self.max)) - i64::from(self.min);
83 (offset * (i64::from(extent) - 1) / span) as i32
84 }
85}
86
87#[derive(Clone, Copy, Debug, Default)]
89struct Slot {
90 tracking_id: Option<i32>,
92 x: i32,
93 y: i32,
94 began: bool,
96 moved: bool,
98 ended: bool,
100}
101
102#[derive(Clone, Copy, Debug)]
104enum Deferred {
105 Button {
106 button: PointerButton,
107 state: ElementState,
108 },
109 Key {
110 code: KeyCode,
111 state: ElementState,
112 repeat: bool,
113 },
114}
115
116#[derive(Clone, Debug)]
118pub struct Translator {
119 surface: Size,
120 pointer: Point,
121 modifiers: Modifiers,
122
123 abs_x: Option<AbsAxis>,
124 abs_y: Option<AbsAxis>,
125
126 rel_x: i32,
127 rel_y: i32,
128 scroll_x: f32,
129 scroll_y: f32,
130
131 pending_abs_x: Option<i32>,
132 pending_abs_y: Option<i32>,
133
134 multitouch: bool,
136 slots: [Slot; MAX_SLOTS],
137 slot: usize,
138
139 composer: Composer,
143
144 touch_down: bool,
146 single_touch_active: bool,
147
148 deferred: Vec<Deferred>,
149}
150
151impl Translator {
152 pub fn new(size: Size) -> Self {
154 Self {
155 surface: size,
156 pointer: Point::new(size.width as i32 / 2, size.height as i32 / 2),
157 modifiers: Modifiers::NONE,
158 abs_x: None,
159 abs_y: None,
160 rel_x: 0,
161 rel_y: 0,
162 scroll_x: 0.0,
163 scroll_y: 0.0,
164 pending_abs_x: None,
165 pending_abs_y: None,
166 multitouch: false,
167 slots: [Slot::default(); MAX_SLOTS],
168 slot: 0,
169 touch_down: false,
170 single_touch_active: false,
171 composer: Composer::new(&layout::US),
172 deferred: Vec::new(),
173 }
174 }
175
176 pub fn set_layout(&mut self, layout: &'static Layout) {
181 self.composer.set_layout(layout);
182 }
183
184 pub fn layout(&self) -> &'static Layout {
186 self.composer.layout()
187 }
188
189 pub fn set_abs_range(&mut self, axis: u16, range: AbsAxis) {
194 match axis {
195 abs::X | abs::MT_POSITION_X => self.abs_x = Some(range),
196 abs::Y | abs::MT_POSITION_Y => self.abs_y = Some(range),
197 _ => {}
198 }
199 }
200
201 pub fn resize(&mut self, size: Size) {
203 self.surface = size;
204 self.pointer = self.clamp(self.pointer);
205 }
206
207 pub fn pointer(&self) -> Point {
209 self.pointer
210 }
211
212 pub fn set_pointer(&mut self, position: Point) {
217 self.pointer = self.clamp(position);
218 }
219
220 pub fn modifiers(&self) -> Modifiers {
222 self.modifiers
223 }
224
225 pub fn abs_ranges(&self) -> (Option<AbsAxis>, Option<AbsAxis>) {
227 (self.abs_x, self.abs_y)
228 }
229
230 pub fn is_multitouch(&self) -> bool {
232 self.multitouch
233 }
234
235 pub fn feed(&mut self, event: RawEvent, out: &mut Vec<InputEvent>) {
237 match event.kind {
238 ev::SYN => match event.code {
239 syn::REPORT => self.flush(out),
240 syn::DROPPED => self.discard_frame(),
243 _ => {}
244 },
245 ev::REL => self.feed_rel(event),
246 ev::ABS => self.feed_abs(event),
247 ev::KEY => self.feed_key(event),
248 _ => {}
249 }
250 }
251
252 pub fn feed_all(&mut self, events: &[RawEvent], out: &mut Vec<InputEvent>) {
254 for event in events {
255 self.feed(*event, out);
256 }
257 }
258
259 fn feed_rel(&mut self, event: RawEvent) {
260 match event.code {
261 rel::X => self.rel_x += event.value,
262 rel::Y => self.rel_y += event.value,
263 rel::WHEEL => self.scroll_y -= event.value as f32 * SCROLL_STEP,
264 rel::HWHEEL => self.scroll_x += event.value as f32 * SCROLL_STEP,
265 _ => {}
266 }
267 }
268
269 fn feed_abs(&mut self, event: RawEvent) {
270 match event.code {
271 abs::MT_SLOT => {
272 self.multitouch = true;
273 self.slot = (event.value.max(0) as usize).min(MAX_SLOTS - 1);
274 }
275 abs::MT_TRACKING_ID => {
276 self.multitouch = true;
277 let slot = &mut self.slots[self.slot];
278 if event.value < 0 {
279 if slot.tracking_id.is_some() {
280 slot.ended = true;
281 }
282 } else {
283 slot.tracking_id = Some(event.value);
284 slot.began = true;
285 }
286 }
287 abs::MT_POSITION_X => {
288 self.multitouch = true;
289 let slot = &mut self.slots[self.slot];
290 slot.x = event.value;
291 slot.moved = true;
292 }
293 abs::MT_POSITION_Y => {
294 self.multitouch = true;
295 let slot = &mut self.slots[self.slot];
296 slot.y = event.value;
297 slot.moved = true;
298 }
299 abs::X => self.pending_abs_x = Some(event.value),
300 abs::Y => self.pending_abs_y = Some(event.value),
301 _ => {}
302 }
303 }
304
305 fn feed_key(&mut self, event: RawEvent) {
306 let state = match event.value {
307 key_value::UP => ElementState::Up,
308 key_value::DOWN | key_value::REPEAT => ElementState::Down,
309 _ => return,
310 };
311 let repeat = event.value == key_value::REPEAT;
312
313 match event.code {
314 btn::LEFT | btn::RIGHT | btn::MIDDLE => {
315 if repeat {
317 return;
318 }
319 let button = match event.code {
320 btn::LEFT => PointerButton::Left,
321 btn::RIGHT => PointerButton::Right,
322 _ => PointerButton::Middle,
323 };
324 self.deferred.push(Deferred::Button { button, state });
325 }
326 btn::TOUCH => self.touch_down = state.is_down(),
327 code => {
328 let key = key_code(code);
329 self.update_modifiers(key, state.is_down());
330 self.deferred.push(Deferred::Key {
331 code: key,
332 state,
333 repeat,
334 });
335 }
336 }
337 }
338
339 fn update_modifiers(&mut self, key: KeyCode, down: bool) {
340 let bit = match key {
341 KeyCode::ShiftLeft | KeyCode::ShiftRight => Modifiers::SHIFT,
342 KeyCode::ControlLeft | KeyCode::ControlRight => Modifiers::CTRL,
343 KeyCode::AltLeft | KeyCode::AltRight => Modifiers::ALT,
344 KeyCode::SuperLeft | KeyCode::SuperRight => Modifiers::SUPER,
345 _ => return,
346 };
347 self.modifiers = self.modifiers.set(bit, down);
348 }
349
350 fn clamp(&self, point: Point) -> Point {
351 Point::new(
352 point
353 .x
354 .clamp(0, self.surface.width.saturating_sub(1) as i32),
355 point
356 .y
357 .clamp(0, self.surface.height.saturating_sub(1) as i32),
358 )
359 }
360
361 fn discard_frame(&mut self) {
363 self.rel_x = 0;
364 self.rel_y = 0;
365 self.scroll_x = 0.0;
366 self.scroll_y = 0.0;
367 self.pending_abs_x = None;
368 self.pending_abs_y = None;
369 self.deferred.clear();
370 for slot in &mut self.slots {
371 slot.began = false;
372 slot.moved = false;
373 slot.ended = false;
374 }
375 }
376
377 fn flush(&mut self, out: &mut Vec<InputEvent>) {
379 if self.multitouch {
380 self.flush_slots(out);
381 } else {
382 self.flush_pointer(out);
383 }
384
385 if self.scroll_x != 0.0 || self.scroll_y != 0.0 {
386 out.push(InputEvent::PointerScroll {
387 delta_x: self.scroll_x,
388 delta_y: self.scroll_y,
389 position: self.pointer,
390 });
391 self.scroll_x = 0.0;
392 self.scroll_y = 0.0;
393 }
394
395 for deferred in std::mem::take(&mut self.deferred) {
396 out.push(match deferred {
397 Deferred::Button { button, state } => InputEvent::PointerButton {
398 button,
399 state,
400 position: self.pointer,
401 modifiers: self.modifiers,
402 },
403 Deferred::Key {
404 code,
405 state,
406 repeat,
407 } => {
408 out.push(InputEvent::Key {
412 code,
413 state,
414 repeat,
415 modifiers: self.modifiers,
416 });
417 let composed = self.composer.feed(code, state, self.modifiers);
418 for &ch in composed.as_slice() {
419 out.push(InputEvent::Text { ch });
420 }
421 continue;
422 }
423 });
424 }
425 }
426
427 fn flush_pointer(&mut self, out: &mut Vec<InputEvent>) {
428 let absolute = self.pending_abs_x.is_some() || self.pending_abs_y.is_some();
429
430 let moved = if absolute {
431 let x = match (self.pending_abs_x.take(), self.abs_x) {
432 (Some(v), Some(axis)) => axis.map(v, self.surface.width),
433 _ => self.pointer.x,
434 };
435 let y = match (self.pending_abs_y.take(), self.abs_y) {
436 (Some(v), Some(axis)) => axis.map(v, self.surface.height),
437 _ => self.pointer.y,
438 };
439 let next = self.clamp(Point::new(x, y));
440 let moved = next != self.pointer;
441 self.pointer = next;
442 moved
443 } else if self.rel_x != 0 || self.rel_y != 0 {
444 let next = self.clamp(Point::new(
445 self.pointer.x + self.rel_x,
446 self.pointer.y + self.rel_y,
447 ));
448 self.rel_x = 0;
449 self.rel_y = 0;
450 let moved = next != self.pointer;
451 self.pointer = next;
452 moved
453 } else {
454 false
455 };
456
457 if self.touch_down || self.single_touch_active {
459 self.flush_single_touch(out);
460 return;
461 }
462
463 if moved {
464 out.push(InputEvent::PointerMoved {
465 position: self.pointer,
466 });
467 }
468 }
469
470 fn flush_single_touch(&mut self, out: &mut Vec<InputEvent>) {
471 const ID: TouchId = 0;
472 match (self.touch_down, self.single_touch_active) {
473 (true, false) => {
474 self.single_touch_active = true;
475 out.push(InputEvent::TouchDown {
476 id: ID,
477 position: self.pointer,
478 });
479 }
480 (true, true) => out.push(InputEvent::TouchMoved {
481 id: ID,
482 position: self.pointer,
483 }),
484 (false, true) => {
485 self.single_touch_active = false;
486 out.push(InputEvent::TouchUp {
487 id: ID,
488 position: self.pointer,
489 cancelled: false,
490 });
491 }
492 (false, false) => {}
493 }
494 }
495
496 fn flush_slots(&mut self, out: &mut Vec<InputEvent>) {
497 for index in 0..MAX_SLOTS {
498 let slot = self.slots[index];
499 if !(slot.began || slot.moved || slot.ended) {
500 continue;
501 }
502
503 let id = index as TouchId;
507 let position = self.map_slot(&slot);
508
509 if slot.began {
510 out.push(InputEvent::TouchDown { id, position });
511 } else if slot.moved && slot.tracking_id.is_some() {
512 out.push(InputEvent::TouchMoved { id, position });
513 }
514
515 if slot.ended {
516 out.push(InputEvent::TouchUp {
517 id,
518 position,
519 cancelled: false,
520 });
521 }
522
523 let slot = &mut self.slots[index];
524 slot.began = false;
525 slot.moved = false;
526 if slot.ended {
527 slot.ended = false;
528 slot.tracking_id = None;
529 }
530 }
531 }
532
533 fn map_slot(&self, slot: &Slot) -> Point {
534 let x = self
535 .abs_x
536 .map_or(slot.x, |axis| axis.map(slot.x, self.surface.width));
537 let y = self
538 .abs_y
539 .map_or(slot.y, |axis| axis.map(slot.y, self.surface.height));
540 self.clamp(Point::new(x, y))
541 }
542}
543
544#[cfg(test)]
545mod tests {
546 use super::*;
547
548 const SURFACE: Size = Size::new(800, 480);
549
550 fn translator() -> Translator {
551 Translator::new(SURFACE)
552 }
553
554 fn touchscreen() -> Translator {
555 let mut t = translator();
556 t.set_abs_range(abs::MT_POSITION_X, AbsAxis::new(0, 4095));
557 t.set_abs_range(abs::MT_POSITION_Y, AbsAxis::new(0, 4095));
558 t
559 }
560
561 fn drain(t: &mut Translator, events: &[RawEvent]) -> Vec<InputEvent> {
562 let mut out = Vec::new();
563 t.feed_all(events, &mut out);
564 out
565 }
566
567 #[test]
568 fn nothing_is_emitted_before_the_frame_closes() {
569 let mut t = translator();
570 let out = drain(
571 &mut t,
572 &[
573 RawEvent::new(ev::REL, rel::X, 10),
574 RawEvent::new(ev::REL, rel::Y, 5),
575 ],
576 );
577 assert!(out.is_empty(), "emitted mid-frame: {out:?}");
578 }
579
580 #[test]
581 fn both_axes_become_one_move() {
582 let mut t = translator();
585 let start = t.pointer();
586 let out = drain(
587 &mut t,
588 &[
589 RawEvent::new(ev::REL, rel::X, 10),
590 RawEvent::new(ev::REL, rel::Y, 5),
591 RawEvent::SYN,
592 ],
593 );
594 assert_eq!(
595 out,
596 vec![InputEvent::PointerMoved {
597 position: Point::new(start.x + 10, start.y + 5)
598 }]
599 );
600 }
601
602 #[test]
603 fn relative_motion_is_clamped_to_the_surface() {
604 let mut t = translator();
605 drain(
606 &mut t,
607 &[RawEvent::new(ev::REL, rel::X, -100_000), RawEvent::SYN],
608 );
609 assert_eq!(t.pointer().x, 0);
610 drain(
611 &mut t,
612 &[RawEvent::new(ev::REL, rel::X, 100_000), RawEvent::SYN],
613 );
614 assert_eq!(t.pointer().x, SURFACE.width as i32 - 1);
615 }
616
617 #[test]
618 fn a_frame_with_no_motion_emits_nothing() {
619 let mut t = translator();
620 assert!(drain(&mut t, &[RawEvent::SYN]).is_empty());
621 }
622
623 #[test]
624 fn absolute_pointers_map_through_their_axis_range() {
625 let mut t = translator();
627 t.set_abs_range(abs::X, AbsAxis::new(0, 32767));
628 t.set_abs_range(abs::Y, AbsAxis::new(0, 32767));
629
630 let out = drain(
631 &mut t,
632 &[
633 RawEvent::new(ev::ABS, abs::X, 32767),
634 RawEvent::new(ev::ABS, abs::Y, 0),
635 RawEvent::SYN,
636 ],
637 );
638 assert_eq!(
639 out,
640 vec![InputEvent::PointerMoved {
641 position: Point::new(799, 0)
642 }]
643 );
644 }
645
646 #[test]
647 fn an_absolute_axis_with_a_nonzero_minimum_still_reaches_the_origin() {
648 let axis = AbsAxis::new(100, 4195);
649 assert_eq!(axis.map(100, 800), 0);
650 assert_eq!(axis.map(4195, 800), 799);
651 }
652
653 #[test]
654 fn a_degenerate_axis_does_not_divide_by_zero() {
655 assert_eq!(AbsAxis::new(5, 5).map(5, 800), 0);
656 assert_eq!(AbsAxis::new(0, 100).map(50, 0), 0);
657 }
658
659 #[test]
660 fn a_click_carries_the_position_from_its_own_frame() {
661 let mut t = translator();
664 t.set_abs_range(abs::X, AbsAxis::new(0, 799));
665 t.set_abs_range(abs::Y, AbsAxis::new(0, 479));
666
667 let out = drain(
669 &mut t,
670 &[
671 RawEvent::new(ev::ABS, abs::X, 600),
672 RawEvent::new(ev::ABS, abs::Y, 300),
673 RawEvent::new(ev::KEY, btn::LEFT, key_value::DOWN),
674 RawEvent::SYN,
675 ],
676 );
677
678 assert_eq!(
679 out,
680 vec![
681 InputEvent::PointerMoved {
682 position: Point::new(600, 300)
683 },
684 InputEvent::PointerButton {
685 button: PointerButton::Left,
686 state: ElementState::Down,
687 position: Point::new(600, 300),
688 modifiers: Modifiers::NONE,
689 },
690 ]
691 );
692 }
693
694 #[test]
695 fn wheel_detents_become_pixels_and_scroll_the_right_way() {
696 let mut t = translator();
697 let out = drain(
698 &mut t,
699 &[RawEvent::new(ev::REL, rel::WHEEL, 1), RawEvent::SYN],
700 );
701 match out.as_slice() {
703 [InputEvent::PointerScroll { delta_y, .. }] => assert!(*delta_y < 0.0),
704 other => panic!("expected one scroll, got {other:?}"),
705 }
706 }
707
708 #[test]
709 fn modifiers_are_held_across_frames_and_reported_on_keys() {
710 let mut t = translator();
711 drain(
712 &mut t,
713 &[RawEvent::new(ev::KEY, 42, key_value::DOWN), RawEvent::SYN],
714 );
715 assert!(t.modifiers().contains(Modifiers::SHIFT));
716
717 let out = drain(
718 &mut t,
719 &[RawEvent::new(ev::KEY, 30, key_value::DOWN), RawEvent::SYN],
720 );
721 assert_eq!(
722 out,
723 vec![
724 InputEvent::Key {
725 code: KeyCode::A,
726 state: ElementState::Down,
727 repeat: false,
728 modifiers: Modifiers::SHIFT,
729 },
730 InputEvent::Text { ch: 'A' },
732 ]
733 );
734
735 drain(
736 &mut t,
737 &[RawEvent::new(ev::KEY, 42, key_value::UP), RawEvent::SYN],
738 );
739 assert!(t.modifiers().is_empty());
740 }
741
742 #[test]
743 fn auto_repeat_is_flagged_but_still_a_press() {
744 let mut t = translator();
745 let out = drain(
746 &mut t,
747 &[RawEvent::new(ev::KEY, 30, key_value::REPEAT), RawEvent::SYN],
748 );
749 assert_eq!(
750 out,
751 vec![
752 InputEvent::Key {
753 code: KeyCode::A,
754 state: ElementState::Down,
755 repeat: true,
756 modifiers: Modifiers::NONE,
757 },
758 InputEvent::Text { ch: 'a' },
760 ]
761 );
762 }
763
764 #[test]
765 fn raw_evdev_codes_become_norwegian_text() {
766 let mut t = translator();
769 t.set_layout(&crate::layout::NORWEGIAN);
770 let out = drain(
771 &mut t,
772 &[RawEvent::new(ev::KEY, 39, key_value::DOWN), RawEvent::SYN],
773 );
774 assert_eq!(
775 out,
776 vec![
777 InputEvent::Key {
778 code: KeyCode::Semicolon,
779 state: ElementState::Down,
780 repeat: false,
781 modifiers: Modifiers::NONE,
782 },
783 InputEvent::Text { ch: '\u{00f8}' },
784 ]
785 );
786 }
787
788 #[test]
789 fn a_dead_key_spans_two_evdev_frames() {
790 let mut t = translator();
793 t.set_layout(&crate::layout::NORWEGIAN);
794 let first = drain(
795 &mut t,
796 &[RawEvent::new(ev::KEY, 27, key_value::DOWN), RawEvent::SYN],
797 );
798 assert_eq!(
799 first.len(),
800 1,
801 "the dead key reports its position and no text: {first:?}"
802 );
803 let second = drain(
804 &mut t,
805 &[RawEvent::new(ev::KEY, 24, key_value::DOWN), RawEvent::SYN],
806 );
807 assert!(
808 second.contains(&InputEvent::Text { ch: '\u{00f6}' }),
809 "expected ö from ¨ then o, got {second:?}"
810 );
811 }
812
813 #[test]
814 fn buttons_never_auto_repeat() {
815 let mut t = translator();
816 let out = drain(
817 &mut t,
818 &[
819 RawEvent::new(ev::KEY, btn::LEFT, key_value::REPEAT),
820 RawEvent::SYN,
821 ],
822 );
823 assert!(out.is_empty(), "a held button repeated: {out:?}");
824 }
825
826 #[test]
827 fn a_single_finger_reports_down_move_and_up() {
828 let mut t = touchscreen();
829 let down = drain(
830 &mut t,
831 &[
832 RawEvent::new(ev::ABS, abs::MT_SLOT, 0),
833 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, 77),
834 RawEvent::new(ev::ABS, abs::MT_POSITION_X, 0),
835 RawEvent::new(ev::ABS, abs::MT_POSITION_Y, 0),
836 RawEvent::SYN,
837 ],
838 );
839 assert_eq!(
840 down,
841 vec![InputEvent::TouchDown {
842 id: 0,
843 position: Point::new(0, 0)
844 }]
845 );
846
847 let moved = drain(
849 &mut t,
850 &[
851 RawEvent::new(ev::ABS, abs::MT_POSITION_X, 4095),
852 RawEvent::SYN,
853 ],
854 );
855 assert_eq!(
856 moved,
857 vec![InputEvent::TouchMoved {
858 id: 0,
859 position: Point::new(799, 0)
860 }]
861 );
862
863 let up = drain(
864 &mut t,
865 &[
866 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, -1),
867 RawEvent::SYN,
868 ],
869 );
870 assert_eq!(
871 up,
872 vec![InputEvent::TouchUp {
873 id: 0,
874 position: Point::new(799, 0),
875 cancelled: false
876 }]
877 );
878 }
879
880 #[test]
881 fn two_fingers_keep_separate_identities() {
882 let mut t = touchscreen();
883 let out = drain(
884 &mut t,
885 &[
886 RawEvent::new(ev::ABS, abs::MT_SLOT, 0),
887 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, 10),
888 RawEvent::new(ev::ABS, abs::MT_POSITION_X, 0),
889 RawEvent::new(ev::ABS, abs::MT_POSITION_Y, 0),
890 RawEvent::new(ev::ABS, abs::MT_SLOT, 1),
891 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, 11),
892 RawEvent::new(ev::ABS, abs::MT_POSITION_X, 4095),
893 RawEvent::new(ev::ABS, abs::MT_POSITION_Y, 4095),
894 RawEvent::SYN,
895 ],
896 );
897
898 assert_eq!(
899 out,
900 vec![
901 InputEvent::TouchDown {
902 id: 0,
903 position: Point::new(0, 0)
904 },
905 InputEvent::TouchDown {
906 id: 1,
907 position: Point::new(799, 479)
908 },
909 ]
910 );
911 }
912
913 #[test]
914 fn lifting_one_finger_leaves_the_other_down() {
915 let mut t = touchscreen();
916 drain(
917 &mut t,
918 &[
919 RawEvent::new(ev::ABS, abs::MT_SLOT, 0),
920 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, 10),
921 RawEvent::new(ev::ABS, abs::MT_SLOT, 1),
922 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, 11),
923 RawEvent::SYN,
924 ],
925 );
926
927 let out = drain(
928 &mut t,
929 &[
930 RawEvent::new(ev::ABS, abs::MT_SLOT, 0),
931 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, -1),
932 RawEvent::SYN,
933 ],
934 );
935 assert_eq!(out.len(), 1);
936 assert!(matches!(out[0], InputEvent::TouchUp { id: 0, .. }));
937
938 let out = drain(
940 &mut t,
941 &[
942 RawEvent::new(ev::ABS, abs::MT_SLOT, 1),
943 RawEvent::new(ev::ABS, abs::MT_POSITION_X, 2048),
944 RawEvent::SYN,
945 ],
946 );
947 assert!(matches!(out[0], InputEvent::TouchMoved { id: 1, .. }));
948 }
949
950 #[test]
951 fn a_reused_slot_starts_a_new_contact() {
952 let mut t = touchscreen();
953 drain(
954 &mut t,
955 &[
956 RawEvent::new(ev::ABS, abs::MT_SLOT, 0),
957 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, 10),
958 RawEvent::SYN,
959 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, -1),
960 RawEvent::SYN,
961 ],
962 );
963 let out = drain(
964 &mut t,
965 &[
966 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, 12),
967 RawEvent::SYN,
968 ],
969 );
970 assert!(
971 matches!(out[0], InputEvent::TouchDown { id: 0, .. }),
972 "a recycled slot must report a fresh contact, got {out:?}"
973 );
974 }
975
976 #[test]
977 fn a_slot_index_beyond_capacity_does_not_panic() {
978 let mut t = touchscreen();
979 let out = drain(
980 &mut t,
981 &[
982 RawEvent::new(ev::ABS, abs::MT_SLOT, 9999),
983 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, 1),
984 RawEvent::SYN,
985 ],
986 );
987 assert_eq!(out.len(), 1);
988 }
989
990 #[test]
991 fn single_contact_panels_without_slots_still_report_touches() {
992 let mut t = translator();
995 t.set_abs_range(abs::X, AbsAxis::new(0, 4095));
996 t.set_abs_range(abs::Y, AbsAxis::new(0, 4095));
997
998 let down = drain(
999 &mut t,
1000 &[
1001 RawEvent::new(ev::KEY, btn::TOUCH, key_value::DOWN),
1002 RawEvent::new(ev::ABS, abs::X, 2048),
1003 RawEvent::new(ev::ABS, abs::Y, 2048),
1004 RawEvent::SYN,
1005 ],
1006 );
1007 assert!(matches!(down[0], InputEvent::TouchDown { id: 0, .. }));
1008
1009 let up = drain(
1010 &mut t,
1011 &[
1012 RawEvent::new(ev::KEY, btn::TOUCH, key_value::UP),
1013 RawEvent::SYN,
1014 ],
1015 );
1016 assert!(matches!(up[0], InputEvent::TouchUp { id: 0, .. }));
1017 }
1018
1019 #[test]
1020 fn a_dropped_frame_is_discarded_rather_than_half_applied() {
1021 let mut t = translator();
1025 let out = drain(
1026 &mut t,
1027 &[
1028 RawEvent::new(ev::REL, rel::X, 50),
1029 RawEvent::new(ev::KEY, btn::LEFT, key_value::DOWN),
1030 RawEvent::new(ev::SYN, syn::DROPPED, 0),
1031 RawEvent::SYN,
1032 ],
1033 );
1034 assert!(out.is_empty(), "a dropped frame was emitted: {out:?}");
1035 }
1036
1037 #[test]
1038 fn resizing_pulls_the_pointer_back_inside() {
1039 let mut t = translator();
1040 drain(
1041 &mut t,
1042 &[RawEvent::new(ev::REL, rel::X, 10_000), RawEvent::SYN],
1043 );
1044 assert_eq!(t.pointer().x, 799);
1045
1046 t.resize(Size::new(320, 240));
1047 assert_eq!(t.pointer(), Point::new(319, 239));
1048 }
1049
1050 #[test]
1051 fn an_absolute_device_never_drifts_from_repeated_frames() {
1052 let mut t = translator();
1055 t.set_abs_range(abs::X, AbsAxis::new(0, 799));
1056 t.set_abs_range(abs::Y, AbsAxis::new(0, 479));
1057
1058 drain(
1059 &mut t,
1060 &[
1061 RawEvent::new(ev::ABS, abs::X, 100),
1062 RawEvent::new(ev::ABS, abs::Y, 100),
1063 RawEvent::SYN,
1064 ],
1065 );
1066 let again = drain(
1067 &mut t,
1068 &[
1069 RawEvent::new(ev::ABS, abs::X, 100),
1070 RawEvent::new(ev::ABS, abs::Y, 100),
1071 RawEvent::SYN,
1072 ],
1073 );
1074 assert!(again.is_empty(), "a repeated position moved the pointer");
1075 assert_eq!(t.pointer(), Point::new(100, 100));
1076 }
1077}