1use crate::input_device::{
17 Handled, InputDeviceDescriptor, InputDeviceEvent, InputEvent, InputEventType,
18 UnhandledInputEvent,
19};
20use crate::input_handler::{Handler, InputHandlerStatus, UnhandledInputHandler};
21use crate::keyboard_binding::{KeyboardDeviceDescriptor, KeyboardEvent};
22use async_trait::async_trait;
23use fidl_fuchsia_input::Key;
24use fidl_fuchsia_ui_input3::KeyEventType;
25use fuchsia_inspect::health::Reporter;
26use fuchsia_trace as ftrace;
27use keymaps::KeyState;
28use maplit::hashmap;
29use std::cell::RefCell;
30use std::collections::HashMap;
31use std::rc::Rc;
32use std::sync::LazyLock;
33
34const VENDOR_ID: u32 = 0x18d1; const PRODUCT_ID: u32 = 0x10003;
39
40const SEARCH_KEY: Key = Key::LeftMeta;
42
43#[derive(Debug)]
44struct KeyPair {
45 without_search: Key,
47 with_search: Key,
49}
50
51static REMAPPED_KEYS: LazyLock<HashMap<Key, KeyPair>> = LazyLock::new(|| {
55 hashmap! {
56 Key::F1 => KeyPair{ without_search: Key::AcBack, with_search: Key::F1 },
57 Key::F2 => KeyPair{ without_search: Key::AcRefresh, with_search: Key::F2},
58 Key::F3 => KeyPair{ without_search: Key::AcFullScreenView, with_search: Key::F3 },
59 Key::F4 => KeyPair{ without_search: Key::AcSelectTaskApplication, with_search: Key::F4 },
60 Key::F5 => KeyPair{ without_search: Key::BrightnessDown, with_search: Key::F5 },
61 Key::F6 => KeyPair{ without_search: Key::BrightnessUp, with_search: Key::F6 },
62 Key::F7 => KeyPair{ without_search: Key::PlayPause, with_search: Key::F7 },
63 Key::F8 => KeyPair{ without_search: Key::Mute, with_search: Key::F8 },
64 Key::F9 => KeyPair{ without_search: Key::VolumeDown, with_search: Key::F9 },
65 Key::F10 => KeyPair{ without_search: Key::VolumeUp, with_search: Key::F10 },
66 Key::Left => KeyPair{ without_search: Key::Left, with_search: Key::Home },
67 Key::Right => KeyPair{ without_search: Key::Right, with_search: Key::End },
68 Key::Up => KeyPair{ without_search: Key::Up, with_search: Key::PageUp },
69 Key::Down => KeyPair{ without_search: Key::Down, with_search: Key::PageDown },
70 Key::Dot => KeyPair{ without_search: Key::Dot, with_search: Key::Insert },
71 Key::Backspace => KeyPair{ without_search: Key::Backspace, with_search: Key::Delete },
72 }
73});
74
75#[derive(Debug, Default)]
79pub struct ChromebookKeyboardHandler {
80 state: RefCell<Inner>,
82
83 pub inspect_status: InputHandlerStatus,
85}
86
87#[derive(Debug, Default)]
88struct Inner {
89 key_state: KeyState,
92 is_search_key_actuated: bool,
94 other_key_events: bool,
97 next_event_time: zx::MonotonicInstant,
101 regular_keys_pressed: bool,
104}
105
106fn is_chromebook_keyboard(device_info: &fidl_fuchsia_input_report::DeviceInformation) -> bool {
108 device_info.product_id.unwrap_or_default() == PRODUCT_ID
109 && device_info.vendor_id.unwrap_or_default() == VENDOR_ID
110}
111
112impl Handler for ChromebookKeyboardHandler {
113 fn set_handler_healthy(self: std::rc::Rc<Self>) {
114 self.inspect_status.health_node.borrow_mut().set_ok();
115 }
116
117 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
118 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
119 }
120
121 fn get_name(&self) -> &'static str {
122 "ChromebookKeyboardHandler"
123 }
124
125 fn interest(&self) -> Vec<InputEventType> {
126 vec![InputEventType::Keyboard]
127 }
128}
129
130#[async_trait(?Send)]
131impl UnhandledInputHandler for ChromebookKeyboardHandler {
132 async fn handle_unhandled_input_event(
133 self: Rc<Self>,
134 input_event: UnhandledInputEvent,
135 ) -> Vec<InputEvent> {
136 fuchsia_trace::duration!("input", "chromebook_keyboard_handler");
137 match input_event {
138 UnhandledInputEvent {
140 device_event: InputDeviceEvent::Keyboard(event),
141 device_descriptor: InputDeviceDescriptor::Keyboard(ref keyboard_descriptor),
142 event_time,
143 trace_id,
144 } if is_chromebook_keyboard(&keyboard_descriptor.device_information) => {
145 fuchsia_trace::duration!("input", "chromebook_keyboard_handler[processing]");
146 self.inspect_status.count_received_event(&event_time);
147 self.process_keyboard_event(
148 event,
149 keyboard_descriptor.clone(),
150 event_time,
151 trace_id,
152 )
153 }
154 _ => {
156 log::warn!("Unhandled input event: {:?}", input_event.get_event_type());
158 vec![InputEvent::from(input_event)]
159 }
160 }
161 }
162}
163
164impl ChromebookKeyboardHandler {
165 pub fn new(input_handlers_node: &fuchsia_inspect::Node) -> Rc<Self> {
167 let inspect_status = InputHandlerStatus::new(
168 input_handlers_node,
169 "chromebook_keyboard_handler",
170 true,
171 );
172 Rc::new(Self { state: RefCell::new(Default::default()), inspect_status })
173 }
174
175 fn next_event_time(self: &Rc<Self>, event_time: zx::MonotonicInstant) -> zx::MonotonicInstant {
178 let proposed = self.state.borrow().next_event_time;
179 let returned = if event_time < proposed { proposed } else { event_time };
180 self.state.borrow_mut().next_event_time = returned + zx::MonotonicDuration::from_nanos(1);
181 returned
182 }
183
184 fn update_key_state(self: &Rc<Self>, event_type: KeyEventType, key: Key) {
186 if REMAPPED_KEYS.contains_key(&key) {
187 self.state.borrow_mut().key_state.update(event_type, key)
188 }
189 }
190
191 fn get_ordered_keys(self: &Rc<Self>) -> Vec<Key> {
193 self.state.borrow().key_state.get_ordered_keys()
194 }
195
196 fn is_search_key_actuated(self: &Rc<Self>) -> bool {
197 self.state.borrow().is_search_key_actuated
198 }
199
200 fn set_search_key_actuated(self: &Rc<Self>, value: bool) {
201 self.state.borrow_mut().is_search_key_actuated = value;
202 }
203
204 fn has_other_key_events(self: &Rc<Self>) -> bool {
205 self.state.borrow().other_key_events
206 }
207
208 fn set_other_key_events(self: &Rc<Self>, value: bool) {
209 self.state.borrow_mut().other_key_events = value;
210 }
211
212 fn is_regular_keys_pressed(self: &Rc<Self>) -> bool {
213 self.state.borrow().regular_keys_pressed
214 }
215
216 fn set_regular_keys_pressed(self: &Rc<Self>, value: bool) {
217 self.state.borrow_mut().regular_keys_pressed = value;
218 }
219
220 fn synthesize_input_events<'a, I: Iterator<Item = &'a Key>>(
221 self: &Rc<Self>,
222 event: KeyboardEvent,
223 event_type: KeyEventType,
224 descriptor: KeyboardDeviceDescriptor,
225 event_time: zx::MonotonicInstant,
226 trace_id: Option<ftrace::Id>,
227 keys: I,
228 mapfn: fn(&KeyPair) -> Key,
229 ) -> Vec<InputEvent> {
230 keys.map(|key| {
231 mapfn(REMAPPED_KEYS.get(key).expect("released_key must be in REMAPPED_KEYS"))
232 })
233 .map(|key| {
234 into_unhandled_input_event(
235 event.clone().into_with_key(key).into_with_event_type(event_type),
236 descriptor.clone(),
237 self.next_event_time(event_time),
238 trace_id,
239 )
240 })
241 .collect()
242 }
243
244 fn process_keyboard_event(
246 self: &Rc<Self>,
247 event: KeyboardEvent,
248 device_descriptor: KeyboardDeviceDescriptor,
249 event_time: zx::MonotonicInstant,
250 trace_id: Option<ftrace::Id>,
251 ) -> Vec<InputEvent> {
252 let is_search_key_actuated = self.is_search_key_actuated();
256
257 let key = event.get_key();
258 let event_type_folded = event.get_event_type_folded();
259 let event_type = event.get_event_type();
260
261 match is_search_key_actuated {
262 true => {
263 if key == SEARCH_KEY && event_type_folded == KeyEventType::Released {
266 let keys_to_release = self.get_ordered_keys();
268
269 let mut new_events = self.synthesize_input_events(
271 event.clone(),
272 KeyEventType::Released,
273 device_descriptor.clone(),
274 event_time,
275 None,
276 keys_to_release.iter().rev(),
277 |kp: &KeyPair| kp.with_search,
278 );
279 new_events.append(&mut self.synthesize_input_events(
280 event.clone(),
281 KeyEventType::Pressed,
282 device_descriptor.clone(),
283 event_time,
284 None,
285 keys_to_release.iter(),
286 |kp: &KeyPair| kp.without_search,
287 ));
288
289 let search_key_only =
296 !self.has_other_key_events() && event_type == KeyEventType::Released;
297 if search_key_only {
300 new_events.push(into_unhandled_input_event(
301 event.clone().into_with_event_type(KeyEventType::Pressed),
302 device_descriptor.clone(),
303 self.next_event_time(event_time),
304 None,
305 ));
306 }
307 if search_key_only || self.is_regular_keys_pressed() {
312 new_events.push(into_unhandled_input_event(
313 event.into_with_event_type(KeyEventType::Released),
314 device_descriptor,
315 self.next_event_time(event_time),
316 None,
317 ));
318 }
319
320 self.set_search_key_actuated(false);
322 self.set_other_key_events(false);
323 self.set_regular_keys_pressed(false);
324
325 return new_events;
326 } else {
327 }
329 }
330 false => {
331 if key == SEARCH_KEY && event_type == KeyEventType::Pressed {
332 let keys_to_release = self.get_ordered_keys();
334
335 let mut new_events = self.synthesize_input_events(
336 event.clone(),
337 KeyEventType::Released,
338 device_descriptor.clone(),
339 event_time,
340 None,
341 keys_to_release.iter().rev(),
342 |kp: &KeyPair| kp.without_search,
343 );
344 new_events.append(&mut self.synthesize_input_events(
345 event,
346 KeyEventType::Pressed,
347 device_descriptor,
348 event_time,
349 None,
350 keys_to_release.iter(),
351 |kp: &KeyPair| kp.with_search,
352 ));
353
354 self.set_search_key_actuated(true);
355 if !keys_to_release.is_empty() {
356 self.set_other_key_events(true);
357 }
358 return new_events;
359 }
360 }
361 }
362
363 self.update_key_state(event_type, key);
364 let maybe_remapped_key = REMAPPED_KEYS.get(&key);
365 let return_events = if let Some(remapped_keypair) = maybe_remapped_key {
366 let key = if is_search_key_actuated {
367 remapped_keypair.with_search
368 } else {
369 remapped_keypair.without_search
370 };
371 vec![into_unhandled_input_event(
372 event.into_with_key(key),
373 device_descriptor,
374 self.next_event_time(event_time),
375 trace_id,
376 )]
377 } else {
378 let mut events = vec![];
379 if self.is_search_key_actuated()
383 && !self.has_other_key_events()
384 && event_type == KeyEventType::Pressed
385 {
386 let new_event = event
387 .clone()
388 .into_with_key(SEARCH_KEY)
389 .into_with_event_type(KeyEventType::Pressed);
390 events.push(into_unhandled_input_event(
391 new_event,
392 device_descriptor.clone(),
393 self.next_event_time(event_time),
394 None,
395 ));
396 self.set_regular_keys_pressed(true);
397 }
398 events.push(into_unhandled_input_event(
399 event,
400 device_descriptor,
401 self.next_event_time(event_time),
402 trace_id,
403 ));
404 events
407 };
408
409 if event_type == KeyEventType::Pressed && key != SEARCH_KEY && is_search_key_actuated {
412 self.set_other_key_events(true);
413 }
414
415 return_events
416 }
417}
418
419fn into_unhandled_input_event(
420 event: KeyboardEvent,
421 device_descriptor: KeyboardDeviceDescriptor,
422 event_time: zx::MonotonicInstant,
423 trace_id: Option<ftrace::Id>,
424) -> InputEvent {
425 InputEvent {
426 device_event: InputDeviceEvent::Keyboard(event),
427 device_descriptor: device_descriptor.into(),
428 event_time,
429 handled: Handled::No,
430 trace_id,
431 }
432}
433
434#[cfg(test)]
435mod tests {
436 use super::*;
437 use crate::testing_utilities::create_input_event;
438 use std::sync::LazyLock;
439 use test_case::test_case;
440
441 static MATCHING_KEYBOARD_DESCRIPTOR: LazyLock<InputDeviceDescriptor> = LazyLock::new(|| {
442 InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
443 keys: vec![],
444 device_information: fidl_fuchsia_input_report::DeviceInformation {
445 vendor_id: Some(VENDOR_ID),
446 product_id: Some(PRODUCT_ID),
447 version: Some(42),
448 polling_rate: Some(1000),
449 ..Default::default()
450 },
451 device_id: 43,
452 })
453 });
454 static MISMATCHING_KEYBOARD_DESCRIPTOR: LazyLock<InputDeviceDescriptor> = LazyLock::new(|| {
455 InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
456 keys: vec![],
457 device_information: fidl_fuchsia_input_report::DeviceInformation {
458 vendor_id: Some(VENDOR_ID + 10),
459 product_id: Some(PRODUCT_ID),
460 version: Some(42),
461 polling_rate: Some(1000),
462 ..Default::default()
463 },
464 device_id: 43,
465 })
466 });
467
468 async fn run_all_events<T: UnhandledInputHandler>(
469 handler: &Rc<T>,
470 events: Vec<InputEvent>,
471 ) -> Vec<InputEvent> {
472 let handler_clone = || handler.clone();
473 let events_futs = events
474 .into_iter()
475 .map(|e| e.try_into().expect("events are always convertible in tests"))
476 .map(|e| handler_clone().handle_unhandled_input_event(e));
477 let mut events_set = vec![];
479 for events_fut in events_futs.into_iter() {
480 events_set.push(events_fut.await);
481 }
482 events_set.into_iter().flatten().collect()
483 }
484
485 fn new_key_sequence(
489 mut event_time: zx::MonotonicInstant,
490 descriptor: &InputDeviceDescriptor,
491 handled: Handled,
492 keys: Vec<(Key, KeyEventType)>,
493 ) -> Vec<InputEvent> {
494 let mut ret = vec![];
495 for (k, t) in keys {
496 ret.push(create_input_event(KeyboardEvent::new(k, t), descriptor, event_time, handled));
497 event_time = event_time + zx::MonotonicDuration::from_nanos(1);
498 }
499 ret
500 }
501
502 #[test]
503 fn next_event_time() {
504 let inspector = fuchsia_inspect::Inspector::default();
505 let test_node = inspector.root().create_child("test_node");
506 let handler = ChromebookKeyboardHandler::new(&test_node);
507 assert_eq!(
508 zx::MonotonicInstant::from_nanos(10),
509 handler.next_event_time(zx::MonotonicInstant::from_nanos(10))
510 );
511 assert_eq!(
512 zx::MonotonicInstant::from_nanos(11),
513 handler.next_event_time(zx::MonotonicInstant::from_nanos(10))
514 );
515 assert_eq!(
516 zx::MonotonicInstant::from_nanos(12),
517 handler.next_event_time(zx::MonotonicInstant::from_nanos(10))
518 );
519 assert_eq!(
520 zx::MonotonicInstant::from_nanos(13),
521 handler.next_event_time(zx::MonotonicInstant::from_nanos(13))
522 );
523 assert_eq!(
524 zx::MonotonicInstant::from_nanos(14),
525 handler.next_event_time(zx::MonotonicInstant::from_nanos(13))
526 );
527 }
528
529 #[test_case(Key::F1, Key::AcBack; "convert F1")]
532 #[test_case(Key::F2, Key::AcRefresh; "convert F2")]
533 #[test_case(Key::F3, Key::AcFullScreenView; "convert F3")]
534 #[test_case(Key::F4, Key::AcSelectTaskApplication; "convert F4")]
535 #[test_case(Key::F5, Key::BrightnessDown; "convert F5")]
536 #[test_case(Key::F6, Key::BrightnessUp; "convert F6")]
537 #[test_case(Key::F7, Key::PlayPause; "convert F7")]
538 #[test_case(Key::F8, Key::Mute; "convert F8")]
539 #[test_case(Key::F9, Key::VolumeDown; "convert F9")]
540 #[test_case(Key::F10, Key::VolumeUp; "convert F10")]
541 #[test_case(Key::A, Key::A; "do not convert A")]
542 #[test_case(Key::Up, Key::Up; "do not convert Up")]
543 #[test_case(Key::Down, Key::Down; "do not convert Down")]
544 #[test_case(Key::Left, Key::Left; "do not convert Left")]
545 #[test_case(Key::Right, Key::Right; "do not convert Right")]
546 #[test_case(Key::Dot, Key::Dot; "do not convert Dot")]
547 #[test_case(Key::Backspace, Key::Backspace; "do not convert Backspace")]
548 #[fuchsia::test]
549 async fn conversion_matching_keyboard(input_key: Key, output_key: Key) {
550 let inspector = fuchsia_inspect::Inspector::default();
551 let test_node = inspector.root().create_child("test_node");
552 let handler = ChromebookKeyboardHandler::new(&test_node);
553 let input = new_key_sequence(
554 zx::MonotonicInstant::from_nanos(42),
555 &MATCHING_KEYBOARD_DESCRIPTOR,
556 Handled::No,
557 vec![(input_key, KeyEventType::Pressed), (input_key, KeyEventType::Released)],
558 );
559 let actual = run_all_events(&handler, input).await;
560 let expected = new_key_sequence(
561 zx::MonotonicInstant::from_nanos(42),
562 &MATCHING_KEYBOARD_DESCRIPTOR,
563 Handled::No,
564 vec![(output_key, KeyEventType::Pressed), (output_key, KeyEventType::Released)],
565 );
566 pretty_assertions::assert_eq!(expected, actual);
567 }
568
569 #[test_case(Key::F1, Key::F1; "do not convert F1")]
572 #[test_case(Key::F2, Key::F2; "do not convert F2")]
573 #[test_case(Key::F3, Key::F3; "do not convert F3")]
574 #[test_case(Key::F4, Key::F4; "do not convert F4")]
575 #[test_case(Key::F5, Key::F5; "do not convert F5")]
576 #[test_case(Key::F6, Key::F6; "do not convert F6")]
577 #[test_case(Key::F7, Key::F7; "do not convert F7")]
578 #[test_case(Key::F8, Key::F8; "do not convert F8")]
579 #[test_case(Key::F9, Key::F9; "do not convert F9")]
580 #[test_case(Key::F10, Key::F10; "do not convert F10")]
581 #[test_case(Key::A, Key::A; "do not convert A")]
582 #[fuchsia::test]
583 async fn conversion_mismatching_keyboard(input_key: Key, output_key: Key) {
584 let inspector = fuchsia_inspect::Inspector::default();
585 let test_node = inspector.root().create_child("test_node");
586 let handler = ChromebookKeyboardHandler::new(&test_node);
587 let input = new_key_sequence(
588 zx::MonotonicInstant::from_nanos(42),
589 &MISMATCHING_KEYBOARD_DESCRIPTOR,
590 Handled::No,
591 vec![(input_key, KeyEventType::Pressed), (input_key, KeyEventType::Released)],
592 );
593 let actual = run_all_events(&handler, input).await;
594 let expected = new_key_sequence(
595 zx::MonotonicInstant::from_nanos(42),
596 &MISMATCHING_KEYBOARD_DESCRIPTOR,
597 Handled::No,
598 vec![(output_key, KeyEventType::Pressed), (output_key, KeyEventType::Released)],
599 );
600 pretty_assertions::assert_eq!(expected, actual);
601 }
602
603 #[fuchsia::test]
610 async fn search_key_only() {
611 let inspector = fuchsia_inspect::Inspector::default();
612 let test_node = inspector.root().create_child("test_node");
613 let handler = ChromebookKeyboardHandler::new(&test_node);
614 let input = new_key_sequence(
615 zx::MonotonicInstant::from_nanos(42),
616 &MATCHING_KEYBOARD_DESCRIPTOR,
617 Handled::No,
618 vec![(SEARCH_KEY, KeyEventType::Pressed), (SEARCH_KEY, KeyEventType::Released)],
619 );
620 let actual = run_all_events(&handler, input).await;
621 let expected = new_key_sequence(
622 zx::MonotonicInstant::from_nanos(43),
623 &MATCHING_KEYBOARD_DESCRIPTOR,
624 Handled::No,
625 vec![(SEARCH_KEY, KeyEventType::Pressed), (SEARCH_KEY, KeyEventType::Released)],
626 );
627 pretty_assertions::assert_eq!(expected, actual);
628 }
629
630 #[fuchsia::test]
639 async fn f1_conversion() {
640 let inspector = fuchsia_inspect::Inspector::default();
641 let test_node = inspector.root().create_child("test_node");
642 let handler = ChromebookKeyboardHandler::new(&test_node);
643 let input = new_key_sequence(
644 zx::MonotonicInstant::from_nanos(42),
645 &MATCHING_KEYBOARD_DESCRIPTOR,
646 Handled::No,
647 vec![
648 (SEARCH_KEY, KeyEventType::Pressed),
649 (Key::F1, KeyEventType::Pressed),
650 (Key::F1, KeyEventType::Released),
651 (SEARCH_KEY, KeyEventType::Released),
652 ],
653 );
654 let actual = run_all_events(&handler, input).await;
655 let expected = new_key_sequence(
656 zx::MonotonicInstant::from_nanos(43),
657 &MATCHING_KEYBOARD_DESCRIPTOR,
658 Handled::No,
659 vec![(Key::F1, KeyEventType::Pressed), (Key::F1, KeyEventType::Released)],
660 );
661 pretty_assertions::assert_eq!(expected, actual);
662 }
663
664 #[test_case(Key::F1, Key::F1; "do not convert F1")]
675 #[test_case(Key::F2, Key::F2; "do not convert F2")]
676 #[test_case(Key::F3, Key::F3; "do not convert F3")]
677 #[test_case(Key::F4, Key::F4; "do not convert F4")]
678 #[test_case(Key::F5, Key::F5; "do not convert F5")]
679 #[test_case(Key::F6, Key::F6; "do not convert F6")]
680 #[test_case(Key::F7, Key::F7; "do not convert F7")]
681 #[test_case(Key::F8, Key::F8; "do not convert F8")]
682 #[test_case(Key::F9, Key::F9; "do not convert F9")]
683 #[test_case(Key::F10, Key::F10; "do not convert F10")]
684 #[test_case(Key::Up, Key::PageUp; "convert Up")]
685 #[test_case(Key::Down, Key::PageDown; "convert Down")]
686 #[test_case(Key::Left, Key::Home; "convert Left")]
687 #[test_case(Key::Right, Key::End; "convert Right")]
688 #[test_case(Key::Dot, Key::Insert; "convert Dot")]
689 #[test_case(Key::Backspace, Key::Delete; "convert Backspace")]
690 #[fuchsia::test]
691 async fn with_search_key_pressed(input_key: Key, output_key: Key) {
692 let inspector = fuchsia_inspect::Inspector::default();
693 let test_node = inspector.root().create_child("test_node");
694 let handler = ChromebookKeyboardHandler::new(&test_node);
695 let input = new_key_sequence(
696 zx::MonotonicInstant::from_nanos(42),
697 &MATCHING_KEYBOARD_DESCRIPTOR,
698 Handled::No,
699 vec![
700 (SEARCH_KEY, KeyEventType::Pressed),
701 (input_key, KeyEventType::Pressed),
702 (input_key, KeyEventType::Released),
703 (SEARCH_KEY, KeyEventType::Released),
704 ],
705 );
706 let actual = run_all_events(&handler, input).await;
707 let expected = new_key_sequence(
708 zx::MonotonicInstant::from_nanos(43),
709 &MATCHING_KEYBOARD_DESCRIPTOR,
710 Handled::No,
711 vec![(output_key, KeyEventType::Pressed), (output_key, KeyEventType::Released)],
712 );
713 pretty_assertions::assert_eq!(expected, actual);
714 }
715
716 #[fuchsia::test]
723 async fn search_released_before_f1() {
724 let inspector = fuchsia_inspect::Inspector::default();
725 let test_node = inspector.root().create_child("test_node");
726 let handler = ChromebookKeyboardHandler::new(&test_node);
727 let input = new_key_sequence(
728 zx::MonotonicInstant::from_nanos(42),
729 &MATCHING_KEYBOARD_DESCRIPTOR,
730 Handled::No,
731 vec![
732 (SEARCH_KEY, KeyEventType::Pressed),
733 (Key::F1, KeyEventType::Pressed),
734 (SEARCH_KEY, KeyEventType::Released),
735 (Key::F1, KeyEventType::Released),
736 ],
737 );
738 let actual = run_all_events(&handler, input).await;
739 let expected = new_key_sequence(
740 zx::MonotonicInstant::from_nanos(43),
741 &MATCHING_KEYBOARD_DESCRIPTOR,
742 Handled::No,
743 vec![
744 (Key::F1, KeyEventType::Pressed),
745 (Key::F1, KeyEventType::Released),
746 (Key::AcBack, KeyEventType::Pressed),
747 (Key::AcBack, KeyEventType::Released),
748 ],
749 );
750 pretty_assertions::assert_eq!(expected, actual);
751 }
752
753 #[fuchsia::test]
762 async fn search_key_a_is_delayed_leftmeta_a() {
763 let inspector = fuchsia_inspect::Inspector::default();
764 let test_node = inspector.root().create_child("test_node");
765 let handler = ChromebookKeyboardHandler::new(&test_node);
766 let input = new_key_sequence(
767 zx::MonotonicInstant::from_nanos(42),
768 &MATCHING_KEYBOARD_DESCRIPTOR,
769 Handled::No,
770 vec![
771 (SEARCH_KEY, KeyEventType::Pressed),
772 (Key::A, KeyEventType::Pressed),
773 (Key::A, KeyEventType::Released),
774 (SEARCH_KEY, KeyEventType::Released),
775 ],
776 );
777 let actual = run_all_events(&handler, input).await;
778 let expected = new_key_sequence(
779 zx::MonotonicInstant::from_nanos(43),
780 &MATCHING_KEYBOARD_DESCRIPTOR,
781 Handled::No,
782 vec![
783 (Key::LeftMeta, KeyEventType::Pressed),
784 (Key::A, KeyEventType::Pressed),
785 (Key::A, KeyEventType::Released),
786 (Key::LeftMeta, KeyEventType::Released),
787 ],
788 );
789 pretty_assertions::assert_eq!(expected, actual);
790 }
791
792 #[fuchsia::test]
800 async fn f1_and_f2_interleaved_conversion() {
801 let inspector = fuchsia_inspect::Inspector::default();
802 let test_node = inspector.root().create_child("test_node");
803 let handler = ChromebookKeyboardHandler::new(&test_node);
804 let input = new_key_sequence(
805 zx::MonotonicInstant::from_nanos(42),
806 &MATCHING_KEYBOARD_DESCRIPTOR,
807 Handled::No,
808 vec![
809 (SEARCH_KEY, KeyEventType::Pressed),
810 (Key::F1, KeyEventType::Pressed),
811 (Key::F2, KeyEventType::Pressed),
812 (Key::F1, KeyEventType::Released),
813 (Key::F2, KeyEventType::Released),
814 (SEARCH_KEY, KeyEventType::Released),
815 ],
816 );
817 let actual = run_all_events(&handler, input).await;
818 let expected = new_key_sequence(
819 zx::MonotonicInstant::from_nanos(43),
820 &MATCHING_KEYBOARD_DESCRIPTOR,
821 Handled::No,
822 vec![
823 (Key::F1, KeyEventType::Pressed),
824 (Key::F2, KeyEventType::Pressed),
825 (Key::F1, KeyEventType::Released),
826 (Key::F2, KeyEventType::Released),
827 ],
828 );
829 pretty_assertions::assert_eq!(expected, actual);
830 }
831
832 #[fuchsia::test]
839 async fn search_pressed_before_f1_released() {
840 let inspector = fuchsia_inspect::Inspector::default();
841 let test_node = inspector.root().create_child("test_node");
842 let handler = ChromebookKeyboardHandler::new(&test_node);
843 let input = new_key_sequence(
844 zx::MonotonicInstant::from_nanos(42),
845 &MATCHING_KEYBOARD_DESCRIPTOR,
846 Handled::No,
847 vec![
848 (Key::F1, KeyEventType::Pressed),
849 (SEARCH_KEY, KeyEventType::Pressed),
850 (Key::F1, KeyEventType::Released),
851 (SEARCH_KEY, KeyEventType::Released),
852 ],
853 );
854 let actual = run_all_events(&handler, input).await;
855 let expected = new_key_sequence(
856 zx::MonotonicInstant::from_nanos(42),
857 &MATCHING_KEYBOARD_DESCRIPTOR,
858 Handled::No,
859 vec![
860 (Key::AcBack, KeyEventType::Pressed),
861 (Key::AcBack, KeyEventType::Released),
862 (Key::F1, KeyEventType::Pressed),
863 (Key::F1, KeyEventType::Released),
864 ],
865 );
866 pretty_assertions::assert_eq!(expected, actual);
867 }
868
869 #[fuchsia::test]
883 async fn search_pressed_while_f1_and_f2_pressed() {
884 let inspector = fuchsia_inspect::Inspector::default();
885 let test_node = inspector.root().create_child("test_node");
886 let handler = ChromebookKeyboardHandler::new(&test_node);
887 let input = new_key_sequence(
888 zx::MonotonicInstant::from_nanos(42),
889 &MATCHING_KEYBOARD_DESCRIPTOR,
890 Handled::No,
891 vec![
892 (Key::F1, KeyEventType::Pressed),
893 (Key::F2, KeyEventType::Pressed),
894 (SEARCH_KEY, KeyEventType::Pressed),
895 (Key::F1, KeyEventType::Released),
896 (Key::F2, KeyEventType::Released),
897 (SEARCH_KEY, KeyEventType::Released),
898 ],
899 );
900 let actual = run_all_events(&handler, input).await;
901 let expected = new_key_sequence(
902 zx::MonotonicInstant::from_nanos(42),
903 &MATCHING_KEYBOARD_DESCRIPTOR,
904 Handled::No,
905 vec![
906 (Key::AcBack, KeyEventType::Pressed),
907 (Key::AcRefresh, KeyEventType::Pressed),
908 (Key::AcRefresh, KeyEventType::Released),
909 (Key::AcBack, KeyEventType::Released),
910 (Key::F1, KeyEventType::Pressed),
911 (Key::F2, KeyEventType::Pressed),
912 (Key::F1, KeyEventType::Released),
913 (Key::F2, KeyEventType::Released),
914 ],
915 );
916 pretty_assertions::assert_eq!(expected, actual);
917 }
918
919 #[fuchsia::test]
935 async fn key_combination() {
936 let inspector = fuchsia_inspect::Inspector::default();
937 let test_node = inspector.root().create_child("test_node");
938 let handler = ChromebookKeyboardHandler::new(&test_node);
939 let input = new_key_sequence(
940 zx::MonotonicInstant::from_nanos(42),
941 &MATCHING_KEYBOARD_DESCRIPTOR,
942 Handled::No,
943 vec![
944 (Key::F1, KeyEventType::Pressed),
945 (SEARCH_KEY, KeyEventType::Pressed),
946 (Key::A, KeyEventType::Pressed),
947 (Key::F1, KeyEventType::Released),
948 (Key::F2, KeyEventType::Pressed),
949 (Key::A, KeyEventType::Released),
950 (SEARCH_KEY, KeyEventType::Released),
951 (Key::F2, KeyEventType::Released),
952 ],
953 );
954 let actual = run_all_events(&handler, input).await;
955 let expected = new_key_sequence(
956 zx::MonotonicInstant::from_nanos(42),
957 &MATCHING_KEYBOARD_DESCRIPTOR,
958 Handled::No,
959 vec![
960 (Key::AcBack, KeyEventType::Pressed),
961 (Key::AcBack, KeyEventType::Released),
962 (Key::F1, KeyEventType::Pressed),
963 (Key::A, KeyEventType::Pressed),
964 (Key::F1, KeyEventType::Released),
965 (Key::F2, KeyEventType::Pressed),
966 (Key::A, KeyEventType::Released),
967 (Key::F2, KeyEventType::Released),
968 (Key::AcRefresh, KeyEventType::Pressed),
969 (Key::AcRefresh, KeyEventType::Released),
970 ],
971 );
972 pretty_assertions::assert_eq!(expected, actual);
973 }
974
975 #[fuchsia::test]
976 async fn chromebook_keyboard_handler_initialized_with_inspect_node() {
977 let inspector = fuchsia_inspect::Inspector::default();
978 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
979 let _handler = ChromebookKeyboardHandler::new(&fake_handlers_node);
980 diagnostics_assertions::assert_data_tree!(inspector, root: {
981 input_handlers_node: {
982 chromebook_keyboard_handler: {
983 events_received_count: 0u64,
984 events_handled_count: 0u64,
985 last_received_timestamp_ns: 0u64,
986 "fuchsia.inspect.Health": {
987 status: "STARTING_UP",
988 start_timestamp_nanos: diagnostics_assertions::AnyProperty
991 },
992 }
993 }
994 });
995 }
996
997 #[fuchsia::test]
998 async fn chromebook_keyboard_handler_inspect_counts_events() {
999 let inspector = fuchsia_inspect::Inspector::default();
1000 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
1001 let handler = ChromebookKeyboardHandler::new(&fake_handlers_node);
1002 let events = new_key_sequence(
1003 zx::MonotonicInstant::from_nanos(42),
1004 &MATCHING_KEYBOARD_DESCRIPTOR,
1005 Handled::No,
1006 vec![
1007 (Key::F1, KeyEventType::Pressed),
1008 (Key::F1, KeyEventType::Released),
1009 (Key::Down, KeyEventType::Pressed),
1010 (Key::Down, KeyEventType::Released),
1011 ],
1012 );
1013 let _ = run_all_events(&handler, events).await;
1014 diagnostics_assertions::assert_data_tree!(inspector, root: {
1015 input_handlers_node: {
1016 chromebook_keyboard_handler: {
1017 events_received_count: 4u64,
1018 events_handled_count: 0u64,
1019 last_received_timestamp_ns: 45u64,
1020 "fuchsia.inspect.Health": {
1021 status: "STARTING_UP",
1022 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1025 },
1026 }
1027 }
1028 });
1029 }
1030}