Skip to main content

starnix_modules_input_event_conversion/
touch_fuchsia_to_linux.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_pointer::{
6    EventPhase as FidlEventPhase, TouchEvent as FidlTouchEvent, TouchPointerSample,
7};
8use starnix_logging::log_warn;
9use starnix_types::time::timeval_from_time;
10use starnix_uapi::uapi;
11use std::collections::{BTreeMap, HashMap, VecDeque};
12
13type SlotId = usize;
14type TrackingId = u32;
15type TimeNanos = i64;
16
17/// TRACKING_ID changed to -1 means the contact is lifted.
18const LIFTED_TRACKING_ID: i32 = -1;
19
20#[derive(Debug, thiserror::Error)]
21enum TouchEventConversionError {
22    #[error("Event does not include enough information")]
23    NotEnoughInformation,
24    #[error("no more available slot id")]
25    NoMoreAvailableSlotId,
26    #[error("receive pointer add already added")]
27    PointerAdded,
28    #[error("receive pointer change/remove before added")]
29    PointerNotFound,
30    #[error("Input pipeline does not send out Cancel")]
31    PointerCancel,
32}
33
34#[derive(Debug, Clone, PartialEq)]
35struct TouchEvent {
36    time_nanos: TimeNanos,
37    pointer_id: TrackingId,
38    phase: FidlEventPhase,
39    x: i32,
40    y: i32,
41}
42
43impl TryFrom<FidlTouchEvent> for TouchEvent {
44    type Error = TouchEventConversionError;
45    fn try_from(e: FidlTouchEvent) -> Result<TouchEvent, Self::Error> {
46        match e {
47            FidlTouchEvent {
48                timestamp: Some(time_nanos),
49                pointer_sample:
50                    Some(TouchPointerSample {
51                        position_in_viewport: Some([x, y]),
52                        phase: Some(phase),
53                        interaction: Some(id),
54                        ..
55                    }),
56                ..
57            } => Ok(TouchEvent {
58                time_nanos,
59                pointer_id: id.pointer_id,
60                phase,
61                x: x as i32,
62                y: y as i32,
63            }),
64            _ => Err(TouchEventConversionError::NotEnoughInformation),
65        }
66    }
67}
68
69/// FuchsiaTouchEventToLinuxTouchEventConverter handles fuchsia.ui.pointer.TouchEvents
70/// and converts them to Linux uapi::input_event in Multi Touch Protocol B.
71#[derive(Debug, Default, PartialEq)]
72pub struct FuchsiaTouchEventToLinuxTouchEventConverter {
73    pointer_id_to_slot_id: HashMap<TrackingId, SlotId>,
74    pointer_id_to_event: HashMap<TrackingId, TouchEvent>,
75}
76
77const MAX_TOUCH_CONTACT: usize = 10;
78
79pub struct LinuxTouchEventBatch {
80    // Linux Multi Touch Protocol B events
81    pub events: VecDeque<uapi::input_event>,
82    pub last_event_time_ns: i64,
83    pub count_converted_fidl_events: u64,
84    pub count_ignored_fidl_events: u64,
85    pub count_unexpected_fidl_events: u64,
86}
87
88impl LinuxTouchEventBatch {
89    pub fn new() -> Self {
90        Self {
91            events: VecDeque::new(),
92            last_event_time_ns: 0,
93            count_converted_fidl_events: 0,
94            count_ignored_fidl_events: 0,
95            count_unexpected_fidl_events: 0,
96        }
97    }
98}
99
100impl FuchsiaTouchEventToLinuxTouchEventConverter {
101    pub fn create() -> Self {
102        Self { pointer_id_to_slot_id: HashMap::new(), pointer_id_to_event: HashMap::new() }
103    }
104
105    /// In Protocol B, the driver should only advertise as many slots as the hardware can report
106    /// so this converter uses `available_slot_id` to find the first available slot id.
107    fn available_slot_id(&self) -> Option<SlotId> {
108        let mut used_slot_ids = bit_vec::BitVec::<u32>::from_elem(MAX_TOUCH_CONTACT, false);
109        for slot_id in self.pointer_id_to_slot_id.values() {
110            used_slot_ids.set(*slot_id, true);
111        }
112
113        used_slot_ids.iter().position(|used| !used)
114    }
115
116    /// Converts fidl touch events to a batch of Linux Multi Touch Protocol B events.
117    ///
118    /// One vector of fidl touch events may convert to multiple Linux Multi Touch Protocol B
119    /// sequences because:
120    /// - Same pointer happens multiple times in the vector of fidl touch events.
121    /// - Linux Multi Touch Protocol B does not allow slot with same id appear multiple times
122    ///   one sequence.
123    pub fn handle(&mut self, events: Vec<FidlTouchEvent>) -> LinuxTouchEventBatch {
124        let mut batch = LinuxTouchEventBatch::new();
125
126        // TODO(https://fxbug.dev/348726475): Group events by timestamp here because events from
127        // fuchsia.ui.pointer.touch.Watch may not sorted by timestamp.
128        let mut sequences: BTreeMap<TimeNanos, Vec<TouchEvent>> = BTreeMap::new();
129        for event in events.into_iter() {
130            match TouchEvent::try_from(event) {
131                Ok(e) => {
132                    sequences.entry(e.time_nanos).or_default().push(e);
133                }
134                Err(_) => {
135                    batch.count_ignored_fidl_events += 1;
136                }
137            }
138        }
139
140        if sequences.is_empty() {
141            return batch;
142        }
143
144        batch.last_event_time_ns = *sequences.last_key_value().unwrap().0;
145
146        for (time_nanos, seq) in sequences.iter() {
147            let count_events = seq.len() as u64;
148            match self.translate_sequence(*time_nanos, seq) {
149                Ok(mut res) => {
150                    batch.events.append(&mut res);
151                    batch.count_converted_fidl_events += count_events;
152                }
153                Err(e) => {
154                    batch.count_unexpected_fidl_events += count_events;
155                    self.reset_state();
156                    log_warn!("{}", e);
157                }
158            }
159        }
160
161        batch
162    }
163
164    /// Translates a vec of fidl FidlTouchEvent to Linux Multi Touch Protocol B sequence. Caller
165    /// ensures the given vec does not include duplicated pointer, and all event includes same
166    /// timestamp.
167    ///
168    /// Return err for unexpected events which should be filtered in earlier component:
169    /// input-pipeline and scenic. If 1 event is unexpected, translate_sequence() drops all events
170    /// from the same scan from driver.
171    fn translate_sequence(
172        &mut self,
173        time_nanos: TimeNanos,
174        events: &Vec<TouchEvent>,
175    ) -> Result<VecDeque<uapi::input_event>, TouchEventConversionError> {
176        let mut existing_slot: VecDeque<uapi::input_event> = VecDeque::new();
177        let mut new_slots: VecDeque<uapi::input_event> = VecDeque::new();
178
179        let time = timeval_from_time(zx::MonotonicInstant::from_nanos(time_nanos));
180
181        let no_contact_before_process_events = self.pointer_id_to_slot_id.is_empty();
182        let mut need_btn_touch_down = false;
183        let mut need_btn_touch_up = false;
184
185        // TODO(https://fxbug.dev/314151713): use event.device_info to route event to different
186        // device file.
187
188        for (index, event) in events.iter().enumerate() {
189            let pointer_id = event.pointer_id;
190            let slot_id = self.pointer_id_to_slot_id.get(&pointer_id).copied();
191            let previous_event = self.pointer_id_to_event.insert(pointer_id, event.clone());
192
193            match event.phase {
194                FidlEventPhase::Add => match (slot_id, previous_event) {
195                    (None, None) => {
196                        let new_slot_id = match self.available_slot_id() {
197                            Some(index) => index,
198                            None => {
199                                return Err(TouchEventConversionError::NoMoreAvailableSlotId);
200                            }
201                        };
202
203                        if no_contact_before_process_events {
204                            need_btn_touch_down = true;
205                        }
206
207                        self.pointer_id_to_slot_id.insert(pointer_id, new_slot_id);
208
209                        new_slots.push_back(uapi::input_event {
210                            time,
211                            type_: uapi::EV_ABS as u16,
212                            code: uapi::ABS_MT_SLOT as u16,
213                            value: new_slot_id as i32,
214                        });
215
216                        new_slots.push_back(uapi::input_event {
217                            time,
218                            type_: uapi::EV_ABS as u16,
219                            code: uapi::ABS_MT_TRACKING_ID as u16,
220                            value: pointer_id as i32,
221                        });
222
223                        new_slots.push_back(uapi::input_event {
224                            time,
225                            type_: uapi::EV_ABS as u16,
226                            code: uapi::ABS_MT_POSITION_X as u16,
227                            value: event.x,
228                        });
229
230                        new_slots.push_back(uapi::input_event {
231                            time,
232                            type_: uapi::EV_ABS as u16,
233                            code: uapi::ABS_MT_POSITION_Y as u16,
234                            value: event.y,
235                        });
236                    }
237                    (_, _) => {
238                        return Err(TouchEventConversionError::PointerAdded);
239                    }
240                },
241                FidlEventPhase::Change => match (slot_id, previous_event) {
242                    (Some(slot_id), Some(prev)) => {
243                        if prev.x != event.x || prev.y != event.y {
244                            existing_slot.push_back(uapi::input_event {
245                                time,
246                                type_: uapi::EV_ABS as u16,
247                                code: uapi::ABS_MT_SLOT as u16,
248                                value: slot_id as i32,
249                            });
250                        }
251
252                        if prev.x != event.x {
253                            existing_slot.push_back(uapi::input_event {
254                                time,
255                                type_: uapi::EV_ABS as u16,
256                                code: uapi::ABS_MT_POSITION_X as u16,
257                                value: event.x,
258                            });
259                        }
260
261                        if prev.y != event.y {
262                            existing_slot.push_back(uapi::input_event {
263                                time,
264                                type_: uapi::EV_ABS as u16,
265                                code: uapi::ABS_MT_POSITION_Y as u16,
266                                value: event.y,
267                            });
268                        }
269                    }
270                    (_, _) => {
271                        return Err(TouchEventConversionError::PointerNotFound);
272                    }
273                },
274                FidlEventPhase::Remove => match (slot_id, previous_event) {
275                    (Some(slot_id), Some(_)) => {
276                        self.pointer_id_to_slot_id.remove(&pointer_id);
277                        self.pointer_id_to_event.remove(&pointer_id);
278
279                        // Ensure BTN_TOUCH up event is only sent when the last pointer is lifted.
280                        // Here check if the event is the last event from the vec prevents a false
281                        // BTN_TOUCH up event if any error reset_state of this converter.
282                        if index == events.len() - 1 && self.pointer_id_to_slot_id.is_empty() {
283                            need_btn_touch_up = true;
284                        }
285
286                        existing_slot.push_back(uapi::input_event {
287                            time,
288                            type_: uapi::EV_ABS as u16,
289                            code: uapi::ABS_MT_SLOT as u16,
290                            value: slot_id as i32,
291                        });
292
293                        existing_slot.push_back(uapi::input_event {
294                            time,
295                            type_: uapi::EV_ABS as u16,
296                            code: uapi::ABS_MT_TRACKING_ID as u16,
297                            value: LIFTED_TRACKING_ID,
298                        });
299                    }
300                    (_, _) => {
301                        return Err(TouchEventConversionError::PointerNotFound);
302                    }
303                },
304                FidlEventPhase::Cancel => {
305                    return Err(TouchEventConversionError::PointerCancel);
306                }
307            }
308        }
309
310        let mut result: VecDeque<uapi::input_event> = VecDeque::new();
311
312        result.append(&mut existing_slot);
313        result.append(&mut new_slots);
314
315        if need_btn_touch_down {
316            result.push_back(uapi::input_event {
317                time,
318                type_: uapi::EV_KEY as u16,
319                code: uapi::BTN_TOUCH as u16,
320                value: 1,
321            });
322        } else if need_btn_touch_up {
323            result.push_back(uapi::input_event {
324                time,
325                type_: uapi::EV_KEY as u16,
326                code: uapi::BTN_TOUCH as u16,
327                value: 0,
328            });
329        }
330
331        if result.len() > 0 {
332            result.push_back(uapi::input_event {
333                time,
334                type_: uapi::EV_SYN as u16,
335                code: uapi::SYN_REPORT as u16,
336                value: 0,
337            });
338        }
339
340        Ok(result)
341    }
342
343    fn reset_state(&mut self) {
344        self.pointer_id_to_slot_id = HashMap::new();
345        self.pointer_id_to_event = HashMap::new();
346    }
347}
348
349#[cfg(test)]
350mod touchscreen_fuchsia_linux_tests {
351    use super::*;
352    use fidl_fuchsia_ui_pointer::TouchInteractionId;
353    use pretty_assertions::assert_eq;
354    use test_case::test_case;
355
356    fn make_touch_event_with_coords_phase_id_time(
357        x: f32,
358        y: f32,
359        phase: FidlEventPhase,
360        pointer_id: u32,
361        time_nanos: i64,
362    ) -> FidlTouchEvent {
363        FidlTouchEvent {
364            timestamp: Some(time_nanos),
365            pointer_sample: Some(TouchPointerSample {
366                position_in_viewport: Some([x, y]),
367                phase: Some(phase),
368                interaction: Some(TouchInteractionId {
369                    pointer_id,
370                    device_id: 0,
371                    interaction_id: 0,
372                }),
373                ..Default::default()
374            }),
375            ..Default::default()
376        }
377    }
378
379    fn make_touch_event_with_coords_phase_id(
380        x: f32,
381        y: f32,
382        phase: FidlEventPhase,
383        pointer_id: u32,
384    ) -> FidlTouchEvent {
385        make_touch_event_with_coords_phase_id_time(x, y, phase, pointer_id, 0)
386    }
387
388    fn make_uapi_input_event_with_time(
389        ty: u32,
390        code: u32,
391        value: i32,
392        time_nanos: i64,
393    ) -> uapi::input_event {
394        uapi::input_event {
395            time: timeval_from_time(zx::MonotonicInstant::from_nanos(time_nanos)),
396            type_: ty as u16,
397            code: code as u16,
398            value,
399        }
400    }
401
402    fn make_uapi_input_event(ty: u32, code: u32, value: i32) -> uapi::input_event {
403        make_uapi_input_event_with_time(ty, code, value, 0)
404    }
405
406    fn make_internal_touch_event(
407        time_nanos: i64,
408        x: i32,
409        y: i32,
410        phase: FidlEventPhase,
411        pointer_id: u32,
412    ) -> TouchEvent {
413        TouchEvent { time_nanos, pointer_id, phase, x, y }
414    }
415
416    #[test_case(FidlTouchEvent::default(); "not enough fields")]
417    fn ignored_events(e: FidlTouchEvent) {
418        let mut converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
419        let _ = converter.handle(vec![make_touch_event_with_coords_phase_id(
420            10.0,
421            20.0,
422            FidlEventPhase::Add,
423            1,
424        )]);
425        let batch = converter.handle(vec![e]);
426        assert_eq!(batch.events, vec![]);
427        assert_eq!(batch.last_event_time_ns, 0);
428        assert_eq!(batch.count_converted_fidl_events, 0);
429        assert_eq!(batch.count_ignored_fidl_events, 1);
430        assert_eq!(batch.count_unexpected_fidl_events, 0);
431    }
432
433    #[test_case(make_touch_event_with_coords_phase_id(
434        1.0,
435        2.0,
436        FidlEventPhase::Add,
437        1,
438    ); "touch add pointer already added")]
439    #[test_case(make_touch_event_with_coords_phase_id(
440        1.0,
441        2.0,
442        FidlEventPhase::Change,
443        2,
444    ); "touch change pointer not added")]
445    #[test_case(make_touch_event_with_coords_phase_id(
446        0.0,
447        0.0,
448        FidlEventPhase::Remove,
449        2,
450    ); "touch remove pointer not added")]
451    #[test_case(make_touch_event_with_coords_phase_id(
452        0.0,
453        0.0,
454        FidlEventPhase::Cancel,
455        1,
456    ); "touch cancel")]
457    fn unexpected_events(e: FidlTouchEvent) {
458        let mut converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
459        let _ = converter.handle(vec![make_touch_event_with_coords_phase_id(
460            10.0,
461            20.0,
462            FidlEventPhase::Add,
463            1,
464        )]);
465        let batch = converter.handle(vec![e]);
466        assert_eq!(batch.events, vec![]);
467        assert_eq!(batch.last_event_time_ns, 0);
468        assert_eq!(batch.count_converted_fidl_events, 0);
469        assert_eq!(batch.count_ignored_fidl_events, 0);
470        assert_eq!(batch.count_unexpected_fidl_events, 1);
471    }
472
473    #[test]
474    fn touch_add() {
475        let mut converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
476        let batch = converter.handle(vec![make_touch_event_with_coords_phase_id(
477            10.0,
478            20.0,
479            FidlEventPhase::Add,
480            1,
481        )]);
482
483        assert_eq!(
484            batch.events,
485            vec![
486                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
487                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1),
488                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
489                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
490                make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 1),
491                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
492            ]
493        );
494        assert_eq!(batch.last_event_time_ns, 0);
495        assert_eq!(batch.count_converted_fidl_events, 1);
496        assert_eq!(batch.count_ignored_fidl_events, 0);
497        assert_eq!(batch.count_unexpected_fidl_events, 0);
498
499        let mut want_converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
500        want_converter.pointer_id_to_slot_id.insert(1, 0);
501        want_converter
502            .pointer_id_to_event
503            .insert(1, make_internal_touch_event(0, 10, 20, FidlEventPhase::Add, 1));
504
505        assert_eq!(converter, want_converter);
506    }
507
508    #[test]
509    fn touch_change() {
510        let mut converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
511        let _ = converter.handle(vec![make_touch_event_with_coords_phase_id(
512            10.0,
513            20.0,
514            FidlEventPhase::Add,
515            1,
516        )]);
517
518        let batch = converter.handle(vec![make_touch_event_with_coords_phase_id(
519            11.0,
520            21.0,
521            FidlEventPhase::Change,
522            1,
523        )]);
524        assert_eq!(
525            batch.events,
526            vec![
527                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
528                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 11),
529                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 21),
530                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
531            ]
532        );
533        assert_eq!(batch.last_event_time_ns, 0);
534        assert_eq!(batch.count_converted_fidl_events, 1);
535        assert_eq!(batch.count_ignored_fidl_events, 0);
536        assert_eq!(batch.count_unexpected_fidl_events, 0);
537
538        let mut want_converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
539
540        want_converter.pointer_id_to_slot_id.insert(1, 0);
541        want_converter
542            .pointer_id_to_event
543            .insert(1, make_internal_touch_event(0, 11, 21, FidlEventPhase::Change, 1));
544
545        assert_eq!(converter, want_converter);
546    }
547
548    #[test]
549    fn touch_change_no_change() {
550        let mut converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
551        let _ = converter.handle(vec![make_touch_event_with_coords_phase_id(
552            10.0,
553            20.0,
554            FidlEventPhase::Add,
555            1,
556        )]);
557
558        let batch = converter.handle(vec![make_touch_event_with_coords_phase_id(
559            10.0,
560            20.0,
561            FidlEventPhase::Change,
562            1,
563        )]);
564        assert_eq!(batch.events, vec![]);
565        assert_eq!(batch.last_event_time_ns, 0);
566        assert_eq!(batch.count_converted_fidl_events, 1);
567        assert_eq!(batch.count_ignored_fidl_events, 0);
568        assert_eq!(batch.count_unexpected_fidl_events, 0);
569
570        let mut want_converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
571
572        want_converter.pointer_id_to_slot_id.insert(1, 0);
573        want_converter
574            .pointer_id_to_event
575            .insert(1, make_internal_touch_event(0, 10, 20, FidlEventPhase::Change, 1));
576
577        assert_eq!(converter, want_converter);
578    }
579
580    #[test]
581    fn touch_remove() {
582        let mut converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
583        let _ = converter.handle(vec![make_touch_event_with_coords_phase_id(
584            10.0,
585            20.0,
586            FidlEventPhase::Add,
587            1,
588        )]);
589        let batch = converter.handle(vec![make_touch_event_with_coords_phase_id(
590            0.0,
591            0.0,
592            FidlEventPhase::Remove,
593            1,
594        )]);
595        assert_eq!(batch.last_event_time_ns, 0);
596        assert_eq!(batch.count_converted_fidl_events, 1);
597        assert_eq!(batch.count_ignored_fidl_events, 0);
598        assert_eq!(batch.count_unexpected_fidl_events, 0);
599
600        assert_eq!(
601            batch.events,
602            vec![
603                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
604                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
605                make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 0),
606                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
607            ]
608        );
609
610        assert_eq!(
611            converter,
612            FuchsiaTouchEventToLinuxTouchEventConverter {
613                pointer_id_to_slot_id: HashMap::new(),
614                pointer_id_to_event: HashMap::new()
615            }
616        );
617    }
618
619    #[test]
620    fn multi_touch_sequence() {
621        let mut converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
622
623        // The first pointer down.
624        let _ = converter.handle(vec![make_touch_event_with_coords_phase_id(
625            10.0,
626            20.0,
627            FidlEventPhase::Add,
628            1,
629        )]);
630
631        // The second pointer down, and the first pointer move.
632        let batch = converter.handle(vec![
633            make_touch_event_with_coords_phase_id(11.0, 21.0, FidlEventPhase::Change, 1),
634            make_touch_event_with_coords_phase_id(100.0, 200.0, FidlEventPhase::Add, 2),
635        ]);
636
637        assert_eq!(
638            batch.events,
639            vec![
640                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
641                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 11),
642                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 21),
643                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
644                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 2),
645                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 100),
646                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 200),
647                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
648            ]
649        );
650        assert_eq!(batch.last_event_time_ns, 0);
651        assert_eq!(batch.count_converted_fidl_events, 2);
652        assert_eq!(batch.count_ignored_fidl_events, 0);
653        assert_eq!(batch.count_unexpected_fidl_events, 0);
654
655        let mut want_converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
656
657        want_converter.pointer_id_to_slot_id.insert(1, 0);
658        want_converter.pointer_id_to_slot_id.insert(2, 1);
659        want_converter
660            .pointer_id_to_event
661            .insert(1, make_internal_touch_event(0, 11, 21, FidlEventPhase::Change, 1));
662        want_converter
663            .pointer_id_to_event
664            .insert(2, make_internal_touch_event(0, 100, 200, FidlEventPhase::Add, 2));
665
666        assert_eq!(converter, want_converter);
667
668        // Both pointer move.
669        let batch = converter.handle(vec![
670            make_touch_event_with_coords_phase_id(12.0, 22.0, FidlEventPhase::Change, 1),
671            make_touch_event_with_coords_phase_id(101.0, 201.0, FidlEventPhase::Change, 2),
672        ]);
673
674        assert_eq!(
675            batch.events,
676            vec![
677                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
678                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 12),
679                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 22),
680                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
681                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 101),
682                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 201),
683                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
684            ]
685        );
686        assert_eq!(batch.last_event_time_ns, 0);
687        assert_eq!(batch.count_converted_fidl_events, 2);
688        assert_eq!(batch.count_ignored_fidl_events, 0);
689        assert_eq!(batch.count_unexpected_fidl_events, 0);
690
691        want_converter
692            .pointer_id_to_event
693            .insert(1, make_internal_touch_event(0, 12, 22, FidlEventPhase::Change, 1));
694        want_converter
695            .pointer_id_to_event
696            .insert(2, make_internal_touch_event(0, 101, 201, FidlEventPhase::Change, 2));
697        assert_eq!(converter, want_converter);
698
699        // The second pointer up, and the first pointer move.
700        let batch = converter.handle(vec![
701            make_touch_event_with_coords_phase_id(13.0, 23.0, FidlEventPhase::Change, 1),
702            make_touch_event_with_coords_phase_id(0.0, 0.0, FidlEventPhase::Remove, 2),
703        ]);
704
705        assert_eq!(
706            batch.events,
707            vec![
708                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
709                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 13),
710                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 23),
711                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
712                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
713                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
714            ]
715        );
716        assert_eq!(batch.last_event_time_ns, 0);
717        assert_eq!(batch.count_converted_fidl_events, 2);
718        assert_eq!(batch.count_ignored_fidl_events, 0);
719        assert_eq!(batch.count_unexpected_fidl_events, 0);
720
721        want_converter
722            .pointer_id_to_event
723            .insert(1, make_internal_touch_event(0, 13, 23, FidlEventPhase::Change, 1));
724        want_converter.pointer_id_to_slot_id.remove(&2);
725        want_converter.pointer_id_to_event.remove(&2);
726
727        assert_eq!(converter, want_converter);
728
729        // The third pointer down, and the first pointer move.
730        let batch = converter.handle(vec![
731            make_touch_event_with_coords_phase_id(14.0, 24.0, FidlEventPhase::Change, 1),
732            make_touch_event_with_coords_phase_id(50.0, 60.0, FidlEventPhase::Add, 3),
733        ]);
734
735        assert_eq!(
736            batch.events,
737            vec![
738                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
739                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 14),
740                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 24),
741                // should reuse slot id 1.
742                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
743                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 3),
744                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 50),
745                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 60),
746                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
747            ]
748        );
749        assert_eq!(batch.last_event_time_ns, 0);
750        assert_eq!(batch.count_converted_fidl_events, 2);
751        assert_eq!(batch.count_ignored_fidl_events, 0);
752        assert_eq!(batch.count_unexpected_fidl_events, 0);
753
754        want_converter.pointer_id_to_slot_id.insert(3, 1);
755        want_converter
756            .pointer_id_to_event
757            .insert(1, make_internal_touch_event(0, 14, 24, FidlEventPhase::Change, 1));
758        want_converter
759            .pointer_id_to_event
760            .insert(3, make_internal_touch_event(0, 50, 60, FidlEventPhase::Add, 3));
761
762        assert_eq!(converter, want_converter);
763
764        // The third pointer up, and the first pointer move.
765        let batch = converter.handle(vec![
766            make_touch_event_with_coords_phase_id(15.0, 25.0, FidlEventPhase::Change, 1),
767            make_touch_event_with_coords_phase_id(0.0, 0.0, FidlEventPhase::Remove, 3),
768        ]);
769
770        assert_eq!(
771            batch.events,
772            vec![
773                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
774                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 15),
775                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 25),
776                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
777                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
778                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
779            ]
780        );
781        assert_eq!(batch.last_event_time_ns, 0);
782        assert_eq!(batch.count_converted_fidl_events, 2);
783        assert_eq!(batch.count_ignored_fidl_events, 0);
784        assert_eq!(batch.count_unexpected_fidl_events, 0);
785
786        want_converter
787            .pointer_id_to_event
788            .insert(1, make_internal_touch_event(0, 15, 25, FidlEventPhase::Change, 1));
789        want_converter.pointer_id_to_slot_id.remove(&3);
790        want_converter.pointer_id_to_event.remove(&3);
791
792        assert_eq!(converter, want_converter);
793
794        // The first pointer up.
795        let batch = converter.handle(vec![make_touch_event_with_coords_phase_id(
796            0.0,
797            0.0,
798            FidlEventPhase::Remove,
799            1,
800        )]);
801
802        assert_eq!(
803            batch.events,
804            vec![
805                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
806                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
807                make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 0),
808                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
809            ]
810        );
811        assert_eq!(batch.last_event_time_ns, 0);
812        assert_eq!(batch.count_converted_fidl_events, 1);
813        assert_eq!(batch.count_ignored_fidl_events, 0);
814        assert_eq!(batch.count_unexpected_fidl_events, 0);
815
816        want_converter.reset_state();
817        assert_eq!(converter, want_converter);
818    }
819
820    #[test]
821    fn multi_touch_sequence_receive_only_one_pointer_change_when_two_pointer_contacting() {
822        let mut converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
823
824        // 2 pointer down.
825        let batch = converter.handle(vec![
826            make_touch_event_with_coords_phase_id(10.0, 20.0, FidlEventPhase::Add, 1),
827            make_touch_event_with_coords_phase_id(100.0, 200.0, FidlEventPhase::Add, 2),
828        ]);
829
830        assert_eq!(
831            batch.events,
832            vec![
833                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
834                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1),
835                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
836                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
837                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
838                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 2),
839                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 100),
840                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 200),
841                make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 1),
842                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
843            ]
844        );
845        assert_eq!(batch.last_event_time_ns, 0);
846        assert_eq!(batch.count_converted_fidl_events, 2);
847        assert_eq!(batch.count_ignored_fidl_events, 0);
848        assert_eq!(batch.count_unexpected_fidl_events, 0);
849
850        let mut want_converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
851
852        want_converter.pointer_id_to_slot_id.insert(1, 0);
853        want_converter.pointer_id_to_slot_id.insert(2, 1);
854        want_converter
855            .pointer_id_to_event
856            .insert(1, make_internal_touch_event(0, 10, 20, FidlEventPhase::Add, 1));
857        want_converter
858            .pointer_id_to_event
859            .insert(2, make_internal_touch_event(0, 100, 200, FidlEventPhase::Add, 2));
860        assert_eq!(converter, want_converter);
861
862        // 1st pointer move, no event for 2nd pointer.
863        let batch = converter.handle(vec![make_touch_event_with_coords_phase_id(
864            12.0,
865            22.0,
866            FidlEventPhase::Change,
867            1,
868        )]);
869
870        assert_eq!(
871            batch.events,
872            vec![
873                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
874                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 12),
875                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 22),
876                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
877            ]
878        );
879        assert_eq!(batch.last_event_time_ns, 0);
880        assert_eq!(batch.count_converted_fidl_events, 1);
881        assert_eq!(batch.count_ignored_fidl_events, 0);
882        assert_eq!(batch.count_unexpected_fidl_events, 0);
883
884        want_converter
885            .pointer_id_to_event
886            .insert(1, make_internal_touch_event(0, 12, 22, FidlEventPhase::Change, 1));
887        assert_eq!(converter, want_converter);
888    }
889
890    #[test]
891    fn handle_return_multi_protocl_b_seq() {
892        let mut converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
893
894        let batch = converter.handle(vec![
895            // ignore
896            FidlTouchEvent::default(),
897            make_touch_event_with_coords_phase_id_time(10.0, 20.0, FidlEventPhase::Add, 1, 1),
898            make_touch_event_with_coords_phase_id_time(11.0, 21.0, FidlEventPhase::Change, 1, 1000),
899        ]);
900
901        assert_eq!(
902            batch.events,
903            vec![
904                make_uapi_input_event_with_time(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0, 1),
905                make_uapi_input_event_with_time(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1, 1),
906                make_uapi_input_event_with_time(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10, 1),
907                make_uapi_input_event_with_time(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20, 1),
908                make_uapi_input_event_with_time(uapi::EV_KEY, uapi::BTN_TOUCH, 1, 1),
909                make_uapi_input_event_with_time(uapi::EV_SYN, uapi::SYN_REPORT, 0, 1),
910                make_uapi_input_event_with_time(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0, 1000),
911                make_uapi_input_event_with_time(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 11, 1000),
912                make_uapi_input_event_with_time(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 21, 1000),
913                make_uapi_input_event_with_time(uapi::EV_SYN, uapi::SYN_REPORT, 0, 1000),
914            ]
915        );
916        assert_eq!(batch.last_event_time_ns, 1000);
917        assert_eq!(batch.count_converted_fidl_events, 2);
918        assert_eq!(batch.count_ignored_fidl_events, 1);
919        assert_eq!(batch.count_unexpected_fidl_events, 0);
920
921        let mut want_converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
922
923        want_converter.pointer_id_to_slot_id.insert(1, 0);
924        want_converter
925            .pointer_id_to_event
926            .insert(1, make_internal_touch_event(1000, 11, 21, FidlEventPhase::Change, 1));
927
928        assert_eq!(converter, want_converter);
929    }
930
931    #[test]
932    fn handle_unsorted_events() {
933        let mut converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
934
935        let batch = converter.handle(vec![
936            // ignore
937            FidlTouchEvent::default(),
938            make_touch_event_with_coords_phase_id_time(11.0, 21.0, FidlEventPhase::Change, 1, 1000),
939            make_touch_event_with_coords_phase_id_time(10.0, 20.0, FidlEventPhase::Add, 1, 1),
940        ]);
941
942        assert_eq!(
943            batch.events,
944            vec![
945                make_uapi_input_event_with_time(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0, 1),
946                make_uapi_input_event_with_time(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1, 1),
947                make_uapi_input_event_with_time(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10, 1),
948                make_uapi_input_event_with_time(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20, 1),
949                make_uapi_input_event_with_time(uapi::EV_KEY, uapi::BTN_TOUCH, 1, 1),
950                make_uapi_input_event_with_time(uapi::EV_SYN, uapi::SYN_REPORT, 0, 1),
951                make_uapi_input_event_with_time(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0, 1000),
952                make_uapi_input_event_with_time(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 11, 1000),
953                make_uapi_input_event_with_time(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 21, 1000),
954                make_uapi_input_event_with_time(uapi::EV_SYN, uapi::SYN_REPORT, 0, 1000),
955            ]
956        );
957        assert_eq!(batch.last_event_time_ns, 1000);
958        assert_eq!(batch.count_converted_fidl_events, 2);
959        assert_eq!(batch.count_ignored_fidl_events, 1);
960        assert_eq!(batch.count_unexpected_fidl_events, 0);
961
962        let mut want_converter = FuchsiaTouchEventToLinuxTouchEventConverter::create();
963
964        want_converter.pointer_id_to_slot_id.insert(1, 0);
965        want_converter
966            .pointer_id_to_event
967            .insert(1, make_internal_touch_event(1000, 11, 21, FidlEventPhase::Change, 1));
968
969        assert_eq!(converter, want_converter);
970    }
971}