1use core::time::Duration;
28
29use denise::{ElementState, InputEvent, KeyCode, Modifiers, Point, PointerButton, Size, TouchId};
30
31use crate::layout::{self, Composer, Layout};
32
33use crate::codes::{abs, btn, ev, key_value, rel, syn};
34use crate::keymap::key_code;
35
36pub const MAX_SLOTS: usize = 10;
38
39const SCROLL_STEP: f32 = 40.0;
41
42const TOUCHPAD_SPEED: f32 = 1.0;
47
48const TAP_TIME: Duration = Duration::from_millis(200);
50
51const TAP_TRAVEL: f32 = 0.03;
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq)]
57pub struct RawEvent {
58 pub kind: u16,
60 pub code: u16,
62 pub value: i32,
64}
65
66impl RawEvent {
67 pub const fn new(kind: u16, code: u16, value: i32) -> Self {
69 Self { kind, code, value }
70 }
71
72 pub const SYN: Self = Self::new(ev::SYN, syn::REPORT, 0);
74}
75
76#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78pub struct AbsAxis {
79 pub min: i32,
81 pub max: i32,
83}
84
85impl AbsAxis {
86 pub const fn new(min: i32, max: i32) -> Self {
88 Self { min, max }
89 }
90
91 pub fn map(&self, value: i32, extent: u32) -> i32 {
96 if extent == 0 {
97 return 0;
98 }
99 let span = i64::from(self.max) - i64::from(self.min);
100 if span <= 0 {
101 return 0;
102 }
103 let offset = i64::from(value.clamp(self.min, self.max)) - i64::from(self.min);
104 (offset * (i64::from(extent) - 1) / span) as i32
105 }
106}
107
108#[derive(Clone, Copy, Debug, Default)]
110struct Slot {
111 tracking_id: Option<i32>,
113 x: i32,
114 y: i32,
115 began: bool,
117 moved: bool,
119 ended: bool,
121}
122
123#[derive(Clone, Copy, Debug, Default)]
125struct Touchpad {
126 last: [Option<(i32, i32)>; MAX_SLOTS],
129 fingers: usize,
131 carry_x: f32,
134 carry_y: f32,
135 tap_start: Option<Duration>,
137 tap_travel: f32,
139 tap_fingers: usize,
141 right_click: bool,
144 tool_fingers: usize,
147 single: (i32, i32),
150}
151
152#[derive(Clone, Copy, Debug)]
154enum Deferred {
155 Button {
156 button: PointerButton,
157 state: ElementState,
158 },
159 Key {
160 code: KeyCode,
161 state: ElementState,
162 repeat: bool,
163 },
164}
165
166#[derive(Clone, Debug)]
168pub struct Translator {
169 surface: Size,
170 pointer: Point,
171 modifiers: Modifiers,
172
173 abs_x: Option<AbsAxis>,
174 abs_y: Option<AbsAxis>,
175
176 rel_x: i32,
177 rel_y: i32,
178 scroll_x: f32,
179 scroll_y: f32,
180
181 pending_abs_x: Option<i32>,
182 pending_abs_y: Option<i32>,
183
184 multitouch: bool,
186 slots: [Slot; MAX_SLOTS],
187 slot: usize,
188
189 composer: Composer,
193
194 touch_down: bool,
196 single_touch_active: bool,
197
198 deferred: Vec<Deferred>,
199
200 touchpad: bool,
202 pad: Touchpad,
203 now: Option<Duration>,
205}
206
207impl Translator {
208 pub fn new(size: Size) -> Self {
210 Self {
211 surface: size,
212 pointer: Point::new(size.width as i32 / 2, size.height as i32 / 2),
213 modifiers: Modifiers::NONE,
214 abs_x: None,
215 abs_y: None,
216 rel_x: 0,
217 rel_y: 0,
218 scroll_x: 0.0,
219 scroll_y: 0.0,
220 pending_abs_x: None,
221 pending_abs_y: None,
222 multitouch: false,
223 slots: [Slot::default(); MAX_SLOTS],
224 slot: 0,
225 touch_down: false,
226 single_touch_active: false,
227 composer: Composer::new(&layout::US),
228 deferred: Vec::new(),
229 touchpad: false,
230 pad: Touchpad::default(),
231 now: None,
232 }
233 }
234
235 pub fn set_touchpad(&mut self, touchpad: bool) {
243 self.touchpad = touchpad;
244 self.pad = Touchpad::default();
245 }
246
247 pub fn is_touchpad(&self) -> bool {
249 self.touchpad
250 }
251
252 pub fn set_layout(&mut self, layout: &'static Layout) {
257 self.composer.set_layout(layout);
258 }
259
260 pub fn layout(&self) -> &'static Layout {
262 self.composer.layout()
263 }
264
265 pub fn set_abs_range(&mut self, axis: u16, range: AbsAxis) {
270 match axis {
271 abs::X | abs::MT_POSITION_X => self.abs_x = Some(range),
272 abs::Y | abs::MT_POSITION_Y => self.abs_y = Some(range),
273 _ => {}
274 }
275 }
276
277 pub fn resize(&mut self, size: Size) {
279 self.surface = size;
280 self.pointer = self.clamp(self.pointer);
281 }
282
283 pub fn pointer(&self) -> Point {
285 self.pointer
286 }
287
288 pub fn set_pointer(&mut self, position: Point) {
293 self.pointer = self.clamp(position);
294 }
295
296 pub fn modifiers(&self) -> Modifiers {
298 self.modifiers
299 }
300
301 pub fn abs_ranges(&self) -> (Option<AbsAxis>, Option<AbsAxis>) {
303 (self.abs_x, self.abs_y)
304 }
305
306 pub fn is_multitouch(&self) -> bool {
308 self.multitouch
309 }
310
311 pub fn feed(&mut self, event: RawEvent, out: &mut Vec<InputEvent>) {
313 match event.kind {
314 ev::SYN => match event.code {
315 syn::REPORT => self.flush(out),
316 syn::DROPPED => self.discard_frame(),
319 _ => {}
320 },
321 ev::REL => self.feed_rel(event),
322 ev::ABS => self.feed_abs(event),
323 ev::KEY => self.feed_key(event),
324 _ => {}
325 }
326 }
327
328 pub fn feed_at(&mut self, event: RawEvent, at: Duration, out: &mut Vec<InputEvent>) {
334 self.now = Some(at);
335 self.feed(event, out);
336 }
337
338 pub fn feed_all(&mut self, events: &[RawEvent], out: &mut Vec<InputEvent>) {
340 for event in events {
341 self.feed(*event, out);
342 }
343 }
344
345 fn feed_rel(&mut self, event: RawEvent) {
346 match event.code {
347 rel::X => self.rel_x += event.value,
348 rel::Y => self.rel_y += event.value,
349 rel::WHEEL => self.scroll_y -= event.value as f32 * SCROLL_STEP,
350 rel::HWHEEL => self.scroll_x += event.value as f32 * SCROLL_STEP,
351 _ => {}
352 }
353 }
354
355 fn feed_abs(&mut self, event: RawEvent) {
356 match event.code {
357 abs::MT_SLOT => {
358 self.multitouch = true;
359 self.slot = (event.value.max(0) as usize).min(MAX_SLOTS - 1);
360 }
361 abs::MT_TRACKING_ID => {
362 self.multitouch = true;
363 let slot = &mut self.slots[self.slot];
364 if event.value < 0 {
365 if slot.tracking_id.is_some() {
366 slot.ended = true;
367 }
368 } else {
369 slot.tracking_id = Some(event.value);
370 slot.began = true;
371 }
372 }
373 abs::MT_POSITION_X => {
374 self.multitouch = true;
375 let slot = &mut self.slots[self.slot];
376 slot.x = event.value;
377 slot.moved = true;
378 }
379 abs::MT_POSITION_Y => {
380 self.multitouch = true;
381 let slot = &mut self.slots[self.slot];
382 slot.y = event.value;
383 slot.moved = true;
384 }
385 abs::X => self.pending_abs_x = Some(event.value),
386 abs::Y => self.pending_abs_y = Some(event.value),
387 _ => {}
388 }
389 }
390
391 fn feed_key(&mut self, event: RawEvent) {
392 let state = match event.value {
393 key_value::UP => ElementState::Up,
394 key_value::DOWN | key_value::REPEAT => ElementState::Down,
395 _ => return,
396 };
397 let repeat = event.value == key_value::REPEAT;
398
399 match event.code {
400 btn::LEFT | btn::RIGHT | btn::MIDDLE => {
401 if repeat {
403 return;
404 }
405 let button = match event.code {
406 btn::LEFT => PointerButton::Left,
407 btn::RIGHT => PointerButton::Right,
408 _ => PointerButton::Middle,
409 };
410 self.deferred.push(Deferred::Button { button, state });
411 }
412 btn::TOUCH => self.touch_down = state.is_down(),
413 btn::TOOL_FINGER
417 | btn::TOOL_DOUBLETAP
418 | btn::TOOL_TRIPLETAP
419 | btn::TOOL_QUADTAP
420 | btn::TOOL_QUINTTAP => {
421 if repeat {
422 return;
423 }
424 let count = match event.code {
425 btn::TOOL_FINGER => 1,
426 btn::TOOL_DOUBLETAP => 2,
427 btn::TOOL_TRIPLETAP => 3,
428 btn::TOOL_QUADTAP => 4,
429 _ => 5,
430 };
431 if state.is_down() {
432 self.pad.tool_fingers = count;
433 } else if self.pad.tool_fingers == count {
434 self.pad.tool_fingers = 0;
435 }
436 }
437 btn::TOOL_PEN => {}
438 code => {
439 let key = key_code(code);
440 self.update_modifiers(key, state.is_down());
441 self.deferred.push(Deferred::Key {
442 code: key,
443 state,
444 repeat,
445 });
446 }
447 }
448 }
449
450 fn update_modifiers(&mut self, key: KeyCode, down: bool) {
451 let bit = match key {
452 KeyCode::ShiftLeft | KeyCode::ShiftRight => Modifiers::SHIFT,
453 KeyCode::ControlLeft | KeyCode::ControlRight => Modifiers::CTRL,
454 KeyCode::AltLeft | KeyCode::AltRight => Modifiers::ALT,
455 KeyCode::SuperLeft | KeyCode::SuperRight => Modifiers::SUPER,
456 _ => return,
457 };
458 self.modifiers = self.modifiers.set(bit, down);
459 }
460
461 fn clamp(&self, point: Point) -> Point {
462 Point::new(
463 point
464 .x
465 .clamp(0, self.surface.width.saturating_sub(1) as i32),
466 point
467 .y
468 .clamp(0, self.surface.height.saturating_sub(1) as i32),
469 )
470 }
471
472 fn discard_frame(&mut self) {
474 self.rel_x = 0;
475 self.rel_y = 0;
476 self.scroll_x = 0.0;
477 self.scroll_y = 0.0;
478 self.pending_abs_x = None;
479 self.pending_abs_y = None;
480 self.deferred.clear();
481 for slot in &mut self.slots {
482 slot.began = false;
483 slot.moved = false;
484 slot.ended = false;
485 }
486 }
487
488 fn flush(&mut self, out: &mut Vec<InputEvent>) {
490 if self.touchpad {
491 self.flush_touchpad(out);
492 } else if self.multitouch {
493 self.flush_slots(out);
494 } else {
495 self.flush_pointer(out);
496 }
497
498 if self.scroll_x != 0.0 || self.scroll_y != 0.0 {
499 out.push(InputEvent::PointerScroll {
500 delta_x: self.scroll_x,
501 delta_y: self.scroll_y,
502 position: self.pointer,
503 });
504 self.scroll_x = 0.0;
505 self.scroll_y = 0.0;
506 }
507
508 for deferred in std::mem::take(&mut self.deferred) {
509 out.push(match deferred {
510 Deferred::Button { button, state } => InputEvent::PointerButton {
511 button,
512 state,
513 position: self.pointer,
514 modifiers: self.modifiers,
515 },
516 Deferred::Key {
517 code,
518 state,
519 repeat,
520 } => {
521 out.push(InputEvent::Key {
525 code,
526 state,
527 repeat,
528 modifiers: self.modifiers,
529 });
530 let composed = self.composer.feed(code, state, self.modifiers);
531 for &ch in composed.as_slice() {
532 out.push(InputEvent::Text { ch });
533 }
534 continue;
535 }
536 });
537 }
538 }
539
540 fn flush_pointer(&mut self, out: &mut Vec<InputEvent>) {
541 let absolute = self.pending_abs_x.is_some() || self.pending_abs_y.is_some();
542
543 let moved = if absolute {
544 let x = match (self.pending_abs_x.take(), self.abs_x) {
545 (Some(v), Some(axis)) => axis.map(v, self.surface.width),
546 _ => self.pointer.x,
547 };
548 let y = match (self.pending_abs_y.take(), self.abs_y) {
549 (Some(v), Some(axis)) => axis.map(v, self.surface.height),
550 _ => self.pointer.y,
551 };
552 let next = self.clamp(Point::new(x, y));
553 let moved = next != self.pointer;
554 self.pointer = next;
555 moved
556 } else if self.rel_x != 0 || self.rel_y != 0 {
557 let next = self.clamp(Point::new(
558 self.pointer.x + self.rel_x,
559 self.pointer.y + self.rel_y,
560 ));
561 self.rel_x = 0;
562 self.rel_y = 0;
563 let moved = next != self.pointer;
564 self.pointer = next;
565 moved
566 } else {
567 false
568 };
569
570 if self.touch_down || self.single_touch_active {
572 self.flush_single_touch(out);
573 return;
574 }
575
576 if moved {
577 out.push(InputEvent::PointerMoved {
578 position: self.pointer,
579 });
580 }
581 }
582
583 fn flush_touchpad(&mut self, out: &mut Vec<InputEvent>) {
586 let mut now: [Option<(i32, i32)>; MAX_SLOTS] = [None; MAX_SLOTS];
588 let fingers = if self.multitouch {
589 for (index, slot) in self.slots.iter_mut().enumerate() {
590 if slot.tracking_id.is_some() && !slot.ended {
591 now[index] = Some((slot.x, slot.y));
592 }
593 slot.began = false;
594 slot.moved = false;
595 if slot.ended {
596 slot.ended = false;
597 slot.tracking_id = None;
598 }
599 }
600 self.pending_abs_x = None;
603 self.pending_abs_y = None;
604 now.iter().flatten().count()
605 } else {
606 if let Some(x) = self.pending_abs_x.take() {
607 self.pad.single.0 = x;
608 }
609 if let Some(y) = self.pending_abs_y.take() {
610 self.pad.single.1 = y;
611 }
612 if self.touch_down {
613 now[0] = Some(self.pad.single);
614 self.pad.tool_fingers.max(1)
615 } else {
616 0
617 }
618 };
619
620 let (mut dx, mut dy, mut moving) = (0.0f32, 0.0f32, 0u32);
624 if fingers == self.pad.fingers {
625 for (was, is) in self.pad.last.iter().zip(now.iter()) {
626 if let (Some(was), Some(is)) = (was, is) {
627 dx += (is.0 - was.0) as f32;
628 dy += (is.1 - was.1) as f32;
629 moving += 1;
630 }
631 }
632 } else {
633 self.pad.carry_x = 0.0;
634 self.pad.carry_y = 0.0;
635 }
636 if moving > 0 {
637 dx /= moving as f32;
638 dy /= moving as f32;
639 }
640
641 let span = self
642 .abs_x
643 .map_or(0, |axis| i64::from(axis.max) - i64::from(axis.min));
644 let scale = if span > 0 {
645 self.surface.width as f32 * TOUCHPAD_SPEED / span as f32
646 } else {
647 1.0
648 };
649
650 match fingers {
651 1 if moving > 0 => {
652 let x = dx * scale + self.pad.carry_x;
653 let y = dy * scale + self.pad.carry_y;
654 let (step_x, step_y) = (x.trunc(), y.trunc());
655 self.pad.carry_x = x - step_x;
656 self.pad.carry_y = y - step_y;
657 let next = self.clamp(Point::new(
658 self.pointer.x + step_x as i32,
659 self.pointer.y + step_y as i32,
660 ));
661 if next != self.pointer {
662 self.pointer = next;
663 out.push(InputEvent::PointerMoved {
664 position: self.pointer,
665 });
666 }
667 }
668 2.. if moving > 0 && (dx != 0.0 || dy != 0.0) => {
671 self.scroll_x += dx * scale;
672 self.scroll_y += dy * scale;
673 }
674 _ => {}
675 }
676
677 if self.pad.fingers == 0 && fingers > 0 {
680 self.pad.tap_start = self.now;
681 self.pad.tap_travel = 0.0;
682 self.pad.tap_fingers = 0;
683 }
684 if fingers > 0 {
685 self.pad.tap_travel += dx.abs() + dy.abs();
686 self.pad.tap_fingers = self.pad.tap_fingers.max(fingers);
687 let still = span <= 0 || self.pad.tap_travel <= TAP_TRAVEL * span as f32;
688 let brief = match (self.pad.tap_start, self.now) {
689 (Some(start), Some(now)) => now.saturating_sub(start) <= TAP_TIME,
690 _ => false,
691 };
692 if !still || !brief {
693 self.pad.tap_start = None;
694 }
695 }
696
697 for deferred in &mut self.deferred {
700 if let Deferred::Button {
701 button: button @ PointerButton::Left,
702 state,
703 } = deferred
704 {
705 self.pad.tap_start = None;
706 match state {
707 ElementState::Down if fingers >= 2 => {
708 self.pad.right_click = true;
709 *button = PointerButton::Right;
710 }
711 ElementState::Up if self.pad.right_click => {
712 self.pad.right_click = false;
713 *button = PointerButton::Right;
714 }
715 _ => {}
716 }
717 }
718 }
719
720 if fingers == 0
721 && self.pad.fingers > 0
722 && let (Some(start), Some(now)) = (self.pad.tap_start.take(), self.now)
723 && now.saturating_sub(start) <= TAP_TIME
724 {
725 let button = if self.pad.tap_fingers >= 2 {
726 PointerButton::Right
727 } else {
728 PointerButton::Left
729 };
730 for state in [ElementState::Down, ElementState::Up] {
731 self.deferred.push(Deferred::Button { button, state });
732 }
733 }
734
735 self.pad.last = now;
736 self.pad.fingers = fingers;
737 }
738
739 fn flush_single_touch(&mut self, out: &mut Vec<InputEvent>) {
740 const ID: TouchId = 0;
741 match (self.touch_down, self.single_touch_active) {
742 (true, false) => {
743 self.single_touch_active = true;
744 out.push(InputEvent::TouchDown {
745 id: ID,
746 position: self.pointer,
747 });
748 }
749 (true, true) => out.push(InputEvent::TouchMoved {
750 id: ID,
751 position: self.pointer,
752 }),
753 (false, true) => {
754 self.single_touch_active = false;
755 out.push(InputEvent::TouchUp {
756 id: ID,
757 position: self.pointer,
758 cancelled: false,
759 });
760 }
761 (false, false) => {}
762 }
763 }
764
765 fn flush_slots(&mut self, out: &mut Vec<InputEvent>) {
766 for index in 0..MAX_SLOTS {
767 let slot = self.slots[index];
768 if !(slot.began || slot.moved || slot.ended) {
769 continue;
770 }
771
772 let id = index as TouchId;
776 let position = self.map_slot(&slot);
777
778 if slot.began {
779 out.push(InputEvent::TouchDown { id, position });
780 } else if slot.moved && slot.tracking_id.is_some() {
781 out.push(InputEvent::TouchMoved { id, position });
782 }
783
784 if slot.ended {
785 out.push(InputEvent::TouchUp {
786 id,
787 position,
788 cancelled: false,
789 });
790 }
791
792 let slot = &mut self.slots[index];
793 slot.began = false;
794 slot.moved = false;
795 if slot.ended {
796 slot.ended = false;
797 slot.tracking_id = None;
798 }
799 }
800 }
801
802 fn map_slot(&self, slot: &Slot) -> Point {
803 let x = self
804 .abs_x
805 .map_or(slot.x, |axis| axis.map(slot.x, self.surface.width));
806 let y = self
807 .abs_y
808 .map_or(slot.y, |axis| axis.map(slot.y, self.surface.height));
809 self.clamp(Point::new(x, y))
810 }
811}
812
813#[cfg(test)]
814mod tests {
815 use super::*;
816
817 const SURFACE: Size = Size::new(800, 480);
818
819 fn translator() -> Translator {
820 Translator::new(SURFACE)
821 }
822
823 fn touchscreen() -> Translator {
824 let mut t = translator();
825 t.set_abs_range(abs::MT_POSITION_X, AbsAxis::new(0, 4095));
826 t.set_abs_range(abs::MT_POSITION_Y, AbsAxis::new(0, 4095));
827 t
828 }
829
830 fn drain(t: &mut Translator, events: &[RawEvent]) -> Vec<InputEvent> {
831 let mut out = Vec::new();
832 t.feed_all(events, &mut out);
833 out
834 }
835
836 #[test]
837 fn nothing_is_emitted_before_the_frame_closes() {
838 let mut t = translator();
839 let out = drain(
840 &mut t,
841 &[
842 RawEvent::new(ev::REL, rel::X, 10),
843 RawEvent::new(ev::REL, rel::Y, 5),
844 ],
845 );
846 assert!(out.is_empty(), "emitted mid-frame: {out:?}");
847 }
848
849 #[test]
850 fn both_axes_become_one_move() {
851 let mut t = translator();
854 let start = t.pointer();
855 let out = drain(
856 &mut t,
857 &[
858 RawEvent::new(ev::REL, rel::X, 10),
859 RawEvent::new(ev::REL, rel::Y, 5),
860 RawEvent::SYN,
861 ],
862 );
863 assert_eq!(
864 out,
865 vec![InputEvent::PointerMoved {
866 position: Point::new(start.x + 10, start.y + 5)
867 }]
868 );
869 }
870
871 #[test]
872 fn relative_motion_is_clamped_to_the_surface() {
873 let mut t = translator();
874 drain(
875 &mut t,
876 &[RawEvent::new(ev::REL, rel::X, -100_000), RawEvent::SYN],
877 );
878 assert_eq!(t.pointer().x, 0);
879 drain(
880 &mut t,
881 &[RawEvent::new(ev::REL, rel::X, 100_000), RawEvent::SYN],
882 );
883 assert_eq!(t.pointer().x, SURFACE.width as i32 - 1);
884 }
885
886 #[test]
887 fn a_frame_with_no_motion_emits_nothing() {
888 let mut t = translator();
889 assert!(drain(&mut t, &[RawEvent::SYN]).is_empty());
890 }
891
892 #[test]
893 fn absolute_pointers_map_through_their_axis_range() {
894 let mut t = translator();
896 t.set_abs_range(abs::X, AbsAxis::new(0, 32767));
897 t.set_abs_range(abs::Y, AbsAxis::new(0, 32767));
898
899 let out = drain(
900 &mut t,
901 &[
902 RawEvent::new(ev::ABS, abs::X, 32767),
903 RawEvent::new(ev::ABS, abs::Y, 0),
904 RawEvent::SYN,
905 ],
906 );
907 assert_eq!(
908 out,
909 vec![InputEvent::PointerMoved {
910 position: Point::new(799, 0)
911 }]
912 );
913 }
914
915 #[test]
916 fn an_absolute_axis_with_a_nonzero_minimum_still_reaches_the_origin() {
917 let axis = AbsAxis::new(100, 4195);
918 assert_eq!(axis.map(100, 800), 0);
919 assert_eq!(axis.map(4195, 800), 799);
920 }
921
922 #[test]
923 fn a_degenerate_axis_does_not_divide_by_zero() {
924 assert_eq!(AbsAxis::new(5, 5).map(5, 800), 0);
925 assert_eq!(AbsAxis::new(0, 100).map(50, 0), 0);
926 }
927
928 #[test]
929 fn a_click_carries_the_position_from_its_own_frame() {
930 let mut t = translator();
933 t.set_abs_range(abs::X, AbsAxis::new(0, 799));
934 t.set_abs_range(abs::Y, AbsAxis::new(0, 479));
935
936 let out = drain(
938 &mut t,
939 &[
940 RawEvent::new(ev::ABS, abs::X, 600),
941 RawEvent::new(ev::ABS, abs::Y, 300),
942 RawEvent::new(ev::KEY, btn::LEFT, key_value::DOWN),
943 RawEvent::SYN,
944 ],
945 );
946
947 assert_eq!(
948 out,
949 vec![
950 InputEvent::PointerMoved {
951 position: Point::new(600, 300)
952 },
953 InputEvent::PointerButton {
954 button: PointerButton::Left,
955 state: ElementState::Down,
956 position: Point::new(600, 300),
957 modifiers: Modifiers::NONE,
958 },
959 ]
960 );
961 }
962
963 #[test]
964 fn wheel_detents_become_pixels_and_scroll_the_right_way() {
965 let mut t = translator();
966 let out = drain(
967 &mut t,
968 &[RawEvent::new(ev::REL, rel::WHEEL, 1), RawEvent::SYN],
969 );
970 match out.as_slice() {
972 [InputEvent::PointerScroll { delta_y, .. }] => assert!(*delta_y < 0.0),
973 other => panic!("expected one scroll, got {other:?}"),
974 }
975 }
976
977 #[test]
978 fn modifiers_are_held_across_frames_and_reported_on_keys() {
979 let mut t = translator();
980 drain(
981 &mut t,
982 &[RawEvent::new(ev::KEY, 42, key_value::DOWN), RawEvent::SYN],
983 );
984 assert!(t.modifiers().contains(Modifiers::SHIFT));
985
986 let out = drain(
987 &mut t,
988 &[RawEvent::new(ev::KEY, 30, key_value::DOWN), RawEvent::SYN],
989 );
990 assert_eq!(
991 out,
992 vec![
993 InputEvent::Key {
994 code: KeyCode::A,
995 state: ElementState::Down,
996 repeat: false,
997 modifiers: Modifiers::SHIFT,
998 },
999 InputEvent::Text { ch: 'A' },
1001 ]
1002 );
1003
1004 drain(
1005 &mut t,
1006 &[RawEvent::new(ev::KEY, 42, key_value::UP), RawEvent::SYN],
1007 );
1008 assert!(t.modifiers().is_empty());
1009 }
1010
1011 #[test]
1012 fn auto_repeat_is_flagged_but_still_a_press() {
1013 let mut t = translator();
1014 let out = drain(
1015 &mut t,
1016 &[RawEvent::new(ev::KEY, 30, key_value::REPEAT), RawEvent::SYN],
1017 );
1018 assert_eq!(
1019 out,
1020 vec![
1021 InputEvent::Key {
1022 code: KeyCode::A,
1023 state: ElementState::Down,
1024 repeat: true,
1025 modifiers: Modifiers::NONE,
1026 },
1027 InputEvent::Text { ch: 'a' },
1029 ]
1030 );
1031 }
1032
1033 #[test]
1034 fn raw_evdev_codes_become_norwegian_text() {
1035 let mut t = translator();
1038 t.set_layout(&crate::layout::NORWEGIAN);
1039 let out = drain(
1040 &mut t,
1041 &[RawEvent::new(ev::KEY, 39, key_value::DOWN), RawEvent::SYN],
1042 );
1043 assert_eq!(
1044 out,
1045 vec![
1046 InputEvent::Key {
1047 code: KeyCode::Semicolon,
1048 state: ElementState::Down,
1049 repeat: false,
1050 modifiers: Modifiers::NONE,
1051 },
1052 InputEvent::Text { ch: '\u{00f8}' },
1053 ]
1054 );
1055 }
1056
1057 #[test]
1058 fn a_dead_key_spans_two_evdev_frames() {
1059 let mut t = translator();
1062 t.set_layout(&crate::layout::NORWEGIAN);
1063 let first = drain(
1064 &mut t,
1065 &[RawEvent::new(ev::KEY, 27, key_value::DOWN), RawEvent::SYN],
1066 );
1067 assert_eq!(
1068 first.len(),
1069 1,
1070 "the dead key reports its position and no text: {first:?}"
1071 );
1072 let second = drain(
1073 &mut t,
1074 &[RawEvent::new(ev::KEY, 24, key_value::DOWN), RawEvent::SYN],
1075 );
1076 assert!(
1077 second.contains(&InputEvent::Text { ch: '\u{00f6}' }),
1078 "expected ö from ¨ then o, got {second:?}"
1079 );
1080 }
1081
1082 #[test]
1083 fn buttons_never_auto_repeat() {
1084 let mut t = translator();
1085 let out = drain(
1086 &mut t,
1087 &[
1088 RawEvent::new(ev::KEY, btn::LEFT, key_value::REPEAT),
1089 RawEvent::SYN,
1090 ],
1091 );
1092 assert!(out.is_empty(), "a held button repeated: {out:?}");
1093 }
1094
1095 #[test]
1096 fn a_single_finger_reports_down_move_and_up() {
1097 let mut t = touchscreen();
1098 let down = drain(
1099 &mut t,
1100 &[
1101 RawEvent::new(ev::ABS, abs::MT_SLOT, 0),
1102 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, 77),
1103 RawEvent::new(ev::ABS, abs::MT_POSITION_X, 0),
1104 RawEvent::new(ev::ABS, abs::MT_POSITION_Y, 0),
1105 RawEvent::SYN,
1106 ],
1107 );
1108 assert_eq!(
1109 down,
1110 vec![InputEvent::TouchDown {
1111 id: 0,
1112 position: Point::new(0, 0)
1113 }]
1114 );
1115
1116 let moved = drain(
1118 &mut t,
1119 &[
1120 RawEvent::new(ev::ABS, abs::MT_POSITION_X, 4095),
1121 RawEvent::SYN,
1122 ],
1123 );
1124 assert_eq!(
1125 moved,
1126 vec![InputEvent::TouchMoved {
1127 id: 0,
1128 position: Point::new(799, 0)
1129 }]
1130 );
1131
1132 let up = drain(
1133 &mut t,
1134 &[
1135 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, -1),
1136 RawEvent::SYN,
1137 ],
1138 );
1139 assert_eq!(
1140 up,
1141 vec![InputEvent::TouchUp {
1142 id: 0,
1143 position: Point::new(799, 0),
1144 cancelled: false
1145 }]
1146 );
1147 }
1148
1149 #[test]
1150 fn two_fingers_keep_separate_identities() {
1151 let mut t = touchscreen();
1152 let out = drain(
1153 &mut t,
1154 &[
1155 RawEvent::new(ev::ABS, abs::MT_SLOT, 0),
1156 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, 10),
1157 RawEvent::new(ev::ABS, abs::MT_POSITION_X, 0),
1158 RawEvent::new(ev::ABS, abs::MT_POSITION_Y, 0),
1159 RawEvent::new(ev::ABS, abs::MT_SLOT, 1),
1160 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, 11),
1161 RawEvent::new(ev::ABS, abs::MT_POSITION_X, 4095),
1162 RawEvent::new(ev::ABS, abs::MT_POSITION_Y, 4095),
1163 RawEvent::SYN,
1164 ],
1165 );
1166
1167 assert_eq!(
1168 out,
1169 vec![
1170 InputEvent::TouchDown {
1171 id: 0,
1172 position: Point::new(0, 0)
1173 },
1174 InputEvent::TouchDown {
1175 id: 1,
1176 position: Point::new(799, 479)
1177 },
1178 ]
1179 );
1180 }
1181
1182 #[test]
1183 fn lifting_one_finger_leaves_the_other_down() {
1184 let mut t = touchscreen();
1185 drain(
1186 &mut t,
1187 &[
1188 RawEvent::new(ev::ABS, abs::MT_SLOT, 0),
1189 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, 10),
1190 RawEvent::new(ev::ABS, abs::MT_SLOT, 1),
1191 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, 11),
1192 RawEvent::SYN,
1193 ],
1194 );
1195
1196 let out = drain(
1197 &mut t,
1198 &[
1199 RawEvent::new(ev::ABS, abs::MT_SLOT, 0),
1200 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, -1),
1201 RawEvent::SYN,
1202 ],
1203 );
1204 assert_eq!(out.len(), 1);
1205 assert!(matches!(out[0], InputEvent::TouchUp { id: 0, .. }));
1206
1207 let out = drain(
1209 &mut t,
1210 &[
1211 RawEvent::new(ev::ABS, abs::MT_SLOT, 1),
1212 RawEvent::new(ev::ABS, abs::MT_POSITION_X, 2048),
1213 RawEvent::SYN,
1214 ],
1215 );
1216 assert!(matches!(out[0], InputEvent::TouchMoved { id: 1, .. }));
1217 }
1218
1219 #[test]
1220 fn a_reused_slot_starts_a_new_contact() {
1221 let mut t = touchscreen();
1222 drain(
1223 &mut t,
1224 &[
1225 RawEvent::new(ev::ABS, abs::MT_SLOT, 0),
1226 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, 10),
1227 RawEvent::SYN,
1228 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, -1),
1229 RawEvent::SYN,
1230 ],
1231 );
1232 let out = drain(
1233 &mut t,
1234 &[
1235 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, 12),
1236 RawEvent::SYN,
1237 ],
1238 );
1239 assert!(
1240 matches!(out[0], InputEvent::TouchDown { id: 0, .. }),
1241 "a recycled slot must report a fresh contact, got {out:?}"
1242 );
1243 }
1244
1245 #[test]
1246 fn a_slot_index_beyond_capacity_does_not_panic() {
1247 let mut t = touchscreen();
1248 let out = drain(
1249 &mut t,
1250 &[
1251 RawEvent::new(ev::ABS, abs::MT_SLOT, 9999),
1252 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, 1),
1253 RawEvent::SYN,
1254 ],
1255 );
1256 assert_eq!(out.len(), 1);
1257 }
1258
1259 #[test]
1260 fn single_contact_panels_without_slots_still_report_touches() {
1261 let mut t = translator();
1264 t.set_abs_range(abs::X, AbsAxis::new(0, 4095));
1265 t.set_abs_range(abs::Y, AbsAxis::new(0, 4095));
1266
1267 let down = drain(
1268 &mut t,
1269 &[
1270 RawEvent::new(ev::KEY, btn::TOUCH, key_value::DOWN),
1271 RawEvent::new(ev::ABS, abs::X, 2048),
1272 RawEvent::new(ev::ABS, abs::Y, 2048),
1273 RawEvent::SYN,
1274 ],
1275 );
1276 assert!(matches!(down[0], InputEvent::TouchDown { id: 0, .. }));
1277
1278 let up = drain(
1279 &mut t,
1280 &[
1281 RawEvent::new(ev::KEY, btn::TOUCH, key_value::UP),
1282 RawEvent::SYN,
1283 ],
1284 );
1285 assert!(matches!(up[0], InputEvent::TouchUp { id: 0, .. }));
1286 }
1287
1288 #[test]
1289 fn a_dropped_frame_is_discarded_rather_than_half_applied() {
1290 let mut t = translator();
1294 let out = drain(
1295 &mut t,
1296 &[
1297 RawEvent::new(ev::REL, rel::X, 50),
1298 RawEvent::new(ev::KEY, btn::LEFT, key_value::DOWN),
1299 RawEvent::new(ev::SYN, syn::DROPPED, 0),
1300 RawEvent::SYN,
1301 ],
1302 );
1303 assert!(out.is_empty(), "a dropped frame was emitted: {out:?}");
1304 }
1305
1306 #[test]
1307 fn resizing_pulls_the_pointer_back_inside() {
1308 let mut t = translator();
1309 drain(
1310 &mut t,
1311 &[RawEvent::new(ev::REL, rel::X, 10_000), RawEvent::SYN],
1312 );
1313 assert_eq!(t.pointer().x, 799);
1314
1315 t.resize(Size::new(320, 240));
1316 assert_eq!(t.pointer(), Point::new(319, 239));
1317 }
1318
1319 #[test]
1320 fn an_absolute_device_never_drifts_from_repeated_frames() {
1321 let mut t = translator();
1324 t.set_abs_range(abs::X, AbsAxis::new(0, 799));
1325 t.set_abs_range(abs::Y, AbsAxis::new(0, 479));
1326
1327 drain(
1328 &mut t,
1329 &[
1330 RawEvent::new(ev::ABS, abs::X, 100),
1331 RawEvent::new(ev::ABS, abs::Y, 100),
1332 RawEvent::SYN,
1333 ],
1334 );
1335 let again = drain(
1336 &mut t,
1337 &[
1338 RawEvent::new(ev::ABS, abs::X, 100),
1339 RawEvent::new(ev::ABS, abs::Y, 100),
1340 RawEvent::SYN,
1341 ],
1342 );
1343 assert!(again.is_empty(), "a repeated position moved the pointer");
1344 assert_eq!(t.pointer(), Point::new(100, 100));
1345 }
1346
1347 fn touchpad() -> Translator {
1351 let mut t = translator();
1352 for axis in [abs::X, abs::MT_POSITION_X] {
1353 t.set_abs_range(axis, AbsAxis::new(0, 1599));
1354 }
1355 for axis in [abs::Y, abs::MT_POSITION_Y] {
1356 t.set_abs_range(axis, AbsAxis::new(0, 999));
1357 }
1358 t.set_touchpad(true);
1359 t
1360 }
1361
1362 fn at(t: &mut Translator, ms: u64, events: &[RawEvent]) -> Vec<InputEvent> {
1364 let mut out = Vec::new();
1365 for event in events.iter().chain([&RawEvent::SYN]) {
1366 t.feed_at(*event, Duration::from_millis(ms), &mut out);
1367 }
1368 out
1369 }
1370
1371 fn touch(slot: i32, id: i32, x: i32, y: i32) -> [RawEvent; 4] {
1372 [
1373 RawEvent::new(ev::ABS, abs::MT_SLOT, slot),
1374 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, id),
1375 RawEvent::new(ev::ABS, abs::MT_POSITION_X, x),
1376 RawEvent::new(ev::ABS, abs::MT_POSITION_Y, y),
1377 ]
1378 }
1379
1380 fn slide(slot: i32, x: i32, y: i32) -> [RawEvent; 3] {
1381 [
1382 RawEvent::new(ev::ABS, abs::MT_SLOT, slot),
1383 RawEvent::new(ev::ABS, abs::MT_POSITION_X, x),
1384 RawEvent::new(ev::ABS, abs::MT_POSITION_Y, y),
1385 ]
1386 }
1387
1388 fn lift(slot: i32) -> [RawEvent; 2] {
1389 [
1390 RawEvent::new(ev::ABS, abs::MT_SLOT, slot),
1391 RawEvent::new(ev::ABS, abs::MT_TRACKING_ID, -1),
1392 ]
1393 }
1394
1395 fn buttons(out: &[InputEvent]) -> Vec<(PointerButton, ElementState)> {
1396 out.iter()
1397 .filter_map(|event| match event {
1398 InputEvent::PointerButton { button, state, .. } => Some((*button, *state)),
1399 _ => None,
1400 })
1401 .collect()
1402 }
1403
1404 #[test]
1405 fn a_finger_on_a_touchpad_moves_the_pointer_by_its_travel_not_to_where_it_is() {
1406 let mut t = touchpad();
1409 let start = t.pointer();
1410
1411 let down = at(&mut t, 0, &touch(0, 1, 0, 0));
1412 assert!(down.is_empty(), "a finger landing did something: {down:?}");
1413 assert_eq!(t.pointer(), start);
1414
1415 let moved = at(&mut t, 10, &slide(0, 200, 100));
1416 assert_eq!(
1417 moved,
1418 vec![InputEvent::PointerMoved {
1419 position: Point::new(start.x + 100, start.y + 50)
1420 }]
1421 );
1422 }
1423
1424 #[test]
1425 fn slow_travel_on_a_touchpad_is_not_lost_to_rounding() {
1426 let mut t = touchpad();
1427 let start = t.pointer();
1428 at(&mut t, 0, &touch(0, 1, 100, 100));
1429 for step in 1..=10 {
1430 at(&mut t, step * 10, &slide(0, 100 + step as i32, 100));
1431 }
1432 assert_eq!(t.pointer().x, start.x + 5);
1434 }
1435
1436 #[test]
1437 fn two_fingers_scroll_the_way_a_wheel_does_and_leave_the_pointer_alone() {
1438 let mut t = touchpad();
1439 let start = t.pointer();
1440 at(
1441 &mut t,
1442 0,
1443 &[touch(0, 1, 400, 600), touch(1, 2, 800, 600)].concat(),
1444 );
1445 let out = at(
1447 &mut t,
1448 20,
1449 &[slide(0, 400, 400), slide(1, 800, 400)].concat(),
1450 );
1451
1452 assert_eq!(t.pointer(), start);
1453 assert!(
1454 !out.iter()
1455 .any(|e| matches!(e, InputEvent::PointerMoved { .. })),
1456 "scrolling moved the pointer: {out:?}"
1457 );
1458 match out.as_slice() {
1459 [InputEvent::PointerScroll { delta_y, .. }] => {
1460 assert!(*delta_y < -90.0 && *delta_y > -110.0, "delta_y {delta_y}");
1461 }
1462 other => panic!("expected one scroll, got {other:?}"),
1463 }
1464 }
1465
1466 #[test]
1467 fn a_second_finger_arriving_does_not_jump_the_pointer() {
1468 let mut t = touchpad();
1469 at(&mut t, 0, &touch(0, 1, 100, 100));
1470 let before = t.pointer();
1471 let out = at(&mut t, 10, &touch(1, 2, 1500, 900));
1472 assert!(out.is_empty(), "a finger arriving moved something: {out:?}");
1473 assert_eq!(t.pointer(), before);
1474 }
1475
1476 #[test]
1477 fn a_quick_still_tap_is_a_left_click_where_the_pointer_is() {
1478 let mut t = touchpad();
1479 let pointer = t.pointer();
1480 at(&mut t, 0, &touch(0, 1, 800, 500));
1481 let out = at(&mut t, 90, &lift(0));
1482 assert_eq!(
1483 out,
1484 vec![
1485 InputEvent::PointerButton {
1486 button: PointerButton::Left,
1487 state: ElementState::Down,
1488 position: pointer,
1489 modifiers: Modifiers::NONE,
1490 },
1491 InputEvent::PointerButton {
1492 button: PointerButton::Left,
1493 state: ElementState::Up,
1494 position: pointer,
1495 modifiers: Modifiers::NONE,
1496 },
1497 ]
1498 );
1499 }
1500
1501 #[test]
1502 fn a_two_finger_tap_is_a_right_click() {
1503 let mut t = touchpad();
1504 at(&mut t, 0, &touch(0, 1, 600, 500));
1505 at(&mut t, 15, &touch(1, 2, 900, 500));
1506 at(&mut t, 80, &lift(1));
1507 let out = at(&mut t, 95, &lift(0));
1508 assert_eq!(
1509 buttons(&out),
1510 vec![
1511 (PointerButton::Right, ElementState::Down),
1512 (PointerButton::Right, ElementState::Up)
1513 ]
1514 );
1515 }
1516
1517 #[test]
1518 fn a_touch_that_lasts_is_not_a_tap() {
1519 let mut t = touchpad();
1520 at(&mut t, 0, &touch(0, 1, 800, 500));
1521 let out = at(&mut t, 600, &lift(0));
1522 assert!(
1523 buttons(&out).is_empty(),
1524 "a resting finger clicked: {out:?}"
1525 );
1526 }
1527
1528 #[test]
1529 fn a_touch_that_travels_is_not_a_tap() {
1530 let mut t = touchpad();
1531 at(&mut t, 0, &touch(0, 1, 100, 500));
1532 at(&mut t, 40, &slide(0, 700, 500));
1533 let out = at(&mut t, 80, &lift(0));
1534 assert!(buttons(&out).is_empty(), "pointing clicked: {out:?}");
1535 }
1536
1537 #[test]
1538 fn taps_need_to_know_when_events_happened() {
1539 let mut t = touchpad();
1542 drain(
1543 &mut t,
1544 &[touch(0, 1, 800, 500).as_slice(), &[RawEvent::SYN]].concat(),
1545 );
1546 let out = drain(&mut t, &[lift(0).as_slice(), &[RawEvent::SYN]].concat());
1547 assert!(buttons(&out).is_empty(), "{out:?}");
1548 }
1549
1550 #[test]
1551 fn the_pads_button_with_two_fingers_down_is_a_right_click_released_as_one() {
1552 let mut t = touchpad();
1553 at(
1554 &mut t,
1555 0,
1556 &[touch(0, 1, 600, 800), touch(1, 2, 900, 800)].concat(),
1557 );
1558 let press = at(
1559 &mut t,
1560 300,
1561 &[RawEvent::new(ev::KEY, btn::LEFT, key_value::DOWN)],
1562 );
1563 assert_eq!(
1564 buttons(&press),
1565 vec![(PointerButton::Right, ElementState::Down)]
1566 );
1567
1568 at(&mut t, 320, &lift(1));
1570 let release = at(
1571 &mut t,
1572 340,
1573 &[RawEvent::new(ev::KEY, btn::LEFT, key_value::UP)],
1574 );
1575 assert_eq!(
1576 buttons(&release),
1577 vec![(PointerButton::Right, ElementState::Up)]
1578 );
1579
1580 let lifted = at(&mut t, 360, &lift(0));
1582 assert!(buttons(&lifted).is_empty(), "{lifted:?}");
1583 }
1584
1585 #[test]
1586 fn the_pads_button_with_one_finger_is_a_left_click() {
1587 let mut t = touchpad();
1588 at(&mut t, 0, &touch(0, 1, 600, 800));
1589 let press = at(
1590 &mut t,
1591 300,
1592 &[RawEvent::new(ev::KEY, btn::LEFT, key_value::DOWN)],
1593 );
1594 assert_eq!(
1595 buttons(&press),
1596 vec![(PointerButton::Left, ElementState::Down)]
1597 );
1598 }
1599
1600 #[test]
1601 fn a_touchpad_without_slots_counts_its_fingers_from_its_tools() {
1602 let mut t = translator();
1603 t.set_abs_range(abs::X, AbsAxis::new(0, 1599));
1604 t.set_abs_range(abs::Y, AbsAxis::new(0, 999));
1605 t.set_touchpad(true);
1606 let start = t.pointer();
1607
1608 let down = at(
1609 &mut t,
1610 0,
1611 &[
1612 RawEvent::new(ev::KEY, btn::TOUCH, key_value::DOWN),
1613 RawEvent::new(ev::KEY, btn::TOOL_FINGER, key_value::DOWN),
1614 RawEvent::new(ev::ABS, abs::X, 400),
1615 RawEvent::new(ev::ABS, abs::Y, 400),
1616 ],
1617 );
1618 assert!(down.is_empty(), "{down:?}");
1619 let moved = at(&mut t, 10, &[RawEvent::new(ev::ABS, abs::X, 600)]);
1620 assert_eq!(
1621 moved,
1622 vec![InputEvent::PointerMoved {
1623 position: Point::new(start.x + 100, start.y)
1624 }]
1625 );
1626
1627 at(
1629 &mut t,
1630 20,
1631 &[
1632 RawEvent::new(ev::KEY, btn::TOOL_FINGER, key_value::UP),
1633 RawEvent::new(ev::KEY, btn::TOOL_DOUBLETAP, key_value::DOWN),
1634 ],
1635 );
1636 let scrolled = at(&mut t, 30, &[RawEvent::new(ev::ABS, abs::Y, 600)]);
1637 assert!(
1638 matches!(scrolled.as_slice(), [InputEvent::PointerScroll { delta_y, .. }] if *delta_y > 0.0),
1639 "{scrolled:?}"
1640 );
1641 }
1642
1643 #[test]
1644 fn finger_tools_are_not_keys() {
1645 let mut t = touchscreen();
1648 let out = drain(
1649 &mut t,
1650 &[
1651 RawEvent::new(ev::KEY, btn::TOOL_FINGER, key_value::DOWN),
1652 RawEvent::SYN,
1653 ],
1654 );
1655 assert!(out.is_empty(), "{out:?}");
1656 }
1657
1658 #[test]
1659 fn a_touchscreen_still_touches() {
1660 let mut t = touchscreen();
1662 assert!(!t.is_touchpad());
1663 let out = drain(
1664 &mut t,
1665 &[touch(0, 1, 0, 0).as_slice(), &[RawEvent::SYN]].concat(),
1666 );
1667 assert!(
1668 matches!(out.as_slice(), [InputEvent::TouchDown { id: 0, .. }]),
1669 "{out:?}"
1670 );
1671 }
1672}