1use fidl_fuchsia_input_report as fir;
6use sorted_vec_map::{SortedVecMap, SortedVecSet};
7use starnix_types::time::timeval_from_time;
8use starnix_uapi::uapi;
9
10const DEFAULT_MAX_TOUCH_CONTACTS: usize = 10;
11const LIFTED_TRACKING_ID: i32 = -1;
12
13pub fn touch_button_to_linux_key(button: fir::TouchButton) -> Option<u32> {
15 match button {
16 fir::TouchButton::Palm => Some(uapi::KEY_SLEEP),
17 fir::TouchButton::SwipeUp => Some(uapi::KEY_UP),
18 fir::TouchButton::SwipeLeft => Some(uapi::KEY_LEFT),
19 fir::TouchButton::SwipeRight => Some(uapi::KEY_RIGHT),
20 fir::TouchButton::SwipeDown => Some(uapi::KEY_DOWN),
21 _ => None,
22 }
23}
24
25#[derive(Debug, Clone, PartialEq, Default)]
26struct ContactState {
27 pos_x: i32,
28 pos_y: i32,
29 pressure: Option<i32>,
30 touch_major: Option<i32>,
31 touch_minor: Option<i32>,
32}
33
34#[derive(Debug, PartialEq)]
36pub struct TouchReportConverter {
37 pointer_id_to_slot_id: SortedVecMap<u32, usize>,
38 slot_to_state: SortedVecMap<usize, ContactState>,
39 pressed_touch_buttons: SortedVecSet<u16>,
40 max_slots: usize,
41}
42
43impl Default for TouchReportConverter {
44 fn default() -> Self {
45 Self {
46 pointer_id_to_slot_id: SortedVecMap::new(),
47 slot_to_state: SortedVecMap::new(),
48 pressed_touch_buttons: SortedVecSet::new(),
49 max_slots: DEFAULT_MAX_TOUCH_CONTACTS,
50 }
51 }
52}
53
54impl TouchReportConverter {
55 pub fn new() -> Self {
56 Self::default()
57 }
58
59 pub fn with_max_slots(max_slots: usize) -> Self {
60 Self {
61 pointer_id_to_slot_id: SortedVecMap::new(),
62 slot_to_state: SortedVecMap::new(),
63 pressed_touch_buttons: SortedVecSet::new(),
64 max_slots,
65 }
66 }
67
68 pub fn from_touch_input_descriptor(input: &fir::TouchInputDescriptor) -> Self {
69 let max_slots = input
70 .max_contacts
71 .map(|m| m as usize)
72 .or_else(|| input.contacts.as_ref().map(|c| c.len()))
73 .unwrap_or(DEFAULT_MAX_TOUCH_CONTACTS)
74 .max(1);
75 Self::with_max_slots(max_slots)
76 }
77
78 pub fn from_touch_descriptor(descriptor: &fir::TouchDescriptor) -> Self {
79 descriptor.input.as_ref().map(Self::from_touch_input_descriptor).unwrap_or_default()
80 }
81
82 pub fn max_slots(&self) -> usize {
83 self.max_slots
84 }
85
86 fn available_slot_id(&self) -> Option<usize> {
87 let mut used_slots = bit_vec::BitVec::<u32>::from_elem(self.max_slots, false);
88 for &slot in self.pointer_id_to_slot_id.values() {
89 if slot < self.max_slots {
90 used_slots.set(slot, true);
91 }
92 }
93 used_slots.iter().position(|used| !used)
94 }
95
96 pub fn handle(
97 &mut self,
98 report: &fir::TouchInputReport,
99 event_time: zx::MonotonicInstant,
100 ) -> Vec<uapi::input_event> {
101 let mut events = Vec::new();
102 let time = timeval_from_time(event_time);
103
104 let current_touch_buttons = report.pressed_buttons.as_ref().map(|buttons| {
105 let mut set = SortedVecSet::new();
106 for &button in buttons.iter() {
107 if let Some(key) = touch_button_to_linux_key(button) {
108 set.insert(key as u16);
109 }
110 }
111 set
112 });
113
114 if let Some(ref contacts) = report.contacts {
115 let was_empty = self.pointer_id_to_slot_id.is_empty();
116 let is_button_report_with_empty_contacts = contacts.is_empty()
117 && current_touch_buttons
118 .as_ref()
119 .is_some_and(|current| *current != self.pressed_touch_buttons);
120
121 let mut current_contact_ids = SortedVecSet::new();
122
123 for contact in contacts.iter() {
124 let Some(contact_id) = contact.contact_id else {
125 continue;
126 };
127
128 if !current_contact_ids.insert(contact_id) {
132 continue;
133 }
134
135 let (Some(raw_pos_x), Some(raw_pos_y)) = (contact.position_x, contact.position_y)
138 else {
139 continue;
140 };
141 let pos_x = crate::clamp_i64_to_i32(raw_pos_x);
142 let pos_y = crate::clamp_i64_to_i32(raw_pos_y);
143
144 let is_new = !self.pointer_id_to_slot_id.contains_key(&contact_id);
145 let slot = if let Some(&existing_slot) = self.pointer_id_to_slot_id.get(&contact_id)
146 {
147 existing_slot
148 } else if let Some(new_slot) = self.available_slot_id() {
149 self.pointer_id_to_slot_id.insert(contact_id, new_slot);
150 new_slot
151 } else {
152 continue;
154 };
155
156 let pressure = contact.pressure.map(crate::clamp_i64_to_i32);
157 let (touch_major, touch_minor) =
158 match (contact.contact_width, contact.contact_height) {
159 (Some(w), Some(h)) => {
160 let major = std::cmp::max(w, h);
161 let minor = std::cmp::min(w, h);
162 (
163 Some(crate::clamp_i64_to_i32(major)),
164 Some(crate::clamp_i64_to_i32(minor)),
165 )
166 }
167 (Some(w), None) => (Some(crate::clamp_i64_to_i32(w)), None),
168 (None, Some(h)) => (Some(crate::clamp_i64_to_i32(h)), None),
169 (None, None) => (None, None),
170 };
171
172 if is_new {
173 let new_state =
174 ContactState { pos_x, pos_y, pressure, touch_major, touch_minor };
175 events.push(uapi::input_event {
176 time,
177 type_: uapi::EV_ABS as u16,
178 code: uapi::ABS_MT_SLOT as u16,
179 value: slot as i32,
180 });
181 events.push(uapi::input_event {
183 time,
184 type_: uapi::EV_ABS as u16,
185 code: uapi::ABS_MT_TRACKING_ID as u16,
186 value: (contact_id & 0x7fff_ffff) as i32,
187 });
188 events.push(uapi::input_event {
189 time,
190 type_: uapi::EV_ABS as u16,
191 code: uapi::ABS_MT_POSITION_X as u16,
192 value: pos_x,
193 });
194 events.push(uapi::input_event {
195 time,
196 type_: uapi::EV_ABS as u16,
197 code: uapi::ABS_MT_POSITION_Y as u16,
198 value: pos_y,
199 });
200 if let Some(p) = pressure {
201 events.push(uapi::input_event {
202 time,
203 type_: uapi::EV_ABS as u16,
204 code: uapi::ABS_MT_PRESSURE as u16,
205 value: p,
206 });
207 }
208 if let Some(major) = touch_major {
209 events.push(uapi::input_event {
210 time,
211 type_: uapi::EV_ABS as u16,
212 code: uapi::ABS_MT_TOUCH_MAJOR as u16,
213 value: major,
214 });
215 }
216 if let Some(minor) = touch_minor {
217 events.push(uapi::input_event {
218 time,
219 type_: uapi::EV_ABS as u16,
220 code: uapi::ABS_MT_TOUCH_MINOR as u16,
221 value: minor,
222 });
223 }
224 self.slot_to_state.insert(slot, new_state);
225 } else {
226 let old_state = self.slot_to_state.get(&slot);
227 let changed_x = old_state.map_or(true, |s| s.pos_x != pos_x);
228 let changed_y = old_state.map_or(true, |s| s.pos_y != pos_y);
229 let changed_pressure =
230 pressure.is_some() && old_state.map_or(true, |s| s.pressure != pressure);
231 let changed_major = touch_major.is_some()
232 && old_state.map_or(true, |s| s.touch_major != touch_major);
233 let changed_minor = touch_minor.is_some()
234 && old_state.map_or(true, |s| s.touch_minor != touch_minor);
235
236 if changed_x || changed_y || changed_pressure || changed_major || changed_minor
237 {
238 let new_state = ContactState {
239 pos_x,
240 pos_y,
241 pressure: pressure.or_else(|| old_state.and_then(|s| s.pressure)),
242 touch_major: touch_major
243 .or_else(|| old_state.and_then(|s| s.touch_major)),
244 touch_minor: touch_minor
245 .or_else(|| old_state.and_then(|s| s.touch_minor)),
246 };
247 events.push(uapi::input_event {
248 time,
249 type_: uapi::EV_ABS as u16,
250 code: uapi::ABS_MT_SLOT as u16,
251 value: slot as i32,
252 });
253 if changed_x {
254 events.push(uapi::input_event {
255 time,
256 type_: uapi::EV_ABS as u16,
257 code: uapi::ABS_MT_POSITION_X as u16,
258 value: pos_x,
259 });
260 }
261 if changed_y {
262 events.push(uapi::input_event {
263 time,
264 type_: uapi::EV_ABS as u16,
265 code: uapi::ABS_MT_POSITION_Y as u16,
266 value: pos_y,
267 });
268 }
269 if changed_pressure {
270 if let Some(p) = pressure {
271 events.push(uapi::input_event {
272 time,
273 type_: uapi::EV_ABS as u16,
274 code: uapi::ABS_MT_PRESSURE as u16,
275 value: p,
276 });
277 }
278 }
279 if changed_major {
280 if let Some(major) = touch_major {
281 events.push(uapi::input_event {
282 time,
283 type_: uapi::EV_ABS as u16,
284 code: uapi::ABS_MT_TOUCH_MAJOR as u16,
285 value: major,
286 });
287 }
288 }
289 if changed_minor {
290 if let Some(minor) = touch_minor {
291 events.push(uapi::input_event {
292 time,
293 type_: uapi::EV_ABS as u16,
294 code: uapi::ABS_MT_TOUCH_MINOR as u16,
295 value: minor,
296 });
297 }
298 }
299 self.slot_to_state.insert(slot, new_state);
300 }
301 }
302 }
303
304 if !is_button_report_with_empty_contacts {
306 let removed_contacts: Vec<(u32, usize)> = self
307 .pointer_id_to_slot_id
308 .iter()
309 .filter(|(id, _)| !current_contact_ids.contains(id))
310 .map(|(&id, &slot)| (id, slot))
311 .collect();
312
313 for (id, slot) in removed_contacts {
314 events.push(uapi::input_event {
315 time,
316 type_: uapi::EV_ABS as u16,
317 code: uapi::ABS_MT_SLOT as u16,
318 value: slot as i32,
319 });
320 events.push(uapi::input_event {
321 time,
322 type_: uapi::EV_ABS as u16,
323 code: uapi::ABS_MT_TRACKING_ID as u16,
324 value: LIFTED_TRACKING_ID,
325 });
326 self.pointer_id_to_slot_id.remove(&id);
327 self.slot_to_state.remove(&slot);
328 }
329
330 let is_empty = self.pointer_id_to_slot_id.is_empty();
332 if was_empty && !is_empty {
333 events.push(uapi::input_event {
334 time,
335 type_: uapi::EV_KEY as u16,
336 code: uapi::BTN_TOUCH as u16,
337 value: 1,
338 });
339 } else if !was_empty && is_empty {
340 events.push(uapi::input_event {
341 time,
342 type_: uapi::EV_KEY as u16,
343 code: uapi::BTN_TOUCH as u16,
344 value: 0,
345 });
346 }
347 }
348 }
349
350 if let Some(current_buttons) = current_touch_buttons {
352 crate::emit_key_transitions(
353 &mut self.pressed_touch_buttons,
354 current_buttons,
355 time,
356 &mut events,
357 );
358 }
359
360 if !events.is_empty() {
361 events.push(uapi::input_event {
362 time,
363 type_: uapi::EV_SYN as u16,
364 code: uapi::SYN_REPORT as u16,
365 value: 0,
366 });
367 }
368
369 events
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 use super::*;
376
377 #[::fuchsia::test]
378 fn test_touch_report_single_contact_lifecycle() {
379 let mut converter = TouchReportConverter::new();
380 let time = zx::MonotonicInstant::from_nanos(100_000_000);
381
382 let report_down = fir::TouchInputReport {
384 contacts: Some(vec![fir::ContactInputReport {
385 contact_id: Some(1),
386 position_x: Some(100),
387 position_y: Some(200),
388 pressure: Some(50),
389 ..Default::default()
390 }]),
391 ..Default::default()
392 };
393
394 let events = converter.handle(&report_down, time);
395 assert_eq!(
396 events.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
397 vec![
398 (uapi::EV_ABS as u16, uapi::ABS_MT_SLOT as u16, 0),
399 (uapi::EV_ABS as u16, uapi::ABS_MT_TRACKING_ID as u16, 1),
400 (uapi::EV_ABS as u16, uapi::ABS_MT_POSITION_X as u16, 100),
401 (uapi::EV_ABS as u16, uapi::ABS_MT_POSITION_Y as u16, 200),
402 (uapi::EV_ABS as u16, uapi::ABS_MT_PRESSURE as u16, 50),
403 (uapi::EV_KEY as u16, uapi::BTN_TOUCH as u16, 1),
404 (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
405 ]
406 );
407
408 let report_move = fir::TouchInputReport {
410 contacts: Some(vec![fir::ContactInputReport {
411 contact_id: Some(1),
412 position_x: Some(150),
413 position_y: Some(250),
414 pressure: Some(60),
415 ..Default::default()
416 }]),
417 ..Default::default()
418 };
419
420 let events = converter.handle(&report_move, time);
421 assert_eq!(
422 events.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
423 vec![
424 (uapi::EV_ABS as u16, uapi::ABS_MT_SLOT as u16, 0),
425 (uapi::EV_ABS as u16, uapi::ABS_MT_POSITION_X as u16, 150),
426 (uapi::EV_ABS as u16, uapi::ABS_MT_POSITION_Y as u16, 250),
427 (uapi::EV_ABS as u16, uapi::ABS_MT_PRESSURE as u16, 60),
428 (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
429 ]
430 );
431
432 let report_up = fir::TouchInputReport { contacts: Some(vec![]), ..Default::default() };
434
435 let events = converter.handle(&report_up, time);
436 assert_eq!(
437 events.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
438 vec![
439 (uapi::EV_ABS as u16, uapi::ABS_MT_SLOT as u16, 0),
440 (uapi::EV_ABS as u16, uapi::ABS_MT_TRACKING_ID as u16, -1),
441 (uapi::EV_KEY as u16, uapi::BTN_TOUCH as u16, 0),
442 (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
443 ]
444 );
445 }
446
447 #[::fuchsia::test]
448 fn test_touch_report_multi_contact() {
449 let mut converter = TouchReportConverter::new();
450 let time = zx::MonotonicInstant::from_nanos(200_000_000);
451
452 let report1 = fir::TouchInputReport {
454 contacts: Some(vec![fir::ContactInputReport {
455 contact_id: Some(10),
456 position_x: Some(50),
457 position_y: Some(60),
458 ..Default::default()
459 }]),
460 ..Default::default()
461 };
462 let events1 = converter.handle(&report1, time);
463 assert_eq!(events1[0].code, uapi::ABS_MT_SLOT as u16);
464 assert_eq!(events1[0].value, 0);
465
466 let report2 = fir::TouchInputReport {
468 contacts: Some(vec![
469 fir::ContactInputReport {
470 contact_id: Some(10),
471 position_x: Some(52),
472 position_y: Some(62),
473 ..Default::default()
474 },
475 fir::ContactInputReport {
476 contact_id: Some(20),
477 position_x: Some(300),
478 position_y: Some(400),
479 ..Default::default()
480 },
481 ]),
482 ..Default::default()
483 };
484 let events2 = converter.handle(&report2, time);
485 assert!(events2.iter().any(|e| e.code == uapi::ABS_MT_SLOT as u16 && e.value == 0));
487 assert!(events2.iter().any(|e| e.code == uapi::ABS_MT_SLOT as u16 && e.value == 1));
488 assert!(events2.iter().any(|e| e.code == uapi::ABS_MT_TRACKING_ID as u16 && e.value == 20));
489
490 let report3 = fir::TouchInputReport {
492 contacts: Some(vec![fir::ContactInputReport {
493 contact_id: Some(20),
494 position_x: Some(305),
495 position_y: Some(405),
496 ..Default::default()
497 }]),
498 ..Default::default()
499 };
500 let events3 = converter.handle(&report3, time);
501 assert!(events3.iter().any(|e| e.code == uapi::ABS_MT_SLOT as u16 && e.value == 0));
503 assert!(events3.iter().any(|e| e.code == uapi::ABS_MT_TRACKING_ID as u16 && e.value == -1));
504 assert!(!events3.iter().any(|e| e.code == uapi::BTN_TOUCH as u16 && e.value == 0));
506
507 let report4 = fir::TouchInputReport { contacts: Some(vec![]), ..Default::default() };
509 let events4 = converter.handle(&report4, time);
510 assert!(events4.iter().any(|e| e.code == uapi::ABS_MT_SLOT as u16 && e.value == 1));
511 assert!(events4.iter().any(|e| e.code == uapi::ABS_MT_TRACKING_ID as u16 && e.value == -1));
512 assert!(events4.iter().any(|e| e.code == uapi::BTN_TOUCH as u16 && e.value == 0));
513 }
514
515 #[::fuchsia::test]
516 fn test_touch_report_buttons() {
517 let mut converter = TouchReportConverter::new();
518 let time = zx::MonotonicInstant::from_nanos(300_000_000);
519
520 let report_press = fir::TouchInputReport {
522 pressed_buttons: Some(vec![fir::TouchButton::Palm]),
523 ..Default::default()
524 };
525 let events = converter.handle(&report_press, time);
526 assert_eq!(
527 events.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
528 vec![
529 (uapi::EV_KEY as u16, uapi::KEY_SLEEP as u16, 1),
530 (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
531 ]
532 );
533
534 let report_release =
536 fir::TouchInputReport { pressed_buttons: Some(vec![]), ..Default::default() };
537 let events = converter.handle(&report_release, time);
538 assert_eq!(
539 events.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
540 vec![
541 (uapi::EV_KEY as u16, uapi::KEY_SLEEP as u16, 0),
542 (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
543 ]
544 );
545 }
546
547 #[::fuchsia::test]
548 fn test_touch_report_contact_size_normalization() {
549 let mut converter = TouchReportConverter::new();
550 let time = zx::MonotonicInstant::from_nanos(100_000_000);
551
552 let report1 = fir::TouchInputReport {
554 contacts: Some(vec![fir::ContactInputReport {
555 contact_id: Some(1),
556 position_x: Some(10),
557 position_y: Some(20),
558 contact_width: Some(30),
559 contact_height: Some(80),
560 ..Default::default()
561 }]),
562 ..Default::default()
563 };
564 let events1 = converter.handle(&report1, time);
565 assert!(events1.iter().any(|e| e.code == uapi::ABS_MT_TOUCH_MAJOR as u16 && e.value == 80));
566 assert!(events1.iter().any(|e| e.code == uapi::ABS_MT_TOUCH_MINOR as u16 && e.value == 30));
567
568 let report2 = fir::TouchInputReport {
570 contacts: Some(vec![fir::ContactInputReport {
571 contact_id: Some(1),
572 position_x: Some(15),
573 position_y: Some(25),
574 contact_width: Some(100),
575 contact_height: Some(50),
576 ..Default::default()
577 }]),
578 ..Default::default()
579 };
580 let events2 = converter.handle(&report2, time);
581 assert!(
582 events2.iter().any(|e| e.code == uapi::ABS_MT_TOUCH_MAJOR as u16 && e.value == 100)
583 );
584 assert!(events2.iter().any(|e| e.code == uapi::ABS_MT_TOUCH_MINOR as u16 && e.value == 50));
585 }
586
587 #[::fuchsia::test]
588 fn test_touch_report_tracking_id_sign_guard() {
589 let mut converter = TouchReportConverter::new();
590 let time = zx::MonotonicInstant::from_nanos(100_000_000);
591
592 let report = fir::TouchInputReport {
594 contacts: Some(vec![fir::ContactInputReport {
595 contact_id: Some(u32::MAX),
596 position_x: Some(10),
597 position_y: Some(20),
598 ..Default::default()
599 }]),
600 ..Default::default()
601 };
602 let events = converter.handle(&report, time);
603 let tracking_id_event =
604 events.iter().find(|e| e.code == uapi::ABS_MT_TRACKING_ID as u16).unwrap();
605 assert_eq!(tracking_id_event.value, 0x7fff_ffff);
606 assert!(tracking_id_event.value >= 0);
607 assert_ne!(tracking_id_event.value, LIFTED_TRACKING_ID);
608 }
609
610 #[::fuchsia::test]
611 fn test_touch_report_duplicate_contact_filtered() {
612 let mut converter = TouchReportConverter::new();
613 let time = zx::MonotonicInstant::from_nanos(100_000_000);
614
615 let report = fir::TouchInputReport {
617 contacts: Some(vec![
618 fir::ContactInputReport {
619 contact_id: Some(1),
620 position_x: Some(10),
621 position_y: Some(20),
622 ..Default::default()
623 },
624 fir::ContactInputReport {
625 contact_id: Some(1),
626 position_x: Some(15),
627 position_y: Some(25),
628 ..Default::default()
629 },
630 ]),
631 ..Default::default()
632 };
633 let events = converter.handle(&report, time);
634 let slot_count = events.iter().filter(|e| e.code == uapi::ABS_MT_SLOT as u16).count();
636 assert_eq!(slot_count, 1);
637 let tracking_count =
638 events.iter().filter(|e| e.code == uapi::ABS_MT_TRACKING_ID as u16).count();
639 assert_eq!(tracking_count, 1);
640 assert!(events.iter().any(|e| e.code == uapi::ABS_MT_POSITION_X as u16 && e.value == 10));
641 assert!(!events.iter().any(|e| e.code == uapi::ABS_MT_POSITION_X as u16 && e.value == 15));
642 }
643
644 #[::fuchsia::test]
645 fn test_touch_report_missing_coordinates_does_not_lift() {
646 let mut converter = TouchReportConverter::new();
647 let time = zx::MonotonicInstant::from_nanos(100_000_000);
648
649 let report1 = fir::TouchInputReport {
651 contacts: Some(vec![fir::ContactInputReport {
652 contact_id: Some(1),
653 position_x: Some(10),
654 position_y: Some(20),
655 ..Default::default()
656 }]),
657 ..Default::default()
658 };
659 let events1 = converter.handle(&report1, time);
660 assert!(events1.iter().any(|e| e.code == uapi::BTN_TOUCH as u16 && e.value == 1));
661
662 let report2 = fir::TouchInputReport {
664 contacts: Some(vec![fir::ContactInputReport {
665 contact_id: Some(1),
666 position_x: None,
667 position_y: None,
668 ..Default::default()
669 }]),
670 ..Default::default()
671 };
672 let events2 = converter.handle(&report2, time);
673 assert!(
675 !events2.iter().any(|e| e.code == uapi::ABS_MT_TRACKING_ID as u16 && e.value == -1)
676 );
677 assert!(!events2.iter().any(|e| e.code == uapi::BTN_TOUCH as u16 && e.value == 0));
678
679 let report3 = fir::TouchInputReport { contacts: Some(vec![]), ..Default::default() };
681 let events3 = converter.handle(&report3, time);
682 assert!(events3.iter().any(|e| e.code == uapi::ABS_MT_TRACKING_ID as u16 && e.value == -1));
683 assert!(events3.iter().any(|e| e.code == uapi::BTN_TOUCH as u16 && e.value == 0));
684 }
685
686 #[::fuchsia::test]
687 fn test_touch_report_slot_exhaustion_and_reuse() {
688 let mut converter = TouchReportConverter::with_max_slots(2);
689 let time = zx::MonotonicInstant::from_nanos(100_000_000);
690
691 let report1 = fir::TouchInputReport {
693 contacts: Some(vec![
694 fir::ContactInputReport {
695 contact_id: Some(1),
696 position_x: Some(10),
697 position_y: Some(20),
698 ..Default::default()
699 },
700 fir::ContactInputReport {
701 contact_id: Some(2),
702 position_x: Some(30),
703 position_y: Some(40),
704 ..Default::default()
705 },
706 fir::ContactInputReport {
707 contact_id: Some(3),
708 position_x: Some(50),
709 position_y: Some(60),
710 ..Default::default()
711 },
712 ]),
713 ..Default::default()
714 };
715 let events1 = converter.handle(&report1, time);
716 assert!(events1.iter().any(|e| e.code == uapi::ABS_MT_SLOT as u16 && e.value == 0));
718 assert!(events1.iter().any(|e| e.code == uapi::ABS_MT_SLOT as u16 && e.value == 1));
719 assert!(!events1.iter().any(|e| e.code == uapi::ABS_MT_SLOT as u16 && e.value == 2));
720 assert!(!events1.iter().any(|e| e.code == uapi::ABS_MT_POSITION_X as u16 && e.value == 50));
721
722 let report2 = fir::TouchInputReport {
724 contacts: Some(vec![fir::ContactInputReport {
725 contact_id: Some(2),
726 position_x: Some(32),
727 position_y: Some(42),
728 ..Default::default()
729 }]),
730 ..Default::default()
731 };
732 let events2 = converter.handle(&report2, time);
733 assert!(events2.iter().any(|e| e.code == uapi::ABS_MT_SLOT as u16 && e.value == 0));
735 assert!(events2.iter().any(|e| e.code == uapi::ABS_MT_TRACKING_ID as u16 && e.value == -1));
736
737 let report3 = fir::TouchInputReport {
739 contacts: Some(vec![
740 fir::ContactInputReport {
741 contact_id: Some(2),
742 position_x: Some(35),
743 position_y: Some(45),
744 ..Default::default()
745 },
746 fir::ContactInputReport {
747 contact_id: Some(3),
748 position_x: Some(52),
749 position_y: Some(62),
750 ..Default::default()
751 },
752 ]),
753 ..Default::default()
754 };
755 let events3 = converter.handle(&report3, time);
756 assert!(events3.iter().any(|e| e.code == uapi::ABS_MT_SLOT as u16 && e.value == 0));
758 assert!(events3.iter().any(|e| e.code == uapi::ABS_MT_TRACKING_ID as u16 && e.value == 3));
759 assert!(events3.iter().any(|e| e.code == uapi::ABS_MT_POSITION_X as u16 && e.value == 52));
760 }
761
762 #[::fuchsia::test]
763 fn test_empty_touch_reports_return_empty_events() {
764 let time = zx::MonotonicInstant::from_nanos(400_000_000);
765 assert!(
766 TouchReportConverter::new().handle(&fir::TouchInputReport::default(), time).is_empty()
767 );
768 }
769
770 #[::fuchsia::test]
771 fn test_touch_report_clamping_out_of_range_axes() {
772 let mut converter = TouchReportConverter::new();
773 let time = zx::MonotonicInstant::from_nanos(100_000_000);
774 let report = fir::TouchInputReport {
775 contacts: Some(vec![fir::ContactInputReport {
776 contact_id: Some(1),
777 position_x: Some(i64::MAX),
778 position_y: Some(i64::MIN),
779 pressure: Some(i64::MAX),
780 contact_width: Some(i32::MAX as i64 + 100),
781 contact_height: Some(i64::MAX),
782 ..Default::default()
783 }]),
784 ..Default::default()
785 };
786 let events = converter.handle(&report, time);
787 assert!(
788 events.iter().any(|e| e.code == uapi::ABS_MT_POSITION_X as u16 && e.value == i32::MAX)
789 );
790 assert!(
791 events.iter().any(|e| e.code == uapi::ABS_MT_POSITION_Y as u16 && e.value == i32::MIN)
792 );
793 assert!(
794 events.iter().any(|e| e.code == uapi::ABS_MT_PRESSURE as u16 && e.value == i32::MAX)
795 );
796 assert!(
797 events.iter().any(|e| e.code == uapi::ABS_MT_TOUCH_MAJOR as u16 && e.value == i32::MAX)
798 );
799 assert!(
800 events.iter().any(|e| e.code == uapi::ABS_MT_TOUCH_MINOR as u16 && e.value == i32::MAX)
801 );
802 }
803
804 #[::fuchsia::test]
805 fn test_touch_report_deduplicate_unchanged_mt_axes() {
806 let mut converter = TouchReportConverter::new();
807 let time = zx::MonotonicInstant::from_nanos(100_000_000);
808
809 let report1 = fir::TouchInputReport {
811 contacts: Some(vec![fir::ContactInputReport {
812 contact_id: Some(1),
813 position_x: Some(10),
814 position_y: Some(20),
815 pressure: Some(30),
816 ..Default::default()
817 }]),
818 ..Default::default()
819 };
820 let events1 = converter.handle(&report1, time);
821 assert!(!events1.is_empty());
822 assert!(events1.iter().any(|e| e.code == uapi::ABS_MT_SLOT as u16 && e.value == 0));
823 assert!(events1.iter().any(|e| e.code == uapi::ABS_MT_POSITION_X as u16 && e.value == 10));
824 assert!(events1.iter().any(|e| e.code == uapi::ABS_MT_POSITION_Y as u16 && e.value == 20));
825 assert!(events1.iter().any(|e| e.code == uapi::ABS_MT_PRESSURE as u16 && e.value == 30));
826
827 let events2 = converter.handle(&report1, time);
829 assert!(events2.is_empty(), "Unchanged report should emit no events, got: {:?}", events2);
830
831 let report3 = fir::TouchInputReport {
833 contacts: Some(vec![fir::ContactInputReport {
834 contact_id: Some(1),
835 position_x: Some(15),
836 position_y: Some(20),
837 pressure: Some(30),
838 ..Default::default()
839 }]),
840 ..Default::default()
841 };
842 let events3 = converter.handle(&report3, time);
843 assert_eq!(
844 events3.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
845 vec![
846 (uapi::EV_ABS as u16, uapi::ABS_MT_SLOT as u16, 0),
847 (uapi::EV_ABS as u16, uapi::ABS_MT_POSITION_X as u16, 15),
848 (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
849 ]
850 );
851
852 let report4 = fir::TouchInputReport {
854 contacts: Some(vec![fir::ContactInputReport {
855 contact_id: Some(1),
856 position_x: Some(15),
857 position_y: Some(20),
858 pressure: Some(40),
859 ..Default::default()
860 }]),
861 ..Default::default()
862 };
863 let events4 = converter.handle(&report4, time);
864 assert_eq!(
865 events4.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
866 vec![
867 (uapi::EV_ABS as u16, uapi::ABS_MT_SLOT as u16, 0),
868 (uapi::EV_ABS as u16, uapi::ABS_MT_PRESSURE as u16, 40),
869 (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
870 ]
871 );
872 }
873
874 #[::fuchsia::test]
875 fn test_touch_report_empty_contacts_with_button_does_not_lift() {
876 let mut converter = TouchReportConverter::new();
877 let time = zx::MonotonicInstant::from_nanos(100_000_000);
878
879 let report_down = fir::TouchInputReport {
881 contacts: Some(vec![fir::ContactInputReport {
882 contact_id: Some(1),
883 position_x: Some(100),
884 position_y: Some(100),
885 ..Default::default()
886 }]),
887 ..Default::default()
888 };
889 let events1 = converter.handle(&report_down, time);
890 assert!(events1.iter().any(|e| e.code == uapi::BTN_TOUCH as u16 && e.value == 1));
891
892 let report_palm = fir::TouchInputReport {
894 contacts: Some(vec![]),
895 pressed_buttons: Some(vec![fir::TouchButton::Palm]),
896 ..Default::default()
897 };
898 let events2 = converter.handle(&report_palm, time);
899 assert!(events2.iter().any(|e| e.code == uapi::KEY_SLEEP as u16 && e.value == 1));
900 assert!(
902 !events2.iter().any(|e| e.code == uapi::ABS_MT_TRACKING_ID as u16 && e.value == -1)
903 );
904 assert!(!events2.iter().any(|e| e.code == uapi::BTN_TOUCH as u16 && e.value == 0));
905
906 let report_release_palm = fir::TouchInputReport {
908 contacts: Some(vec![]),
909 pressed_buttons: Some(vec![]),
910 ..Default::default()
911 };
912 let events3 = converter.handle(&report_release_palm, time);
913 assert!(events3.iter().any(|e| e.code == uapi::KEY_SLEEP as u16 && e.value == 0));
914 assert!(
916 !events3.iter().any(|e| e.code == uapi::ABS_MT_TRACKING_ID as u16 && e.value == -1)
917 );
918 assert!(!events3.iter().any(|e| e.code == uapi::BTN_TOUCH as u16 && e.value == 0));
919
920 let report_lift = fir::TouchInputReport {
922 contacts: Some(vec![]),
923 pressed_buttons: None,
924 ..Default::default()
925 };
926 let events4 = converter.handle(&report_lift, time);
927 assert!(events4.iter().any(|e| e.code == uapi::ABS_MT_TRACKING_ID as u16 && e.value == -1));
928 assert!(events4.iter().any(|e| e.code == uapi::BTN_TOUCH as u16 && e.value == 0));
929 }
930
931 #[::fuchsia::test]
932 fn test_touch_report_lift_while_button_remains_held() {
933 let mut converter = TouchReportConverter::new();
934 let time = zx::MonotonicInstant::from_nanos(100_000_000);
935
936 let report_down = fir::TouchInputReport {
938 contacts: Some(vec![fir::ContactInputReport {
939 contact_id: Some(1),
940 position_x: Some(100),
941 position_y: Some(100),
942 ..Default::default()
943 }]),
944 pressed_buttons: Some(vec![fir::TouchButton::Palm]),
945 ..Default::default()
946 };
947 let events1 = converter.handle(&report_down, time);
948 assert!(events1.iter().any(|e| e.code == uapi::BTN_TOUCH as u16 && e.value == 1));
949 assert!(events1.iter().any(|e| e.code == uapi::KEY_SLEEP as u16 && e.value == 1));
950
951 let report_lift = fir::TouchInputReport {
953 contacts: Some(vec![]),
954 pressed_buttons: Some(vec![fir::TouchButton::Palm]),
955 ..Default::default()
956 };
957 let events2 = converter.handle(&report_lift, time);
958 assert!(events2.iter().any(|e| e.code == uapi::ABS_MT_TRACKING_ID as u16 && e.value == -1));
960 assert!(events2.iter().any(|e| e.code == uapi::BTN_TOUCH as u16 && e.value == 0));
961 assert!(!events2.iter().any(|e| e.code == uapi::KEY_SLEEP as u16));
963 }
964
965 #[::fuchsia::test]
966 fn test_touch_report_lift_with_none_pressed_buttons_after_button_pressed() {
967 let mut converter = TouchReportConverter::new();
968 let time = zx::MonotonicInstant::from_nanos(100_000_000);
969
970 let report_down = fir::TouchInputReport {
972 contacts: Some(vec![fir::ContactInputReport {
973 contact_id: Some(1),
974 position_x: Some(100),
975 position_y: Some(100),
976 ..Default::default()
977 }]),
978 pressed_buttons: Some(vec![fir::TouchButton::Palm]),
979 ..Default::default()
980 };
981 let events1 = converter.handle(&report_down, time);
982 assert!(events1.iter().any(|e| e.code == uapi::BTN_TOUCH as u16 && e.value == 1));
983 assert!(events1.iter().any(|e| e.code == uapi::KEY_SLEEP as u16 && e.value == 1));
984
985 let report_lift = fir::TouchInputReport {
987 contacts: Some(vec![]),
988 pressed_buttons: None,
989 ..Default::default()
990 };
991 let events2 = converter.handle(&report_lift, time);
992 assert!(events2.iter().any(|e| e.code == uapi::ABS_MT_TRACKING_ID as u16 && e.value == -1));
994 assert!(events2.iter().any(|e| e.code == uapi::BTN_TOUCH as u16 && e.value == 0));
995 }
996
997 #[::fuchsia::test]
998 fn test_touch_report_omitted_optional_axes_preserve_cached_values() {
999 let mut converter = TouchReportConverter::new();
1000 let time = zx::MonotonicInstant::from_nanos(100_000_000);
1001
1002 let report1 = fir::TouchInputReport {
1004 contacts: Some(vec![fir::ContactInputReport {
1005 contact_id: Some(1),
1006 position_x: Some(10),
1007 position_y: Some(20),
1008 pressure: Some(40),
1009 contact_width: Some(30),
1010 contact_height: Some(15),
1011 ..Default::default()
1012 }]),
1013 ..Default::default()
1014 };
1015 let events1 = converter.handle(&report1, time);
1016 assert!(events1.iter().any(|e| e.code == uapi::ABS_MT_PRESSURE as u16 && e.value == 40));
1017 assert!(events1.iter().any(|e| e.code == uapi::ABS_MT_TOUCH_MAJOR as u16 && e.value == 30));
1018 assert!(events1.iter().any(|e| e.code == uapi::ABS_MT_TOUCH_MINOR as u16 && e.value == 15));
1019
1020 let report2 = fir::TouchInputReport {
1023 contacts: Some(vec![fir::ContactInputReport {
1024 contact_id: Some(1),
1025 position_x: Some(10),
1026 position_y: Some(20),
1027 pressure: None,
1028 contact_width: None,
1029 contact_height: None,
1030 ..Default::default()
1031 }]),
1032 ..Default::default()
1033 };
1034 let events2 = converter.handle(&report2, time);
1035 assert!(
1036 events2.is_empty(),
1037 "Expected no events when optional axes are omitted and coords unchanged, got: {:?}",
1038 events2
1039 );
1040
1041 let report3 = fir::TouchInputReport {
1044 contacts: Some(vec![fir::ContactInputReport {
1045 contact_id: Some(1),
1046 position_x: Some(15),
1047 position_y: Some(20),
1048 pressure: None,
1049 contact_width: None,
1050 contact_height: None,
1051 ..Default::default()
1052 }]),
1053 ..Default::default()
1054 };
1055 let events3 = converter.handle(&report3, time);
1056 assert_eq!(
1057 events3.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
1058 vec![
1059 (uapi::EV_ABS as u16, uapi::ABS_MT_SLOT as u16, 0),
1060 (uapi::EV_ABS as u16, uapi::ABS_MT_POSITION_X as u16, 15),
1061 (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
1062 ]
1063 );
1064
1065 let report4 = fir::TouchInputReport {
1068 contacts: Some(vec![fir::ContactInputReport {
1069 contact_id: Some(1),
1070 position_x: Some(15),
1071 position_y: Some(20),
1072 pressure: Some(40),
1073 contact_width: Some(30),
1074 contact_height: Some(15),
1075 ..Default::default()
1076 }]),
1077 ..Default::default()
1078 };
1079 let events4 = converter.handle(&report4, time);
1080 assert!(
1081 events4.is_empty(),
1082 "Expected no events when reporting matching cached optional axes, got: {:?}",
1083 events4
1084 );
1085 }
1086
1087 #[::fuchsia::test]
1088 fn test_touch_report_converter_from_descriptors() {
1089 let input_desc1 = fir::TouchInputDescriptor {
1091 max_contacts: Some(5),
1092 contacts: Some(vec![fir::ContactInputDescriptor::default(); 2]),
1093 ..Default::default()
1094 };
1095 let converter1 = TouchReportConverter::from_touch_input_descriptor(&input_desc1);
1096 assert_eq!(converter1.max_slots(), 5);
1097
1098 let input_desc2 = fir::TouchInputDescriptor {
1100 max_contacts: None,
1101 contacts: Some(vec![fir::ContactInputDescriptor::default(); 3]),
1102 ..Default::default()
1103 };
1104 let converter2 = TouchReportConverter::from_touch_input_descriptor(&input_desc2);
1105 assert_eq!(converter2.max_slots(), 3);
1106
1107 let input_desc_zero = fir::TouchInputDescriptor {
1109 max_contacts: Some(0),
1110 contacts: None,
1111 ..Default::default()
1112 };
1113 let converter_zero = TouchReportConverter::from_touch_input_descriptor(&input_desc_zero);
1114 assert_eq!(converter_zero.max_slots(), 1);
1115
1116 let input_desc_default = fir::TouchInputDescriptor::default();
1118 let converter_default =
1119 TouchReportConverter::from_touch_input_descriptor(&input_desc_default);
1120 assert_eq!(converter_default.max_slots(), DEFAULT_MAX_TOUCH_CONTACTS);
1121
1122 let touch_desc = fir::TouchDescriptor {
1124 input: Some(fir::TouchInputDescriptor { max_contacts: Some(8), ..Default::default() }),
1125 ..Default::default()
1126 };
1127 let converter3 = TouchReportConverter::from_touch_descriptor(&touch_desc);
1128 assert_eq!(converter3.max_slots(), 8);
1129
1130 let touch_desc_none = fir::TouchDescriptor::default();
1132 let converter4 = TouchReportConverter::from_touch_descriptor(&touch_desc_none);
1133 assert_eq!(converter4.max_slots(), DEFAULT_MAX_TOUCH_CONTACTS);
1134 }
1135}