1use crate::input_device::{
17 Handled, InputDeviceDescriptor, InputDeviceEvent, InputEvent, UnhandledInputEvent,
18};
19use crate::input_handler::{InputHandlerStatus, UnhandledInputHandler};
20use crate::keyboard_binding::{KeyboardDeviceDescriptor, KeyboardEvent};
21use async_trait::async_trait;
22use fidl_fuchsia_input::Key;
23use fidl_fuchsia_ui_input3::KeyEventType;
24use fuchsia_inspect::health::Reporter;
25use fuchsia_trace as ftrace;
26use keymaps::KeyState;
27use maplit::hashmap;
28use std::cell::RefCell;
29use std::collections::HashMap;
30use std::rc::Rc;
31use std::sync::LazyLock;
32
33const VENDOR_ID: u32 = 0x18d1; const PRODUCT_ID: u32 = 0x10003;
38
39const SEARCH_KEY: Key = Key::LeftMeta;
41
42#[derive(Debug)]
43struct KeyPair {
44 without_search: Key,
46 with_search: Key,
48}
49
50static REMAPPED_KEYS: LazyLock<HashMap<Key, KeyPair>> = LazyLock::new(|| {
54 hashmap! {
55 Key::F1 => KeyPair{ without_search: Key::AcBack, with_search: Key::F1 },
56 Key::F2 => KeyPair{ without_search: Key::AcRefresh, with_search: Key::F2},
57 Key::F3 => KeyPair{ without_search: Key::AcFullScreenView, with_search: Key::F3 },
58 Key::F4 => KeyPair{ without_search: Key::AcSelectTaskApplication, with_search: Key::F4 },
59 Key::F5 => KeyPair{ without_search: Key::BrightnessDown, with_search: Key::F5 },
60 Key::F6 => KeyPair{ without_search: Key::BrightnessUp, with_search: Key::F6 },
61 Key::F7 => KeyPair{ without_search: Key::PlayPause, with_search: Key::F7 },
62 Key::F8 => KeyPair{ without_search: Key::Mute, with_search: Key::F8 },
63 Key::F9 => KeyPair{ without_search: Key::VolumeDown, with_search: Key::F9 },
64 Key::F10 => KeyPair{ without_search: Key::VolumeUp, with_search: Key::F10 },
65 Key::Left => KeyPair{ without_search: Key::Left, with_search: Key::Home },
66 Key::Right => KeyPair{ without_search: Key::Right, with_search: Key::End },
67 Key::Up => KeyPair{ without_search: Key::Up, with_search: Key::PageUp },
68 Key::Down => KeyPair{ without_search: Key::Down, with_search: Key::PageDown },
69 Key::Dot => KeyPair{ without_search: Key::Dot, with_search: Key::Insert },
70 Key::Backspace => KeyPair{ without_search: Key::Backspace, with_search: Key::Delete },
71 }
72});
73
74#[derive(Debug, Default)]
78pub struct ChromebookKeyboardHandler {
79 state: RefCell<Inner>,
81
82 pub inspect_status: InputHandlerStatus,
84}
85
86#[derive(Debug, Default)]
87struct Inner {
88 key_state: KeyState,
91 is_search_key_actuated: bool,
93 other_key_events: bool,
96 next_event_time: zx::MonotonicInstant,
100 regular_keys_pressed: bool,
103}
104
105fn is_chromebook_keyboard(device_info: &fidl_fuchsia_input_report::DeviceInformation) -> bool {
107 device_info.product_id.unwrap_or_default() == PRODUCT_ID
108 && device_info.vendor_id.unwrap_or_default() == VENDOR_ID
109}
110
111#[async_trait(?Send)]
112impl UnhandledInputHandler for ChromebookKeyboardHandler {
113 async fn handle_unhandled_input_event(
114 self: Rc<Self>,
115 input_event: UnhandledInputEvent,
116 ) -> Vec<InputEvent> {
117 fuchsia_trace::duration!(c"input", c"chromebook_keyboard_handler");
118 match input_event.clone() {
119 UnhandledInputEvent {
121 device_event: InputDeviceEvent::Keyboard(event),
122 device_descriptor: InputDeviceDescriptor::Keyboard(ref keyboard_descriptor),
123 event_time,
124 trace_id,
125 } if is_chromebook_keyboard(&keyboard_descriptor.device_information) => {
126 fuchsia_trace::duration!(c"input", c"chromebook_keyboard_handler[processing]");
127 self.inspect_status.count_received_event(&event_time);
128 self.process_keyboard_event(
129 event,
130 keyboard_descriptor.clone(),
131 event_time,
132 trace_id,
133 )
134 }
135 _ => vec![InputEvent::from(input_event)],
137 }
138 }
139
140 fn set_handler_healthy(self: std::rc::Rc<Self>) {
141 self.inspect_status.health_node.borrow_mut().set_ok();
142 }
143
144 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
145 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
146 }
147}
148
149impl ChromebookKeyboardHandler {
150 pub fn new(input_handlers_node: &fuchsia_inspect::Node) -> Rc<Self> {
152 let inspect_status = InputHandlerStatus::new(
153 input_handlers_node,
154 "chromebook_keyboard_handler",
155 true,
156 );
157 Rc::new(Self { state: RefCell::new(Default::default()), inspect_status })
158 }
159
160 fn next_event_time(self: &Rc<Self>, event_time: zx::MonotonicInstant) -> zx::MonotonicInstant {
163 let proposed = self.state.borrow().next_event_time;
164 let returned = if event_time < proposed { proposed } else { event_time };
165 self.state.borrow_mut().next_event_time = returned + zx::MonotonicDuration::from_nanos(1);
166 returned
167 }
168
169 fn update_key_state(self: &Rc<Self>, event_type: KeyEventType, key: Key) {
171 if REMAPPED_KEYS.contains_key(&key) {
172 self.state.borrow_mut().key_state.update(event_type, key)
173 }
174 }
175
176 fn get_ordered_keys(self: &Rc<Self>) -> Vec<Key> {
178 self.state.borrow().key_state.get_ordered_keys()
179 }
180
181 fn is_search_key_actuated(self: &Rc<Self>) -> bool {
182 self.state.borrow().is_search_key_actuated
183 }
184
185 fn set_search_key_actuated(self: &Rc<Self>, value: bool) {
186 self.state.borrow_mut().is_search_key_actuated = value;
187 }
188
189 fn has_other_key_events(self: &Rc<Self>) -> bool {
190 self.state.borrow().other_key_events
191 }
192
193 fn set_other_key_events(self: &Rc<Self>, value: bool) {
194 self.state.borrow_mut().other_key_events = value;
195 }
196
197 fn is_regular_keys_pressed(self: &Rc<Self>) -> bool {
198 self.state.borrow().regular_keys_pressed
199 }
200
201 fn set_regular_keys_pressed(self: &Rc<Self>, value: bool) {
202 self.state.borrow_mut().regular_keys_pressed = value;
203 }
204
205 fn synthesize_input_events<'a, I: Iterator<Item = &'a Key>>(
206 self: &Rc<Self>,
207 event: KeyboardEvent,
208 event_type: KeyEventType,
209 descriptor: KeyboardDeviceDescriptor,
210 event_time: zx::MonotonicInstant,
211 trace_id: Option<ftrace::Id>,
212 keys: I,
213 mapfn: fn(&KeyPair) -> Key,
214 ) -> Vec<InputEvent> {
215 keys.map(|key| {
216 mapfn(REMAPPED_KEYS.get(key).expect("released_key must be in REMAPPED_KEYS"))
217 })
218 .map(|key| {
219 into_unhandled_input_event(
220 event.clone().into_with_key(key).into_with_event_type(event_type),
221 descriptor.clone(),
222 self.next_event_time(event_time),
223 trace_id,
224 )
225 })
226 .collect()
227 }
228
229 fn process_keyboard_event(
231 self: &Rc<Self>,
232 event: KeyboardEvent,
233 device_descriptor: KeyboardDeviceDescriptor,
234 event_time: zx::MonotonicInstant,
235 trace_id: Option<ftrace::Id>,
236 ) -> Vec<InputEvent> {
237 let is_search_key_actuated = self.is_search_key_actuated();
241
242 let key = event.get_key();
243 let event_type_folded = event.get_event_type_folded();
244 let event_type = event.get_event_type();
245
246 match is_search_key_actuated {
247 true => {
248 if key == SEARCH_KEY && event_type_folded == KeyEventType::Released {
251 let keys_to_release = self.get_ordered_keys();
253
254 let mut new_events = self.synthesize_input_events(
256 event.clone(),
257 KeyEventType::Released,
258 device_descriptor.clone(),
259 event_time,
260 None,
261 keys_to_release.iter().rev(),
262 |kp: &KeyPair| kp.with_search,
263 );
264 new_events.append(&mut self.synthesize_input_events(
265 event.clone(),
266 KeyEventType::Pressed,
267 device_descriptor.clone(),
268 event_time,
269 None,
270 keys_to_release.iter(),
271 |kp: &KeyPair| kp.without_search,
272 ));
273
274 let search_key_only =
281 !self.has_other_key_events() && event_type == KeyEventType::Released;
282 if search_key_only {
285 new_events.push(into_unhandled_input_event(
286 event.clone().into_with_event_type(KeyEventType::Pressed),
287 device_descriptor.clone(),
288 self.next_event_time(event_time),
289 None,
290 ));
291 }
292 if search_key_only || self.is_regular_keys_pressed() {
297 new_events.push(into_unhandled_input_event(
298 event.into_with_event_type(KeyEventType::Released),
299 device_descriptor,
300 self.next_event_time(event_time),
301 None,
302 ));
303 }
304
305 self.set_search_key_actuated(false);
307 self.set_other_key_events(false);
308 self.set_regular_keys_pressed(false);
309
310 return new_events;
311 } else {
312 }
314 }
315 false => {
316 if key == SEARCH_KEY && event_type == KeyEventType::Pressed {
317 let keys_to_release = self.get_ordered_keys();
319
320 let mut new_events = self.synthesize_input_events(
321 event.clone(),
322 KeyEventType::Released,
323 device_descriptor.clone(),
324 event_time,
325 None,
326 keys_to_release.iter().rev(),
327 |kp: &KeyPair| kp.without_search,
328 );
329 new_events.append(&mut self.synthesize_input_events(
330 event,
331 KeyEventType::Pressed,
332 device_descriptor,
333 event_time,
334 None,
335 keys_to_release.iter(),
336 |kp: &KeyPair| kp.with_search,
337 ));
338
339 self.set_search_key_actuated(true);
340 if !keys_to_release.is_empty() {
341 self.set_other_key_events(true);
342 }
343 return new_events;
344 }
345 }
346 }
347
348 self.update_key_state(event_type, key);
349 let maybe_remapped_key = REMAPPED_KEYS.get(&key);
350 let return_events = if let Some(remapped_keypair) = maybe_remapped_key {
351 let key = if is_search_key_actuated {
352 remapped_keypair.with_search
353 } else {
354 remapped_keypair.without_search
355 };
356 vec![into_unhandled_input_event(
357 event.into_with_key(key),
358 device_descriptor,
359 self.next_event_time(event_time),
360 trace_id,
361 )]
362 } else {
363 let mut events = vec![];
364 if self.is_search_key_actuated()
368 && !self.has_other_key_events()
369 && event_type == KeyEventType::Pressed
370 {
371 let new_event = event
372 .clone()
373 .into_with_key(SEARCH_KEY)
374 .into_with_event_type(KeyEventType::Pressed);
375 events.push(into_unhandled_input_event(
376 new_event,
377 device_descriptor.clone(),
378 self.next_event_time(event_time),
379 None,
380 ));
381 self.set_regular_keys_pressed(true);
382 }
383 events.push(into_unhandled_input_event(
384 event,
385 device_descriptor,
386 self.next_event_time(event_time),
387 trace_id,
388 ));
389 events
392 };
393
394 if event_type == KeyEventType::Pressed && key != SEARCH_KEY && is_search_key_actuated {
397 self.set_other_key_events(true);
398 }
399
400 return_events
401 }
402}
403
404fn into_unhandled_input_event(
405 event: KeyboardEvent,
406 device_descriptor: KeyboardDeviceDescriptor,
407 event_time: zx::MonotonicInstant,
408 trace_id: Option<ftrace::Id>,
409) -> InputEvent {
410 InputEvent {
411 device_event: InputDeviceEvent::Keyboard(event),
412 device_descriptor: device_descriptor.into(),
413 event_time,
414 handled: Handled::No,
415 trace_id,
416 }
417}
418
419#[cfg(test)]
420mod tests {
421 use super::*;
422 use crate::testing_utilities::create_input_event;
423 use std::sync::LazyLock;
424 use test_case::test_case;
425
426 static MATCHING_KEYBOARD_DESCRIPTOR: LazyLock<InputDeviceDescriptor> = LazyLock::new(|| {
427 InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
428 keys: vec![],
429 device_information: fidl_fuchsia_input_report::DeviceInformation {
430 vendor_id: Some(VENDOR_ID),
431 product_id: Some(PRODUCT_ID),
432 version: Some(42),
433 polling_rate: Some(1000),
434 ..Default::default()
435 },
436 device_id: 43,
437 })
438 });
439 static MISMATCHING_KEYBOARD_DESCRIPTOR: LazyLock<InputDeviceDescriptor> = LazyLock::new(|| {
440 InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
441 keys: vec![],
442 device_information: fidl_fuchsia_input_report::DeviceInformation {
443 vendor_id: Some(VENDOR_ID + 10),
444 product_id: Some(PRODUCT_ID),
445 version: Some(42),
446 polling_rate: Some(1000),
447 ..Default::default()
448 },
449 device_id: 43,
450 })
451 });
452
453 async fn run_all_events<T: UnhandledInputHandler>(
454 handler: &Rc<T>,
455 events: Vec<InputEvent>,
456 ) -> Vec<InputEvent> {
457 let handler_clone = || handler.clone();
458 let events_futs = events
459 .into_iter()
460 .map(|e| e.try_into().expect("events are always convertible in tests"))
461 .map(|e| handler_clone().handle_unhandled_input_event(e));
462 let mut events_set = vec![];
464 for events_fut in events_futs.into_iter() {
465 events_set.push(events_fut.await);
466 }
467 events_set.into_iter().flatten().collect()
468 }
469
470 fn new_key_sequence(
474 mut event_time: zx::MonotonicInstant,
475 descriptor: &InputDeviceDescriptor,
476 handled: Handled,
477 keys: Vec<(Key, KeyEventType)>,
478 ) -> Vec<InputEvent> {
479 let mut ret = vec![];
480 for (k, t) in keys {
481 ret.push(create_input_event(KeyboardEvent::new(k, t), descriptor, event_time, handled));
482 event_time = event_time + zx::MonotonicDuration::from_nanos(1);
483 }
484 ret
485 }
486
487 #[test]
488 fn next_event_time() {
489 let inspector = fuchsia_inspect::Inspector::default();
490 let test_node = inspector.root().create_child("test_node");
491 let handler = ChromebookKeyboardHandler::new(&test_node);
492 assert_eq!(
493 zx::MonotonicInstant::from_nanos(10),
494 handler.next_event_time(zx::MonotonicInstant::from_nanos(10))
495 );
496 assert_eq!(
497 zx::MonotonicInstant::from_nanos(11),
498 handler.next_event_time(zx::MonotonicInstant::from_nanos(10))
499 );
500 assert_eq!(
501 zx::MonotonicInstant::from_nanos(12),
502 handler.next_event_time(zx::MonotonicInstant::from_nanos(10))
503 );
504 assert_eq!(
505 zx::MonotonicInstant::from_nanos(13),
506 handler.next_event_time(zx::MonotonicInstant::from_nanos(13))
507 );
508 assert_eq!(
509 zx::MonotonicInstant::from_nanos(14),
510 handler.next_event_time(zx::MonotonicInstant::from_nanos(13))
511 );
512 }
513
514 #[test_case(Key::F1, Key::AcBack; "convert F1")]
517 #[test_case(Key::F2, Key::AcRefresh; "convert F2")]
518 #[test_case(Key::F3, Key::AcFullScreenView; "convert F3")]
519 #[test_case(Key::F4, Key::AcSelectTaskApplication; "convert F4")]
520 #[test_case(Key::F5, Key::BrightnessDown; "convert F5")]
521 #[test_case(Key::F6, Key::BrightnessUp; "convert F6")]
522 #[test_case(Key::F7, Key::PlayPause; "convert F7")]
523 #[test_case(Key::F8, Key::Mute; "convert F8")]
524 #[test_case(Key::F9, Key::VolumeDown; "convert F9")]
525 #[test_case(Key::F10, Key::VolumeUp; "convert F10")]
526 #[test_case(Key::A, Key::A; "do not convert A")]
527 #[test_case(Key::Up, Key::Up; "do not convert Up")]
528 #[test_case(Key::Down, Key::Down; "do not convert Down")]
529 #[test_case(Key::Left, Key::Left; "do not convert Left")]
530 #[test_case(Key::Right, Key::Right; "do not convert Right")]
531 #[test_case(Key::Dot, Key::Dot; "do not convert Dot")]
532 #[test_case(Key::Backspace, Key::Backspace; "do not convert Backspace")]
533 #[fuchsia::test]
534 async fn conversion_matching_keyboard(input_key: Key, output_key: Key) {
535 let inspector = fuchsia_inspect::Inspector::default();
536 let test_node = inspector.root().create_child("test_node");
537 let handler = ChromebookKeyboardHandler::new(&test_node);
538 let input = new_key_sequence(
539 zx::MonotonicInstant::from_nanos(42),
540 &MATCHING_KEYBOARD_DESCRIPTOR,
541 Handled::No,
542 vec![(input_key, KeyEventType::Pressed), (input_key, KeyEventType::Released)],
543 );
544 let actual = run_all_events(&handler, input).await;
545 let expected = new_key_sequence(
546 zx::MonotonicInstant::from_nanos(42),
547 &MATCHING_KEYBOARD_DESCRIPTOR,
548 Handled::No,
549 vec![(output_key, KeyEventType::Pressed), (output_key, KeyEventType::Released)],
550 );
551 pretty_assertions::assert_eq!(expected, actual);
552 }
553
554 #[test_case(Key::F1, Key::F1; "do not convert F1")]
557 #[test_case(Key::F2, Key::F2; "do not convert F2")]
558 #[test_case(Key::F3, Key::F3; "do not convert F3")]
559 #[test_case(Key::F4, Key::F4; "do not convert F4")]
560 #[test_case(Key::F5, Key::F5; "do not convert F5")]
561 #[test_case(Key::F6, Key::F6; "do not convert F6")]
562 #[test_case(Key::F7, Key::F7; "do not convert F7")]
563 #[test_case(Key::F8, Key::F8; "do not convert F8")]
564 #[test_case(Key::F9, Key::F9; "do not convert F9")]
565 #[test_case(Key::F10, Key::F10; "do not convert F10")]
566 #[test_case(Key::A, Key::A; "do not convert A")]
567 #[fuchsia::test]
568 async fn conversion_mismatching_keyboard(input_key: Key, output_key: Key) {
569 let inspector = fuchsia_inspect::Inspector::default();
570 let test_node = inspector.root().create_child("test_node");
571 let handler = ChromebookKeyboardHandler::new(&test_node);
572 let input = new_key_sequence(
573 zx::MonotonicInstant::from_nanos(42),
574 &MISMATCHING_KEYBOARD_DESCRIPTOR,
575 Handled::No,
576 vec![(input_key, KeyEventType::Pressed), (input_key, KeyEventType::Released)],
577 );
578 let actual = run_all_events(&handler, input).await;
579 let expected = new_key_sequence(
580 zx::MonotonicInstant::from_nanos(42),
581 &MISMATCHING_KEYBOARD_DESCRIPTOR,
582 Handled::No,
583 vec![(output_key, KeyEventType::Pressed), (output_key, KeyEventType::Released)],
584 );
585 pretty_assertions::assert_eq!(expected, actual);
586 }
587
588 #[fuchsia::test]
595 async fn search_key_only() {
596 let inspector = fuchsia_inspect::Inspector::default();
597 let test_node = inspector.root().create_child("test_node");
598 let handler = ChromebookKeyboardHandler::new(&test_node);
599 let input = new_key_sequence(
600 zx::MonotonicInstant::from_nanos(42),
601 &MATCHING_KEYBOARD_DESCRIPTOR,
602 Handled::No,
603 vec![(SEARCH_KEY, KeyEventType::Pressed), (SEARCH_KEY, KeyEventType::Released)],
604 );
605 let actual = run_all_events(&handler, input).await;
606 let expected = new_key_sequence(
607 zx::MonotonicInstant::from_nanos(43),
608 &MATCHING_KEYBOARD_DESCRIPTOR,
609 Handled::No,
610 vec![(SEARCH_KEY, KeyEventType::Pressed), (SEARCH_KEY, KeyEventType::Released)],
611 );
612 pretty_assertions::assert_eq!(expected, actual);
613 }
614
615 #[fuchsia::test]
624 async fn f1_conversion() {
625 let inspector = fuchsia_inspect::Inspector::default();
626 let test_node = inspector.root().create_child("test_node");
627 let handler = ChromebookKeyboardHandler::new(&test_node);
628 let input = new_key_sequence(
629 zx::MonotonicInstant::from_nanos(42),
630 &MATCHING_KEYBOARD_DESCRIPTOR,
631 Handled::No,
632 vec![
633 (SEARCH_KEY, KeyEventType::Pressed),
634 (Key::F1, KeyEventType::Pressed),
635 (Key::F1, KeyEventType::Released),
636 (SEARCH_KEY, KeyEventType::Released),
637 ],
638 );
639 let actual = run_all_events(&handler, input).await;
640 let expected = new_key_sequence(
641 zx::MonotonicInstant::from_nanos(43),
642 &MATCHING_KEYBOARD_DESCRIPTOR,
643 Handled::No,
644 vec![(Key::F1, KeyEventType::Pressed), (Key::F1, KeyEventType::Released)],
645 );
646 pretty_assertions::assert_eq!(expected, actual);
647 }
648
649 #[test_case(Key::F1, Key::F1; "do not convert F1")]
660 #[test_case(Key::F2, Key::F2; "do not convert F2")]
661 #[test_case(Key::F3, Key::F3; "do not convert F3")]
662 #[test_case(Key::F4, Key::F4; "do not convert F4")]
663 #[test_case(Key::F5, Key::F5; "do not convert F5")]
664 #[test_case(Key::F6, Key::F6; "do not convert F6")]
665 #[test_case(Key::F7, Key::F7; "do not convert F7")]
666 #[test_case(Key::F8, Key::F8; "do not convert F8")]
667 #[test_case(Key::F9, Key::F9; "do not convert F9")]
668 #[test_case(Key::F10, Key::F10; "do not convert F10")]
669 #[test_case(Key::Up, Key::PageUp; "convert Up")]
670 #[test_case(Key::Down, Key::PageDown; "convert Down")]
671 #[test_case(Key::Left, Key::Home; "convert Left")]
672 #[test_case(Key::Right, Key::End; "convert Right")]
673 #[test_case(Key::Dot, Key::Insert; "convert Dot")]
674 #[test_case(Key::Backspace, Key::Delete; "convert Backspace")]
675 #[fuchsia::test]
676 async fn with_search_key_pressed(input_key: Key, output_key: Key) {
677 let inspector = fuchsia_inspect::Inspector::default();
678 let test_node = inspector.root().create_child("test_node");
679 let handler = ChromebookKeyboardHandler::new(&test_node);
680 let input = new_key_sequence(
681 zx::MonotonicInstant::from_nanos(42),
682 &MATCHING_KEYBOARD_DESCRIPTOR,
683 Handled::No,
684 vec![
685 (SEARCH_KEY, KeyEventType::Pressed),
686 (input_key, KeyEventType::Pressed),
687 (input_key, KeyEventType::Released),
688 (SEARCH_KEY, KeyEventType::Released),
689 ],
690 );
691 let actual = run_all_events(&handler, input).await;
692 let expected = new_key_sequence(
693 zx::MonotonicInstant::from_nanos(43),
694 &MATCHING_KEYBOARD_DESCRIPTOR,
695 Handled::No,
696 vec![(output_key, KeyEventType::Pressed), (output_key, KeyEventType::Released)],
697 );
698 pretty_assertions::assert_eq!(expected, actual);
699 }
700
701 #[fuchsia::test]
708 async fn search_released_before_f1() {
709 let inspector = fuchsia_inspect::Inspector::default();
710 let test_node = inspector.root().create_child("test_node");
711 let handler = ChromebookKeyboardHandler::new(&test_node);
712 let input = new_key_sequence(
713 zx::MonotonicInstant::from_nanos(42),
714 &MATCHING_KEYBOARD_DESCRIPTOR,
715 Handled::No,
716 vec![
717 (SEARCH_KEY, KeyEventType::Pressed),
718 (Key::F1, KeyEventType::Pressed),
719 (SEARCH_KEY, KeyEventType::Released),
720 (Key::F1, KeyEventType::Released),
721 ],
722 );
723 let actual = run_all_events(&handler, input).await;
724 let expected = new_key_sequence(
725 zx::MonotonicInstant::from_nanos(43),
726 &MATCHING_KEYBOARD_DESCRIPTOR,
727 Handled::No,
728 vec![
729 (Key::F1, KeyEventType::Pressed),
730 (Key::F1, KeyEventType::Released),
731 (Key::AcBack, KeyEventType::Pressed),
732 (Key::AcBack, KeyEventType::Released),
733 ],
734 );
735 pretty_assertions::assert_eq!(expected, actual);
736 }
737
738 #[fuchsia::test]
747 async fn search_key_a_is_delayed_leftmeta_a() {
748 let inspector = fuchsia_inspect::Inspector::default();
749 let test_node = inspector.root().create_child("test_node");
750 let handler = ChromebookKeyboardHandler::new(&test_node);
751 let input = new_key_sequence(
752 zx::MonotonicInstant::from_nanos(42),
753 &MATCHING_KEYBOARD_DESCRIPTOR,
754 Handled::No,
755 vec![
756 (SEARCH_KEY, KeyEventType::Pressed),
757 (Key::A, KeyEventType::Pressed),
758 (Key::A, KeyEventType::Released),
759 (SEARCH_KEY, KeyEventType::Released),
760 ],
761 );
762 let actual = run_all_events(&handler, input).await;
763 let expected = new_key_sequence(
764 zx::MonotonicInstant::from_nanos(43),
765 &MATCHING_KEYBOARD_DESCRIPTOR,
766 Handled::No,
767 vec![
768 (Key::LeftMeta, KeyEventType::Pressed),
769 (Key::A, KeyEventType::Pressed),
770 (Key::A, KeyEventType::Released),
771 (Key::LeftMeta, KeyEventType::Released),
772 ],
773 );
774 pretty_assertions::assert_eq!(expected, actual);
775 }
776
777 #[fuchsia::test]
785 async fn f1_and_f2_interleaved_conversion() {
786 let inspector = fuchsia_inspect::Inspector::default();
787 let test_node = inspector.root().create_child("test_node");
788 let handler = ChromebookKeyboardHandler::new(&test_node);
789 let input = new_key_sequence(
790 zx::MonotonicInstant::from_nanos(42),
791 &MATCHING_KEYBOARD_DESCRIPTOR,
792 Handled::No,
793 vec![
794 (SEARCH_KEY, KeyEventType::Pressed),
795 (Key::F1, KeyEventType::Pressed),
796 (Key::F2, KeyEventType::Pressed),
797 (Key::F1, KeyEventType::Released),
798 (Key::F2, KeyEventType::Released),
799 (SEARCH_KEY, KeyEventType::Released),
800 ],
801 );
802 let actual = run_all_events(&handler, input).await;
803 let expected = new_key_sequence(
804 zx::MonotonicInstant::from_nanos(43),
805 &MATCHING_KEYBOARD_DESCRIPTOR,
806 Handled::No,
807 vec![
808 (Key::F1, KeyEventType::Pressed),
809 (Key::F2, KeyEventType::Pressed),
810 (Key::F1, KeyEventType::Released),
811 (Key::F2, KeyEventType::Released),
812 ],
813 );
814 pretty_assertions::assert_eq!(expected, actual);
815 }
816
817 #[fuchsia::test]
824 async fn search_pressed_before_f1_released() {
825 let inspector = fuchsia_inspect::Inspector::default();
826 let test_node = inspector.root().create_child("test_node");
827 let handler = ChromebookKeyboardHandler::new(&test_node);
828 let input = new_key_sequence(
829 zx::MonotonicInstant::from_nanos(42),
830 &MATCHING_KEYBOARD_DESCRIPTOR,
831 Handled::No,
832 vec![
833 (Key::F1, KeyEventType::Pressed),
834 (SEARCH_KEY, KeyEventType::Pressed),
835 (Key::F1, KeyEventType::Released),
836 (SEARCH_KEY, KeyEventType::Released),
837 ],
838 );
839 let actual = run_all_events(&handler, input).await;
840 let expected = new_key_sequence(
841 zx::MonotonicInstant::from_nanos(42),
842 &MATCHING_KEYBOARD_DESCRIPTOR,
843 Handled::No,
844 vec![
845 (Key::AcBack, KeyEventType::Pressed),
846 (Key::AcBack, KeyEventType::Released),
847 (Key::F1, KeyEventType::Pressed),
848 (Key::F1, KeyEventType::Released),
849 ],
850 );
851 pretty_assertions::assert_eq!(expected, actual);
852 }
853
854 #[fuchsia::test]
868 async fn search_pressed_while_f1_and_f2_pressed() {
869 let inspector = fuchsia_inspect::Inspector::default();
870 let test_node = inspector.root().create_child("test_node");
871 let handler = ChromebookKeyboardHandler::new(&test_node);
872 let input = new_key_sequence(
873 zx::MonotonicInstant::from_nanos(42),
874 &MATCHING_KEYBOARD_DESCRIPTOR,
875 Handled::No,
876 vec![
877 (Key::F1, KeyEventType::Pressed),
878 (Key::F2, KeyEventType::Pressed),
879 (SEARCH_KEY, KeyEventType::Pressed),
880 (Key::F1, KeyEventType::Released),
881 (Key::F2, KeyEventType::Released),
882 (SEARCH_KEY, KeyEventType::Released),
883 ],
884 );
885 let actual = run_all_events(&handler, input).await;
886 let expected = new_key_sequence(
887 zx::MonotonicInstant::from_nanos(42),
888 &MATCHING_KEYBOARD_DESCRIPTOR,
889 Handled::No,
890 vec![
891 (Key::AcBack, KeyEventType::Pressed),
892 (Key::AcRefresh, KeyEventType::Pressed),
893 (Key::AcRefresh, KeyEventType::Released),
894 (Key::AcBack, KeyEventType::Released),
895 (Key::F1, KeyEventType::Pressed),
896 (Key::F2, KeyEventType::Pressed),
897 (Key::F1, KeyEventType::Released),
898 (Key::F2, KeyEventType::Released),
899 ],
900 );
901 pretty_assertions::assert_eq!(expected, actual);
902 }
903
904 #[fuchsia::test]
920 async fn key_combination() {
921 let inspector = fuchsia_inspect::Inspector::default();
922 let test_node = inspector.root().create_child("test_node");
923 let handler = ChromebookKeyboardHandler::new(&test_node);
924 let input = new_key_sequence(
925 zx::MonotonicInstant::from_nanos(42),
926 &MATCHING_KEYBOARD_DESCRIPTOR,
927 Handled::No,
928 vec![
929 (Key::F1, KeyEventType::Pressed),
930 (SEARCH_KEY, KeyEventType::Pressed),
931 (Key::A, KeyEventType::Pressed),
932 (Key::F1, KeyEventType::Released),
933 (Key::F2, KeyEventType::Pressed),
934 (Key::A, KeyEventType::Released),
935 (SEARCH_KEY, KeyEventType::Released),
936 (Key::F2, KeyEventType::Released),
937 ],
938 );
939 let actual = run_all_events(&handler, input).await;
940 let expected = new_key_sequence(
941 zx::MonotonicInstant::from_nanos(42),
942 &MATCHING_KEYBOARD_DESCRIPTOR,
943 Handled::No,
944 vec![
945 (Key::AcBack, KeyEventType::Pressed),
946 (Key::AcBack, KeyEventType::Released),
947 (Key::F1, KeyEventType::Pressed),
948 (Key::A, KeyEventType::Pressed),
949 (Key::F1, KeyEventType::Released),
950 (Key::F2, KeyEventType::Pressed),
951 (Key::A, KeyEventType::Released),
952 (Key::F2, KeyEventType::Released),
953 (Key::AcRefresh, KeyEventType::Pressed),
954 (Key::AcRefresh, KeyEventType::Released),
955 ],
956 );
957 pretty_assertions::assert_eq!(expected, actual);
958 }
959
960 #[fuchsia::test]
961 async fn chromebook_keyboard_handler_initialized_with_inspect_node() {
962 let inspector = fuchsia_inspect::Inspector::default();
963 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
964 let _handler = ChromebookKeyboardHandler::new(&fake_handlers_node);
965 diagnostics_assertions::assert_data_tree!(inspector, root: {
966 input_handlers_node: {
967 chromebook_keyboard_handler: {
968 events_received_count: 0u64,
969 events_handled_count: 0u64,
970 last_received_timestamp_ns: 0u64,
971 "fuchsia.inspect.Health": {
972 status: "STARTING_UP",
973 start_timestamp_nanos: diagnostics_assertions::AnyProperty
976 },
977 }
978 }
979 });
980 }
981
982 #[fuchsia::test]
983 async fn chromebook_keyboard_handler_inspect_counts_events() {
984 let inspector = fuchsia_inspect::Inspector::default();
985 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
986 let handler = ChromebookKeyboardHandler::new(&fake_handlers_node);
987 let events = new_key_sequence(
988 zx::MonotonicInstant::from_nanos(42),
989 &MATCHING_KEYBOARD_DESCRIPTOR,
990 Handled::No,
991 vec![
992 (Key::F1, KeyEventType::Pressed),
993 (Key::F1, KeyEventType::Released),
994 (Key::Down, KeyEventType::Pressed),
995 (Key::Down, KeyEventType::Released),
996 ],
997 );
998 let _ = run_all_events(&handler, events).await;
999 diagnostics_assertions::assert_data_tree!(inspector, root: {
1000 input_handlers_node: {
1001 chromebook_keyboard_handler: {
1002 events_received_count: 4u64,
1003 events_handled_count: 0u64,
1004 last_received_timestamp_ns: 45u64,
1005 "fuchsia.inspect.Health": {
1006 status: "STARTING_UP",
1007 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1010 },
1011 }
1012 }
1013 });
1014 }
1015}