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