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