Skip to main content

starnix_modules_input_event_conversion/
touch_linux_to_fuchsia.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fidl_fuchsia_input_report as fir;
6use starnix_logging::log_warn;
7use starnix_types::time::time_from_timeval;
8use starnix_uapi::errors::Errno;
9use starnix_uapi::{error, uapi};
10use std::collections::{HashMap, HashSet};
11
12type SlotId = usize;
13type TrackingId = u32;
14
15/// TRACKING_ID changed to -1 means the contact is lifted.
16const LIFTED_TRACKING_ID: i32 = -1;
17
18/// For unify conversion for ABS_MT_POSITION_X, ABS_MT_POSITION_Y.
19enum MtPosition {
20    X(i64),
21    Y(i64),
22}
23
24/// A state machine accepts uapi::input_event, produces fir::InputReport
25/// when (Touch Event.. + Sync Event) received.
26///
27/// This parser currently only supports "Type B" protocol in:
28/// https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
29///
30/// Each event report contains a sequence of packets (uapi::input_event).
31/// EV_EYN means the report is completed.
32///
33/// There may be multiple contacts inside the sequence of packets, each contact
34/// data started by a MT_SLOT event with slot_id.
35///
36/// In the initiated contact, slot (X) will include a ABS_MT_TRACKING_ID
37/// event.
38/// In following events in slot (X) will continue use the same TRACKING_ID.
39/// Slot (X) with TRACKING_ID (-1) means the contact is lifted.
40///
41/// Warning output, clean state and return errno when received events:
42/// - unknown events type / code.
43/// - "Type A" events: SYN_MT_REPORT.
44/// - invalid event.
45/// - not follow "Type B" pattern.
46#[derive(Debug, Default, PartialEq)]
47pub struct LinuxTouchEventParser {
48    /// Store slot id -> tracking id mapping for Type B protocol. Remove the
49    /// mapping when contact lifted.
50    slot_id_to_tracking_id: HashMap<SlotId, TrackingId>,
51    /// Store slot id -> Contact.
52    slot_id_to_contact: HashMap<SlotId, fir::ContactInputReport>,
53
54    /// Store received events while conversion still ongoing.
55    cached_events: Vec<uapi::input_event>,
56
57    // Following states only when start parsing one event sequence (SYN_REPORT
58    // received).
59    /// There will be multiple slots in one event sequence, this field records
60    /// the current parsing slot's id.
61    current_slot_id: Option<SlotId>,
62    /// This record processed slots' id to check if duplicated slot id appear
63    /// in one event sequence.
64    processed_slots: HashSet<SlotId>,
65
66    /// Allowing single pointer sequence without leading MT_SLOT, will set the
67    /// pointer to slot 0.
68    single_pointer_sequence: bool,
69}
70
71impl LinuxTouchEventParser {
72    /// Create the LinuxTouchEventParser.
73    pub fn create() -> Self {
74        Self {
75            slot_id_to_tracking_id: HashMap::new(),
76            slot_id_to_contact: HashMap::new(),
77            cached_events: vec![],
78            current_slot_id: None,
79            processed_slots: HashSet::new(),
80            single_pointer_sequence: false,
81        }
82    }
83
84    /// Clean states stored in the parser, call when parser got any error.
85    fn reset_state(&mut self) {
86        self.cached_events = vec![];
87        self.slot_id_to_tracking_id = HashMap::new();
88        self.slot_id_to_contact = HashMap::new();
89
90        self.reset_sequence_state();
91    }
92
93    /// Clean state for parsing sequence, call for parsing sequence begin and end.
94    fn reset_sequence_state(&mut self) {
95        self.current_slot_id = None;
96        self.processed_slots = HashSet::new();
97        self.single_pointer_sequence = false;
98    }
99
100    /// There are 2 possible state for a MT_SLOT event:
101    /// - This is the first slot so no previous slot.
102    /// - This is the end of previous slot.
103    ///   * Return errno if duplicated slot found.
104    ///   * Add the current contact to the list if the current contact is
105    ///     valid, otherwise return errno.
106    ///
107    /// Add the slot id to processed_slots, current_slot_id and reset
108    /// current_contact.
109    fn mt_slot(&mut self, new_slot_id: SlotId) -> Result<(), Errno> {
110        if self.single_pointer_sequence {
111            log_warn!("sequence contains events in slot and out of slot");
112            self.reset_state();
113            return error!(EINVAL);
114        }
115
116        if self.processed_slots.contains(&new_slot_id) {
117            log_warn!("duplicated slot_id in one sequence, slot_id = {}", new_slot_id);
118            self.reset_state();
119            return error!(EINVAL);
120        }
121
122        self.processed_slots.insert(new_slot_id);
123        self.current_slot_id = Some(new_slot_id);
124
125        if !self.slot_id_to_contact.contains_key(&new_slot_id) {
126            self.slot_id_to_contact.insert(
127                new_slot_id.clone(),
128                fir::ContactInputReport {
129                    contact_id: Some(new_slot_id as u32),
130                    ..fir::ContactInputReport::default()
131                },
132            );
133        }
134
135        Ok(())
136    }
137
138    /// Type B requires ABS events leading by a MT_SLOT.
139    /// Returns SlotId if the requirement meet,
140    /// else fallback to single_pointer_sequence.
141    fn get_current_slot_id_or_err(&mut self, curr_event: &str) -> Result<SlotId, Errno> {
142        match self.current_slot_id {
143            Some(slot_id) => Ok(slot_id),
144            None => {
145                log_warn!(
146                    "{:?} is not following ABS_MT_SLOT, fallback to single_pointer_sequence",
147                    curr_event
148                );
149                let res = self.mt_slot(0);
150                match res {
151                    Ok(_) => {
152                        self.single_pointer_sequence = true;
153                        Ok(0)
154                    }
155                    Err(e) => Err(e),
156                }
157            }
158        }
159    }
160
161    /// MT_TRACKING_ID associate tracking id with slot id, this event is must
162    /// have for a slot first appear in event sequences. Add slot id ->
163    /// tracking id mapping, this only used to verify the tracking id is not
164    /// changed.
165    ///
166    /// Tracking id = -1 means the contact is lifted, the slot id -> tracking
167    /// id mapping should also be removed after this.
168    ///
169    /// Tracking id should not change otherwise, return errno.
170    ///
171    /// Returns errno if no leading MT_SLOT.
172    fn mt_tracking_id(&mut self, tracking_id: i32) -> Result<(), Errno> {
173        let slot_id = self.get_current_slot_id_or_err("ABS_MT_TRACKING_ID")?;
174
175        if tracking_id < LIFTED_TRACKING_ID {
176            // TRACKING_ID < -1, invalid value.
177            log_warn!("invalid TRACKING_ID {}", tracking_id);
178            self.reset_state();
179            return error!(EINVAL);
180        }
181
182        if tracking_id == LIFTED_TRACKING_ID {
183            self.slot_id_to_tracking_id.remove(&slot_id);
184            self.slot_id_to_contact.remove(&slot_id);
185
186            return Ok(());
187        }
188
189        // A valid TRACKING_ID. Check if it is changed.
190        let tid = tracking_id as TrackingId;
191        match self.slot_id_to_tracking_id.get(&slot_id) {
192            Some(id) => {
193                if tid != *id {
194                    log_warn!(
195                        "TRACKING_ID changed form {} to {} for unknown reason for slot {}",
196                        *id,
197                        tid,
198                        slot_id
199                    );
200                    self.reset_state();
201                    return error!(EINVAL);
202                }
203            }
204            None => {
205                self.slot_id_to_tracking_id.insert(slot_id, tid);
206            }
207        }
208
209        Ok(())
210    }
211
212    /// Set contact position. Returns errno if:
213    /// - no leading MT_SLOT.
214    /// - contact is lifted.
215    fn mt_position_x_y(&mut self, mt_position: MtPosition) -> Result<(), Errno> {
216        let ty = match mt_position {
217            MtPosition::X(_) => "ABS_MT_POSITION_X",
218            MtPosition::Y(_) => "ABS_MT_POSITION_Y",
219        };
220        let slot_id = self.get_current_slot_id_or_err(ty)?;
221
222        match self.slot_id_to_contact.get_mut(&slot_id) {
223            Some(contact) => {
224                match mt_position {
225                    MtPosition::X(x) => {
226                        contact.position_x = Some(x);
227                    }
228                    MtPosition::Y(y) => {
229                        contact.position_y = Some(y);
230                    }
231                }
232                Ok(())
233            }
234            None => {
235                log_warn!("current_contact is None when set position");
236                self.reset_state();
237                return error!(EINVAL);
238            }
239        }
240    }
241
242    fn produce_input_report(
243        &mut self,
244        event_time: zx::MonotonicInstant,
245    ) -> Result<Option<fir::InputReport>, Errno> {
246        self.reset_sequence_state();
247
248        let cached_events = std::mem::take(&mut self.cached_events);
249
250        for e in cached_events {
251            match e.code as u32 {
252                uapi::ABS_MT_SLOT => {
253                    let slot_id = e.value as SlotId;
254                    self.mt_slot(slot_id)?;
255                }
256                uapi::ABS_MT_TRACKING_ID => {
257                    self.mt_tracking_id(e.value)?;
258                }
259                uapi::ABS_MT_POSITION_X => {
260                    self.mt_position_x_y(MtPosition::X(e.value as i64))?;
261                }
262                uapi::ABS_MT_POSITION_Y => {
263                    self.mt_position_x_y(MtPosition::Y(e.value as i64))?;
264                }
265                _ => {
266                    // handle() ensure only 4 event_code above will be stored in cached_events.
267                    unreachable!();
268                }
269            }
270        }
271
272        let mut contacts: Vec<fir::ContactInputReport> = vec![];
273        for contact in self.slot_id_to_contact.values() {
274            if validate_contact_input_report(contact) {
275                contacts.push(contact.clone());
276            } else {
277                log_warn!(
278                    "current contact does not have required information, current_contact = {:?}",
279                    contact
280                );
281                self.reset_state();
282                return error!(EINVAL);
283            }
284        }
285
286        // This `unwrap` is safe because `validate_contact_input_report()` ensured `contact_id` exists.
287        contacts.sort_by(|a, b| a.contact_id.unwrap().cmp(&b.contact_id.unwrap()));
288
289        let res = Ok(Some(fir::InputReport {
290            event_time: Some(event_time.into_nanos()),
291            touch: Some(fir::TouchInputReport { contacts: Some(contacts), ..Default::default() }),
292            ..Default::default()
293        }));
294
295        self.reset_sequence_state();
296
297        res
298    }
299
300    /// Handle received input_event, only produce event when SYN_REPORT is received.
301    pub fn handle(&mut self, e: uapi::input_event) -> Result<Option<fir::InputReport>, Errno> {
302        let event_code = e.code as u32;
303        match e.type_ as u32 {
304            uapi::EV_SYN => match event_code {
305                uapi::SYN_REPORT => self.produce_input_report(time_from_timeval(e.time)?),
306                uapi::SYN_MT_REPORT => {
307                    log_warn!("Touchscreen got 'Type A' event SYN_MT_REPORT");
308                    self.reset_state();
309                    error!(EINVAL)
310                }
311                _ => {
312                    log_warn!("Touchscreen got unexpected EV_SYN, event = {:?}", e);
313                    self.reset_state();
314                    error!(EINVAL)
315                }
316            },
317            uapi::EV_ABS => match event_code {
318                uapi::ABS_MT_SLOT
319                | uapi::ABS_MT_TRACKING_ID
320                | uapi::ABS_MT_POSITION_X
321                | uapi::ABS_MT_POSITION_Y => {
322                    self.cached_events.push(e);
323                    Ok(None)
324                }
325                uapi::ABS_MT_TOUCH_MAJOR
326                | uapi::ABS_MT_TOUCH_MINOR
327                | uapi::ABS_MT_WIDTH_MAJOR
328                | uapi::ABS_MT_WIDTH_MINOR
329                | uapi::ABS_MT_ORIENTATION
330                | uapi::ABS_MT_TOOL_TYPE
331                | uapi::ABS_MT_BLOB_ID
332                | uapi::ABS_MT_PRESSURE
333                | uapi::ABS_MT_DISTANCE
334                | uapi::ABS_MT_TOOL_X
335                | uapi::ABS_MT_TOOL_Y => {
336                    // We don't use these event. Just respsond Ok.
337                    Ok(None)
338                }
339                _ => {
340                    log_warn!("Touchscreen got unexpected EV_ABS, event = {:?}", e);
341                    self.reset_state();
342                    error!(EINVAL)
343                }
344            },
345            uapi::EV_KEY => {
346                match event_code {
347                    // For "Type B" protocol, BTN_TOUCH can be ignored.
348                    uapi::BTN_TOUCH => Ok(None),
349                    _ => {
350                        log_warn!("Touchscreen got unexpected EV_KEY, event = {:?}", e);
351                        self.reset_state();
352                        error!(EINVAL)
353                    }
354                }
355            }
356            _ => {
357                log_warn!("Touchscreen got unexpected event type, got = {:?}", e);
358                self.reset_state();
359                error!(EINVAL)
360            }
361        }
362    }
363}
364
365/// ContactInputReport should contains X, Y, Contact ID
366fn validate_contact_input_report(c: &fir::ContactInputReport) -> bool {
367    match c {
368        &fir::ContactInputReport {
369            contact_id: Some(_),
370            position_x: Some(_),
371            position_y: Some(_),
372            ..
373        } => true,
374        _ => false,
375    }
376}
377
378#[cfg(test)]
379mod touchscreen_linux_fuchsia_tests {
380    use super::*;
381    use pretty_assertions::assert_eq;
382    use test_case::test_case;
383    use uapi::timeval;
384
385    fn input_event(ty: u32, code: u32, value: i32) -> uapi::input_event {
386        uapi::input_event { time: timeval::default(), type_: ty as u16, code: code as u16, value }
387    }
388
389    #[test]
390    fn handle_btn_touch_ok_does_not_produce_input_report() {
391        let e = input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 1);
392        let mut parser = LinuxTouchEventParser::create();
393        assert_eq!(parser.handle(e), Ok(None));
394        assert_eq!(parser, LinuxTouchEventParser::default());
395    }
396
397    #[test_case(input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1); "ABS_MT_SLOT")]
398    #[test_case(input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1); "ABS_MT_TRACKING_ID")]
399    #[test_case(input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 1); "ABS_MT_POSITION_X")]
400    #[test_case(input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 1); "ABS_MT_POSITION_Y")]
401    fn handle_input_event_ok_does_not_produce_input_report(e: uapi::input_event) {
402        let mut parser = LinuxTouchEventParser::create();
403        assert_eq!(parser.handle(e), Ok(None));
404        assert_eq!(
405            parser,
406            LinuxTouchEventParser {
407                cached_events: vec![e],
408                slot_id_to_tracking_id: HashMap::new(),
409                ..LinuxTouchEventParser::default()
410            }
411        );
412    }
413
414    #[test_case(input_event(uapi::EV_KEY, uapi::KEY_A, 1); "unsupported keycode")]
415    #[test_case(input_event(uapi::EV_ABS, uapi::ABS_PRESSURE, 1); "unsupported ABS event")]
416    #[test_case(input_event(uapi::EV_SYN, uapi::SYN_MT_REPORT, 1); "Type A")]
417    #[test_case(input_event(uapi::EV_SYN, uapi::SYN_CONFIG, 1); "unsupported SYN event")]
418    fn handle_input_event_error(e: uapi::input_event) {
419        let mut parser = LinuxTouchEventParser::create();
420        assert_eq!(parser.handle(e), error!(EINVAL));
421        assert_eq!(parser, LinuxTouchEventParser::default());
422    }
423
424    #[test_case(input_event(uapi::EV_ABS, uapi::ABS_MT_TOUCH_MAJOR, 1); "ignore ABS_MT_TOUCH_MAJOR event")]
425    #[test_case(input_event(uapi::EV_ABS, uapi::ABS_MT_TOUCH_MINOR, 1); "ignore ABS_MT_TOUCH_MINOR event")]
426    #[test_case(input_event(uapi::EV_ABS, uapi::ABS_MT_WIDTH_MAJOR, 1); "ignore ABS_MT_WIDTH_MAJOR event")]
427    #[test_case(input_event(uapi::EV_ABS, uapi::ABS_MT_WIDTH_MINOR, 1); "ignore ABS_MT_WIDTH_MINOR event")]
428    #[test_case(input_event(uapi::EV_ABS, uapi::ABS_MT_ORIENTATION, 1); "ignore ABS_MT_ORIENTATION event")]
429    #[test_case(input_event(uapi::EV_ABS, uapi::ABS_MT_TOOL_TYPE, 1); "ignore ABS_MT_TOOL_TYPE event")]
430    #[test_case(input_event(uapi::EV_ABS, uapi::ABS_MT_BLOB_ID, 1); "ignore ABS_MT_BLOB_ID event")]
431    #[test_case(input_event(uapi::EV_ABS, uapi::ABS_MT_PRESSURE, 1); "ignore ABS_MT_PRESSURE event")]
432    #[test_case(input_event(uapi::EV_ABS, uapi::ABS_MT_DISTANCE, 1); "ignore ABS_MT_DISTANCE event")]
433    #[test_case(input_event(uapi::EV_ABS, uapi::ABS_MT_TOOL_X, 1); "ignore ABS_MT_TOOL_X event")]
434    #[test_case(input_event(uapi::EV_ABS, uapi::ABS_MT_TOOL_Y , 1); "ignore ABS_MT_TOOL_Y event")]
435    fn handle_input_event_ignore(e: uapi::input_event) {
436        let mut parser = LinuxTouchEventParser::create();
437        assert_eq!(parser.handle(e), Ok(None));
438        assert_eq!(parser, LinuxTouchEventParser::default());
439    }
440
441    #[test]
442    fn no_slot_leading_event_fallback_to_single_pointer_mode() {
443        let syn = input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0);
444
445        let mut parser = LinuxTouchEventParser::create();
446        assert_eq!(parser.handle(input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1)), Ok(None));
447        assert_eq!(parser.handle(input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 2)), Ok(None));
448        assert_eq!(parser.handle(input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 3)), Ok(None));
449        assert_eq!(
450            parser.handle(syn),
451            Ok(Some(fir::InputReport {
452                event_time: Some(0),
453                touch: Some(fir::TouchInputReport {
454                    contacts: Some(vec![fir::ContactInputReport {
455                        contact_id: Some(0),
456                        position_x: Some(2),
457                        position_y: Some(3),
458                        ..fir::ContactInputReport::default()
459                    },]),
460                    ..fir::TouchInputReport::default()
461                }),
462                ..fir::InputReport::default()
463            }))
464        );
465        assert_eq!(
466            parser,
467            LinuxTouchEventParser {
468                cached_events: vec![],
469                slot_id_to_tracking_id: HashMap::from([(0, 1)]),
470                slot_id_to_contact: HashMap::from([(
471                    0,
472                    fir::ContactInputReport {
473                        contact_id: Some(0),
474                        position_x: Some(2),
475                        position_y: Some(3),
476                        ..fir::ContactInputReport::default()
477                    }
478                )]),
479                ..LinuxTouchEventParser::default()
480            }
481        );
482    }
483
484    #[test]
485    fn single_pointer_mode_slot_does_not_have_enough_information() {
486        let syn = input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0);
487
488        let mut parser = LinuxTouchEventParser::create();
489        assert_eq!(parser.handle(input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1)), Ok(None));
490        assert_eq!(parser.handle(input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 2)), Ok(None));
491        assert_eq!(parser.handle(input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 3)), Ok(None));
492        assert_eq!(
493            parser.handle(syn),
494            Ok(Some(fir::InputReport {
495                event_time: Some(0),
496                touch: Some(fir::TouchInputReport {
497                    contacts: Some(vec![fir::ContactInputReport {
498                        contact_id: Some(0),
499                        position_x: Some(2),
500                        position_y: Some(3),
501                        ..fir::ContactInputReport::default()
502                    },]),
503                    ..fir::TouchInputReport::default()
504                }),
505                ..fir::InputReport::default()
506            }))
507        );
508        assert_eq!(
509            parser,
510            LinuxTouchEventParser {
511                cached_events: vec![],
512                slot_id_to_tracking_id: HashMap::from([(0, 1)]),
513                slot_id_to_contact: HashMap::from([(
514                    0,
515                    fir::ContactInputReport {
516                        contact_id: Some(0),
517                        position_x: Some(2),
518                        position_y: Some(3),
519                        ..fir::ContactInputReport::default()
520                    }
521                )]),
522                ..LinuxTouchEventParser::default()
523            }
524        );
525
526        // the second event does not have x.
527        assert_eq!(parser.handle(input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 4)), Ok(None));
528        assert_eq!(
529            parser.handle(syn),
530            Ok(Some(fir::InputReport {
531                event_time: Some(0),
532                touch: Some(fir::TouchInputReport {
533                    contacts: Some(vec![fir::ContactInputReport {
534                        contact_id: Some(0),
535                        position_x: Some(2),
536                        position_y: Some(4),
537                        ..fir::ContactInputReport::default()
538                    },]),
539                    ..fir::TouchInputReport::default()
540                }),
541                ..fir::InputReport::default()
542            }))
543        );
544        assert_eq!(
545            parser,
546            LinuxTouchEventParser {
547                cached_events: vec![],
548                slot_id_to_tracking_id: HashMap::from([(0, 1)]),
549                slot_id_to_contact: HashMap::from([(
550                    0,
551                    fir::ContactInputReport {
552                        contact_id: Some(0),
553                        position_x: Some(2),
554                        position_y: Some(4),
555                        ..fir::ContactInputReport::default()
556                    }
557                )]),
558                ..LinuxTouchEventParser::default()
559            }
560        );
561
562        // only contains pressure event.
563        assert_eq!(parser.handle(input_event(uapi::EV_ABS, uapi::ABS_MT_PRESSURE, 10)), Ok(None));
564        assert_eq!(
565            parser.handle(syn),
566            Ok(Some(fir::InputReport {
567                event_time: Some(0),
568                touch: Some(fir::TouchInputReport {
569                    contacts: Some(vec![fir::ContactInputReport {
570                        contact_id: Some(0),
571                        position_x: Some(2),
572                        position_y: Some(4),
573                        ..fir::ContactInputReport::default()
574                    },]),
575                    ..fir::TouchInputReport::default()
576                }),
577                ..fir::InputReport::default()
578            }))
579        );
580        assert_eq!(
581            parser,
582            LinuxTouchEventParser {
583                cached_events: vec![],
584                slot_id_to_tracking_id: HashMap::from([(0, 1)]),
585                slot_id_to_contact: HashMap::from([(
586                    0,
587                    fir::ContactInputReport {
588                        contact_id: Some(0),
589                        position_x: Some(2),
590                        position_y: Some(4),
591                        ..fir::ContactInputReport::default()
592                    }
593                )]),
594                ..LinuxTouchEventParser::default()
595            }
596        );
597    }
598
599    #[test]
600    fn slot_has_only_pressure_event() {
601        let slot_0 = input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0);
602        let syn = input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0);
603
604        let mut parser = LinuxTouchEventParser::create();
605        assert_eq!(parser.handle(slot_0), Ok(None));
606        assert_eq!(parser.handle(input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1)), Ok(None));
607        assert_eq!(parser.handle(input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 2)), Ok(None));
608        assert_eq!(parser.handle(input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 3)), Ok(None));
609        assert_eq!(
610            parser.handle(syn),
611            Ok(Some(fir::InputReport {
612                event_time: Some(0),
613                touch: Some(fir::TouchInputReport {
614                    contacts: Some(vec![fir::ContactInputReport {
615                        contact_id: Some(0),
616                        position_x: Some(2),
617                        position_y: Some(3),
618                        ..fir::ContactInputReport::default()
619                    },]),
620                    ..fir::TouchInputReport::default()
621                }),
622                ..fir::InputReport::default()
623            }))
624        );
625        assert_eq!(
626            parser,
627            LinuxTouchEventParser {
628                cached_events: vec![],
629                slot_id_to_tracking_id: HashMap::from([(0, 1)]),
630                slot_id_to_contact: HashMap::from([(
631                    0,
632                    fir::ContactInputReport {
633                        contact_id: Some(0),
634                        position_x: Some(2),
635                        position_y: Some(3),
636                        ..fir::ContactInputReport::default()
637                    }
638                )]),
639                ..LinuxTouchEventParser::default()
640            }
641        );
642
643        // next event only contains pressure
644        assert_eq!(parser.handle(slot_0), Ok(None));
645        assert_eq!(parser.handle(input_event(uapi::EV_ABS, uapi::ABS_MT_PRESSURE, 10)), Ok(None));
646        assert_eq!(
647            parser.handle(syn),
648            Ok(Some(fir::InputReport {
649                event_time: Some(0),
650                touch: Some(fir::TouchInputReport {
651                    contacts: Some(vec![fir::ContactInputReport {
652                        contact_id: Some(0),
653                        position_x: Some(2),
654                        position_y: Some(3),
655                        ..fir::ContactInputReport::default()
656                    },]),
657                    ..fir::TouchInputReport::default()
658                }),
659                ..fir::InputReport::default()
660            }))
661        );
662        assert_eq!(
663            parser,
664            LinuxTouchEventParser {
665                cached_events: vec![],
666                slot_id_to_tracking_id: HashMap::from([(0, 1)]),
667                slot_id_to_contact: HashMap::from([(
668                    0,
669                    fir::ContactInputReport {
670                        contact_id: Some(0),
671                        position_x: Some(2),
672                        position_y: Some(3),
673                        ..fir::ContactInputReport::default()
674                    }
675                )]),
676                ..LinuxTouchEventParser::default()
677            }
678        );
679    }
680
681    #[test]
682    fn slot_does_not_have_enough_information() {
683        let slot_0 = input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0);
684        let syn = input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0);
685
686        let mut parser = LinuxTouchEventParser::create();
687
688        // The last slot does not have enough information.
689        assert_eq!(parser.handle(slot_0), Ok(None));
690        assert_eq!(parser.handle(syn), error!(EINVAL));
691        assert_eq!(parser, LinuxTouchEventParser::default());
692
693        // The first slot does not have enough information.
694        let slot_1 = input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1);
695        assert_eq!(parser.handle(slot_0), Ok(None));
696        assert_eq!(parser.handle(slot_1), Ok(None));
697        assert_eq!(parser.handle(syn), error!(EINVAL));
698        assert_eq!(parser, LinuxTouchEventParser::default());
699    }
700
701    #[test]
702    fn same_slot_id_in_one_event() {
703        let slot_0 = input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0);
704        let traking_id = input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 0);
705        let x = input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 0);
706        let y = input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 0);
707        let syn = input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0);
708
709        let mut parser = LinuxTouchEventParser::create();
710        assert_eq!(parser.handle(slot_0), Ok(None));
711        assert_eq!(parser.handle(traking_id), Ok(None));
712        assert_eq!(parser.handle(x), Ok(None));
713        assert_eq!(parser.handle(y), Ok(None));
714        assert_eq!(parser.handle(slot_0), Ok(None));
715        assert_eq!(parser.handle(syn), error!(EINVAL));
716        assert_eq!(parser, LinuxTouchEventParser::default());
717    }
718
719    #[test]
720    fn tracking_id_changed_in_slot() {
721        let slot_0 = input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0);
722        let traking_id_0 = input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 0);
723        let traking_id_1 = input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1);
724        let syn = input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0);
725
726        let mut parser = LinuxTouchEventParser::create();
727        assert_eq!(parser.handle(slot_0), Ok(None));
728        assert_eq!(parser.handle(traking_id_0), Ok(None));
729        assert_eq!(parser.handle(traking_id_1), Ok(None));
730        assert_eq!(parser.handle(syn), error!(EINVAL));
731        assert_eq!(parser, LinuxTouchEventParser::default());
732    }
733
734    #[test]
735    fn tracking_id_different_with_parser_recorded() {
736        let slot_0 = input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0);
737        let traking_id_1 = input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1);
738        let syn = input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0);
739
740        let mut parser = LinuxTouchEventParser::create();
741        parser.slot_id_to_tracking_id.insert(0, 0);
742        assert_eq!(parser.handle(slot_0), Ok(None));
743        assert_eq!(parser.handle(traking_id_1), Ok(None));
744        assert_eq!(parser.handle(syn), error!(EINVAL));
745        assert_eq!(parser, LinuxTouchEventParser::default());
746    }
747
748    #[test]
749    fn produce_input_report() {
750        // 1 contact.
751        let slot_0 = input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0);
752        let traking_id_0 = input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1);
753        let x_0 = input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 2);
754        let y_0 = input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 3);
755        let syn = input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0);
756
757        let mut parser = LinuxTouchEventParser::create();
758        assert_eq!(parser.handle(slot_0), Ok(None));
759        assert_eq!(parser.handle(traking_id_0), Ok(None));
760        assert_eq!(parser.handle(x_0), Ok(None));
761        assert_eq!(parser.handle(y_0), Ok(None));
762        assert_eq!(
763            parser.handle(syn),
764            Ok(Some(fir::InputReport {
765                event_time: Some(0),
766                touch: Some(fir::TouchInputReport {
767                    contacts: Some(vec![fir::ContactInputReport {
768                        contact_id: Some(0),
769                        position_x: Some(2),
770                        position_y: Some(3),
771                        ..fir::ContactInputReport::default()
772                    },]),
773                    ..fir::TouchInputReport::default()
774                }),
775                ..fir::InputReport::default()
776            }))
777        );
778        assert_eq!(
779            parser,
780            LinuxTouchEventParser {
781                cached_events: vec![],
782                slot_id_to_tracking_id: HashMap::from([(0, 1)]),
783                slot_id_to_contact: HashMap::from([(
784                    0,
785                    fir::ContactInputReport {
786                        contact_id: Some(0),
787                        position_x: Some(2),
788                        position_y: Some(3),
789                        ..fir::ContactInputReport::default()
790                    }
791                )]),
792                ..LinuxTouchEventParser::default()
793            }
794        );
795
796        // 2 contact.
797        let x_0 = input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 4);
798        let y_0 = input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 5);
799
800        let slot_1 = input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1);
801        let traking_id_1 = input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 2);
802        let x_1 = input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10);
803        let y_1 = input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 11);
804
805        assert_eq!(parser.handle(slot_0), Ok(None));
806        assert_eq!(parser.handle(x_0), Ok(None));
807        assert_eq!(parser.handle(y_0), Ok(None));
808        assert_eq!(parser.handle(slot_1), Ok(None));
809        assert_eq!(parser.handle(traking_id_1), Ok(None));
810        assert_eq!(parser.handle(x_1), Ok(None));
811        assert_eq!(parser.handle(y_1), Ok(None));
812        assert_eq!(
813            parser.handle(syn),
814            Ok(Some(fir::InputReport {
815                event_time: Some(0),
816                touch: Some(fir::TouchInputReport {
817                    contacts: Some(vec![
818                        fir::ContactInputReport {
819                            contact_id: Some(0),
820                            position_x: Some(4),
821                            position_y: Some(5),
822                            ..fir::ContactInputReport::default()
823                        },
824                        fir::ContactInputReport {
825                            contact_id: Some(1),
826                            position_x: Some(10),
827                            position_y: Some(11),
828                            ..fir::ContactInputReport::default()
829                        },
830                    ]),
831                    ..fir::TouchInputReport::default()
832                }),
833                ..fir::InputReport::default()
834            }))
835        );
836        assert_eq!(
837            parser,
838            LinuxTouchEventParser {
839                cached_events: vec![],
840                slot_id_to_tracking_id: HashMap::from([(0, 1), (1, 2)]),
841                slot_id_to_contact: HashMap::from([
842                    (
843                        0,
844                        fir::ContactInputReport {
845                            contact_id: Some(0),
846                            position_x: Some(4),
847                            position_y: Some(5),
848                            ..fir::ContactInputReport::default()
849                        }
850                    ),
851                    (
852                        1,
853                        fir::ContactInputReport {
854                            contact_id: Some(1),
855                            position_x: Some(10),
856                            position_y: Some(11),
857                            ..fir::ContactInputReport::default()
858                        }
859                    )
860                ]),
861                ..LinuxTouchEventParser::default()
862            }
863        );
864
865        // lift the first contact.
866        let tracking_id_lifted = input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1);
867
868        assert_eq!(parser.handle(slot_0), Ok(None));
869        assert_eq!(parser.handle(tracking_id_lifted), Ok(None));
870        assert_eq!(parser.handle(slot_1), Ok(None));
871        assert_eq!(parser.handle(x_1), Ok(None));
872        assert_eq!(parser.handle(y_1), Ok(None));
873        assert_eq!(
874            parser.handle(syn),
875            Ok(Some(fir::InputReport {
876                event_time: Some(0),
877                touch: Some(fir::TouchInputReport {
878                    contacts: Some(vec![fir::ContactInputReport {
879                        contact_id: Some(1),
880                        position_x: Some(10),
881                        position_y: Some(11),
882                        ..fir::ContactInputReport::default()
883                    },]),
884                    ..fir::TouchInputReport::default()
885                }),
886                ..fir::InputReport::default()
887            }))
888        );
889        // should remove the mapping.
890        assert_eq!(
891            parser,
892            LinuxTouchEventParser {
893                cached_events: vec![],
894                slot_id_to_tracking_id: HashMap::from([(1, 2)]),
895                slot_id_to_contact: HashMap::from([(
896                    1,
897                    fir::ContactInputReport {
898                        contact_id: Some(1),
899                        position_x: Some(10),
900                        position_y: Some(11),
901                        ..fir::ContactInputReport::default()
902                    }
903                )]),
904                ..LinuxTouchEventParser::default()
905            }
906        );
907
908        // lift all contact.
909        assert_eq!(parser.handle(slot_1), Ok(None));
910        assert_eq!(parser.handle(tracking_id_lifted), Ok(None));
911        assert_eq!(
912            parser.handle(syn),
913            Ok(Some(fir::InputReport {
914                event_time: Some(0),
915                touch: Some(fir::TouchInputReport {
916                    contacts: Some(vec![]),
917                    ..fir::TouchInputReport::default()
918                }),
919                ..fir::InputReport::default()
920            }))
921        );
922        // should remove the mapping.
923        assert_eq!(parser, LinuxTouchEventParser::default());
924    }
925}