Skip to main content

denise_evdev/
translate.rs

1//! Turning raw evdev events into [`InputEvent`]s.
2//!
3//! This is a state machine, and it is the part of input handling that is actually
4//! difficult. It is kept platform-independent so every case below is a unit test
5//! rather than something you discover by dragging a finger across a panel in a
6//! workshop.
7//!
8//! Three things it gets right that a naive loop does not:
9//!
10//! - **Frames.** evdev delivers a position as separate `REL_X` and `REL_Y` events
11//!   terminated by `SYN_REPORT`. Emitting on each axis produces two moves per
12//!   physical motion, doubling every drag and breaking every gesture threshold.
13//!   Nothing is emitted until the frame closes.
14//! - **Ordering.** A click arrives in the same frame as the motion that positioned
15//!   it. Motion is resolved first, so a button event carries where the pointer
16//!   actually was, not where it had been.
17//! - **Contact identity.** Multitouch slots are stateful: a slot keeps reporting
18//!   for the same finger until its tracking id goes to `-1`, and only the axes
19//!   that changed are resent.
20//!
21//! A touchpad reports the same slots as a touchscreen, and is not one: where the
22//! finger is on the pad says nothing about where on the screen it points. A
23//! translator told it is reading a touchpad ([`Translator::set_touchpad`]) moves
24//! the pointer by how far a finger travels, scrolls with two, and turns a short
25//! tap into a click. The rest of the protocol is read the same way.
26
27use 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
36/// Contacts tracked at once. Ten fingers is more than any panel this targets.
37pub const MAX_SLOTS: usize = 10;
38
39/// Pixels scrolled per wheel detent.
40const SCROLL_STEP: f32 = 40.0;
41
42/// How far a finger's travel across a touchpad moves the pointer: the pad's full
43/// width is this many widths of the surface. One pad across for one screen across
44/// reaches every point in a stroke without the pointer racing away from a finger
45/// that barely moved.
46const TOUCHPAD_SPEED: f32 = 1.0;
47
48/// The longest a touch can last and still be a tap.
49const TAP_TIME: Duration = Duration::from_millis(200);
50
51/// How far fingers may travel during a tap, as a share of the pad's width. A
52/// resting finger drifts; one that moves further than this was pointing.
53const TAP_TRAVEL: f32 = 0.03;
54
55/// One event as read from `/dev/input/eventN`.
56#[derive(Clone, Copy, Debug, PartialEq, Eq)]
57pub struct RawEvent {
58    /// `EV_*` event type.
59    pub kind: u16,
60    /// Type-specific code.
61    pub code: u16,
62    /// Type-specific value.
63    pub value: i32,
64}
65
66impl RawEvent {
67    /// Creates a raw event.
68    pub const fn new(kind: u16, code: u16, value: i32) -> Self {
69        Self { kind, code, value }
70    }
71
72    /// The frame separator.
73    pub const SYN: Self = Self::new(ev::SYN, syn::REPORT, 0);
74}
75
76/// The calibration of one absolute axis.
77#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78pub struct AbsAxis {
79    /// Lowest value the device reports.
80    pub min: i32,
81    /// Highest value the device reports.
82    pub max: i32,
83}
84
85impl AbsAxis {
86    /// Creates an axis range.
87    pub const fn new(min: i32, max: i32) -> Self {
88        Self { min, max }
89    }
90
91    /// Maps a device reading onto `0..extent` pixels.
92    ///
93    /// A touchscreen reports in its own units — commonly `0..4095`, which is not
94    /// the panel resolution and is not even the same aspect ratio.
95    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/// One multitouch slot.
109#[derive(Clone, Copy, Debug, Default)]
110struct Slot {
111    /// Contact identity from the kernel; `None` when the slot is free.
112    tracking_id: Option<i32>,
113    x: i32,
114    y: i32,
115    /// A contact began this frame.
116    began: bool,
117    /// A position axis changed this frame.
118    moved: bool,
119    /// The contact ended this frame.
120    ended: bool,
121}
122
123/// What a touchpad remembers from one frame to the next.
124#[derive(Clone, Copy, Debug, Default)]
125struct Touchpad {
126    /// Where each finger was when the last frame closed, in pad units, by slot.
127    /// A single-contact pad keeps its one finger in the first.
128    last: [Option<(i32, i32)>; MAX_SLOTS],
129    /// Fingers down when the last frame closed.
130    fingers: usize,
131    /// The part of a pixel travelled but not yet moved, so slow motion is not
132    /// lost to rounding.
133    carry_x: f32,
134    carry_y: f32,
135    /// When the fingers now down first touched, if this can still be a tap.
136    tap_start: Option<Duration>,
137    /// How far they have travelled since, in pad units.
138    tap_travel: f32,
139    /// The most fingers down at once since, which decides the tap's button.
140    tap_fingers: usize,
141    /// The pad's own button went down with two fingers on the pad, so its
142    /// release is a right button too.
143    right_click: bool,
144    /// Fingers down as the `BTN_TOOL_*` keys count them, for a pad without
145    /// slots, which reports two fingers as one position.
146    tool_fingers: usize,
147    /// The finger's position on a pad without slots, which resends only the axis
148    /// that changed.
149    single: (i32, i32),
150}
151
152/// A pointer or key event waiting for its frame to close.
153#[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/// Accumulates raw events and emits [`InputEvent`]s a frame at a time.
167#[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    /// The device has used the slot protocol, so `ABS_X`/`ABS_Y` are not a pointer.
185    multitouch: bool,
186    slots: [Slot; MAX_SLOTS],
187    slot: usize,
188
189    /// Turns key positions into characters. Held here rather than beside the
190    /// keymap because composition is a property of the *sequence* of keystrokes,
191    /// and this is the only thing that sees them in order.
192    composer: Composer,
193
194    /// `BTN_TOUCH` state, for single-contact panels with no slots.
195    touch_down: bool,
196    single_touch_active: bool,
197
198    deferred: Vec<Deferred>,
199
200    /// Contacts move the pointer rather than touching the surface.
201    touchpad: bool,
202    pad: Touchpad,
203    /// When the event being fed happened, where the caller knows.
204    now: Option<Duration>,
205}
206
207impl Translator {
208    /// Creates a translator for a surface of `size`.
209    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    /// Reads this device as a touchpad.
236    ///
237    /// A finger moves the pointer by how far it travels rather than to where it
238    /// is, two fingers scroll, the pad's button is a right button with two
239    /// fingers down, and a short tap is a click — a right click with two
240    /// fingers. Taps need to know when events happened, so they come only
241    /// through [`feed_at`](Self::feed_at).
242    pub fn set_touchpad(&mut self, touchpad: bool) {
243        self.touchpad = touchpad;
244        self.pad = Touchpad::default();
245    }
246
247    /// Whether this device is read as a touchpad.
248    pub fn is_touchpad(&self) -> bool {
249        self.touchpad
250    }
251
252    /// Reads this device with a different keyboard layout.
253    ///
254    /// Defaults to US, because [`KeyCode`] names US positions and defaulting to
255    /// anything else would make the two disagree by default.
256    pub fn set_layout(&mut self, layout: &'static Layout) {
257        self.composer.set_layout(layout);
258    }
259
260    /// The layout this device is being read with.
261    pub fn layout(&self) -> &'static Layout {
262        self.composer.layout()
263    }
264
265    /// Declares the range of an absolute axis, from the device's `absinfo`.
266    ///
267    /// Without this an absolute device is ignored: mapping a reading to a pixel is
268    /// impossible without knowing what the reading is out of.
269    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    /// Tells the translator the surface changed size.
278    pub fn resize(&mut self, size: Size) {
279        self.surface = size;
280        self.pointer = self.clamp(self.pointer);
281    }
282
283    /// Current pointer position.
284    pub fn pointer(&self) -> Point {
285        self.pointer
286    }
287
288    /// Moves the pointer without emitting anything.
289    ///
290    /// Used to share one cursor between several pointing devices: a mouse and a
291    /// tablet should move the same pointer rather than each keeping its own.
292    pub fn set_pointer(&mut self, position: Point) {
293        self.pointer = self.clamp(position);
294    }
295
296    /// Currently held modifiers.
297    pub fn modifiers(&self) -> Modifiers {
298        self.modifiers
299    }
300
301    /// The calibration read from the device, as `(x, y)`.
302    pub fn abs_ranges(&self) -> (Option<AbsAxis>, Option<AbsAxis>) {
303        (self.abs_x, self.abs_y)
304    }
305
306    /// Returns `true` once the device has identified itself as multitouch.
307    pub fn is_multitouch(&self) -> bool {
308        self.multitouch
309    }
310
311    /// Feeds one raw event, appending any completed events to `out`.
312    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                // The kernel's buffer overflowed and events were lost. Anything
317                // half-accumulated describes a state that never existed.
318                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    /// Feeds one raw event that happened at `at`, on any clock that only moves
329    /// forward — the kernel's timestamp, as a duration since the epoch.
330    ///
331    /// The same as [`feed`](Self::feed), except that a touchpad can tell a tap
332    /// from a touch that lasted.
333    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    /// Feeds a whole batch, which need not be frame-aligned.
339    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                // Auto-repeat is meaningless for a button.
402                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            // How many fingers are down, not keys: a pad without slots counts its
414            // fingers this way, and on anything else they are not worth a
415            // keypress nobody can bind.
416            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    /// Throws away everything accumulated but not yet emitted.
473    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    /// Closes a frame: motion first, then everything positioned by it.
489    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                    // The key event first, then whatever it typed. A binding on
522                    // Enter or Escape therefore runs before any text arrives, and
523                    // a field can insert every `Text` it sees without filtering.
524                    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        // A single-contact panel: BTN_TOUCH plus plain absolute axes, no slots.
571        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    /// A touchpad's frame: pointer motion or scrolling from how the fingers
584    /// travelled, and a tap when they lifted soon enough and had not moved.
585    fn flush_touchpad(&mut self, out: &mut Vec<InputEvent>) {
586        // Where each finger is now, in pad units.
587        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            // The legacy single-touch axes repeat the first finger; the slots
601            // already said it.
602            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        // How far the fingers that were already down travelled, on average. A
621        // finger arriving or leaving says nothing about motion, and averaging it
622        // in would jump the pointer by the distance between two fingers.
623        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            // Two fingers or more scroll the way a wheel does: moving them away
669            // from you is a detent away from you.
670            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        // Taps. A touch starts one; travel, a press of the pad's own button, or
678        // lasting too long ends it; lifting the last finger in time clicks.
679        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        // The pad's own button: a right button with two fingers down, released
698        // as the button it was pressed as.
699        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            // The slot index is the contact identity. A slot holds one finger at a
773            // time, which is exactly the lifetime TouchId promises, and unlike the
774            // kernel's tracking id it cannot wrap around and collide.
775            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        // The bug this prevents: one PointerMoved per axis, so every drag travels
852        // twice and every gesture threshold trips early.
853        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        // A QEMU tablet, and every touchscreen: device units, not pixels.
895        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        // Motion and button arrive together; resolving the button first would
931        // report where the pointer used to be.
932        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        // Deliberately away from the initial centre, so the move is a real one.
937        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        // One detent away from the user scrolls content up, so delta_y is negative.
971        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                // The held shift decides the character as well as the flag.
1000                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                // A held key keeps typing, which is the entire point of repeat.
1028                InputEvent::Text { ch: 'a' },
1029            ]
1030        );
1031    }
1032
1033    #[test]
1034    fn raw_evdev_codes_become_norwegian_text() {
1035        // End to end from the wire: evdev code 39 is the US semicolon position,
1036        // which on a Norwegian keyboard is where ø lives.
1037        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        // Composition survives the frame boundary, which is the thing a per-frame
1060        // translator could plausibly get wrong.
1061        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        // Only the axis that changed is resent, which is the protocol's whole point.
1117        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        // Slot 1 must still be live and still report as itself.
1208        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        // BTN_TOUCH plus plain ABS_X/ABS_Y: cheap resistive panels, and the
1262        // protocol-A fallback.
1263        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        // SYN_DROPPED means the kernel lost events. Whatever accumulated describes
1291        // a state that never existed, and emitting it desynchronises everything
1292        // downstream.
1293        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        // Absolute readings are positions, not deltas. Re-sending the same
1322        // reading must not move anything.
1323        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    // Touchpads. The pad is 1600 by 1000 units on an 800-pixel-wide surface, so
1348    // a unit of travel is half a pixel.
1349
1350    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    /// Feeds a frame that happened `ms` milliseconds in.
1363    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        // The bug this fixes: the pad read as a touchscreen, so a finger in its
1407        // corner touched the corner of the screen and the pointer never moved.
1408        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        // Ten units at half a pixel each.
1433        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        // Both fingers move 200 units away from the user: a wheel turned away.
1446        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        // Fed without timestamps there is no telling a tap from a finger left
1540        // resting, so none is guessed at.
1541        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        // One finger lifts before the button comes up: still a right button.
1569        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        // And lifting the last finger afterwards is not a tap on top of it.
1581        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        // Two fingers, reported as one position and a double-tap tool.
1628        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        // Touchscreens report BTN_TOOL_FINGER too; it used to come out as a key
1646        // press nobody could name.
1647        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        // The touchpad reading is asked for, never assumed.
1661        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}