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