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