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