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 crate::metrics;
23use async_trait::async_trait;
24use fidl_fuchsia_input::Key;
25use fidl_fuchsia_ui_input3::KeyEventType;
26use fuchsia_inspect::health::Reporter;
27use fuchsia_trace as ftrace;
28use keymaps::KeyState;
29use maplit::hashmap;
30use metrics_registry::InputPipelineErrorMetricDimensionEvent;
31use std::cell::RefCell;
32use std::collections::HashMap;
33use std::rc::Rc;
34use std::sync::LazyLock;
35
36const VENDOR_ID: u32 = 0x18d1; const PRODUCT_ID: u32 = 0x10003;
41
42const SEARCH_KEY: Key = Key::LeftMeta;
44
45#[derive(Debug)]
46struct KeyPair {
47 without_search: Key,
49 with_search: Key,
51}
52
53static REMAPPED_KEYS: LazyLock<HashMap<Key, KeyPair>> = LazyLock::new(|| {
57 hashmap! {
58 Key::F1 => KeyPair{ without_search: Key::AcBack, with_search: Key::F1 },
59 Key::F2 => KeyPair{ without_search: Key::AcRefresh, with_search: Key::F2},
60 Key::F3 => KeyPair{ without_search: Key::AcFullScreenView, with_search: Key::F3 },
61 Key::F4 => KeyPair{ without_search: Key::AcSelectTaskApplication, with_search: Key::F4 },
62 Key::F5 => KeyPair{ without_search: Key::BrightnessDown, with_search: Key::F5 },
63 Key::F6 => KeyPair{ without_search: Key::BrightnessUp, with_search: Key::F6 },
64 Key::F7 => KeyPair{ without_search: Key::PlayPause, with_search: Key::F7 },
65 Key::F8 => KeyPair{ without_search: Key::Mute, with_search: Key::F8 },
66 Key::F9 => KeyPair{ without_search: Key::VolumeDown, with_search: Key::F9 },
67 Key::F10 => KeyPair{ without_search: Key::VolumeUp, with_search: Key::F10 },
68 Key::Left => KeyPair{ without_search: Key::Left, with_search: Key::Home },
69 Key::Right => KeyPair{ without_search: Key::Right, with_search: Key::End },
70 Key::Up => KeyPair{ without_search: Key::Up, with_search: Key::PageUp },
71 Key::Down => KeyPair{ without_search: Key::Down, with_search: Key::PageDown },
72 Key::Dot => KeyPair{ without_search: Key::Dot, with_search: Key::Insert },
73 Key::Backspace => KeyPair{ without_search: Key::Backspace, with_search: Key::Delete },
74 }
75});
76
77#[derive(Debug, Default)]
81pub struct ChromebookKeyboardHandler {
82 state: RefCell<Inner>,
84
85 metrics_logger: metrics::MetricsLogger,
87
88 pub inspect_status: InputHandlerStatus,
90}
91
92#[derive(Debug, Default)]
93struct Inner {
94 key_state: KeyState,
97 is_search_key_actuated: bool,
99 other_key_events: bool,
102 next_event_time: zx::MonotonicInstant,
106 regular_keys_pressed: bool,
109}
110
111fn is_chromebook_keyboard(device_info: &fidl_fuchsia_input_report::DeviceInformation) -> bool {
113 device_info.product_id.unwrap_or_default() == PRODUCT_ID
114 && device_info.vendor_id.unwrap_or_default() == VENDOR_ID
115}
116
117impl Handler for ChromebookKeyboardHandler {
118 fn set_handler_healthy(self: std::rc::Rc<Self>) {
119 self.inspect_status.health_node.borrow_mut().set_ok();
120 }
121
122 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
123 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
124 }
125
126 fn get_name(&self) -> &'static str {
127 "ChromebookKeyboardHandler"
128 }
129
130 fn interest(&self) -> Vec<InputEventType> {
131 vec![InputEventType::Keyboard]
132 }
133}
134
135#[async_trait(?Send)]
136impl UnhandledInputHandler for ChromebookKeyboardHandler {
137 async fn handle_unhandled_input_event(
138 self: Rc<Self>,
139 input_event: UnhandledInputEvent,
140 ) -> Vec<InputEvent> {
141 fuchsia_trace::duration!("input", "chromebook_keyboard_handler");
142 match input_event {
143 UnhandledInputEvent {
145 device_event: InputDeviceEvent::Keyboard(event),
146 device_descriptor: InputDeviceDescriptor::Keyboard(ref keyboard_descriptor),
147 event_time,
148 trace_id,
149 } if is_chromebook_keyboard(&keyboard_descriptor.device_information) => {
150 fuchsia_trace::duration!("input", "chromebook_keyboard_handler[processing]");
151 self.inspect_status.count_received_event(&event_time);
152 self.process_keyboard_event(
153 event,
154 keyboard_descriptor.clone(),
155 event_time,
156 trace_id,
157 )
158 }
159 _ => {
161 if InputEventType::from(&input_event.device_event) != InputEventType::Keyboard {
162 self.metrics_logger.log_error(
163 InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
164 std::format!(
165 "{} uninterested input event: {:?}",
166 self.get_name(),
167 input_event.get_event_type()
168 ),
169 );
170 }
171 vec![InputEvent::from(input_event)]
172 }
173 }
174 }
175}
176
177impl ChromebookKeyboardHandler {
178 pub fn new(
180 input_handlers_node: &fuchsia_inspect::Node,
181 metrics_logger: metrics::MetricsLogger,
182 ) -> Rc<Self> {
183 let inspect_status = InputHandlerStatus::new(
184 input_handlers_node,
185 "chromebook_keyboard_handler",
186 true,
187 );
188 Rc::new(Self { state: RefCell::new(Default::default()), metrics_logger, inspect_status })
189 }
190
191 fn next_event_time(self: &Rc<Self>, event_time: zx::MonotonicInstant) -> zx::MonotonicInstant {
194 let proposed = self.state.borrow().next_event_time;
195 let returned = if event_time < proposed { proposed } else { event_time };
196 self.state.borrow_mut().next_event_time = returned + zx::MonotonicDuration::from_nanos(1);
197 returned
198 }
199
200 fn update_key_state(self: &Rc<Self>, event_type: KeyEventType, key: Key) {
202 if REMAPPED_KEYS.contains_key(&key) {
203 self.state.borrow_mut().key_state.update(event_type, key)
204 }
205 }
206
207 fn get_ordered_keys(self: &Rc<Self>) -> Vec<Key> {
209 self.state.borrow().key_state.get_ordered_keys()
210 }
211
212 fn is_search_key_actuated(self: &Rc<Self>) -> bool {
213 self.state.borrow().is_search_key_actuated
214 }
215
216 fn set_search_key_actuated(self: &Rc<Self>, value: bool) {
217 self.state.borrow_mut().is_search_key_actuated = value;
218 }
219
220 fn has_other_key_events(self: &Rc<Self>) -> bool {
221 self.state.borrow().other_key_events
222 }
223
224 fn set_other_key_events(self: &Rc<Self>, value: bool) {
225 self.state.borrow_mut().other_key_events = value;
226 }
227
228 fn is_regular_keys_pressed(self: &Rc<Self>) -> bool {
229 self.state.borrow().regular_keys_pressed
230 }
231
232 fn set_regular_keys_pressed(self: &Rc<Self>, value: bool) {
233 self.state.borrow_mut().regular_keys_pressed = value;
234 }
235
236 fn synthesize_input_events<'a, I: Iterator<Item = &'a Key>>(
237 self: &Rc<Self>,
238 event: KeyboardEvent,
239 event_type: KeyEventType,
240 descriptor: KeyboardDeviceDescriptor,
241 event_time: zx::MonotonicInstant,
242 trace_id: Option<ftrace::Id>,
243 keys: I,
244 mapfn: fn(&KeyPair) -> Key,
245 ) -> Vec<InputEvent> {
246 keys.map(|key| {
247 mapfn(REMAPPED_KEYS.get(key).expect("released_key must be in REMAPPED_KEYS"))
248 })
249 .map(|key| {
250 into_unhandled_input_event(
251 event.clone().into_with_key(key).into_with_event_type(event_type),
252 descriptor.clone(),
253 self.next_event_time(event_time),
254 trace_id,
255 )
256 })
257 .collect()
258 }
259
260 fn process_keyboard_event(
262 self: &Rc<Self>,
263 event: KeyboardEvent,
264 device_descriptor: KeyboardDeviceDescriptor,
265 event_time: zx::MonotonicInstant,
266 trace_id: Option<ftrace::Id>,
267 ) -> Vec<InputEvent> {
268 let is_search_key_actuated = self.is_search_key_actuated();
272
273 let key = event.get_key();
274 let event_type_folded = event.get_event_type_folded();
275 let event_type = event.get_event_type();
276
277 match is_search_key_actuated {
278 true => {
279 if key == SEARCH_KEY && event_type_folded == KeyEventType::Released {
282 let keys_to_release = self.get_ordered_keys();
284
285 let mut new_events = self.synthesize_input_events(
287 event.clone(),
288 KeyEventType::Released,
289 device_descriptor.clone(),
290 event_time,
291 None,
292 keys_to_release.iter().rev(),
293 |kp: &KeyPair| kp.with_search,
294 );
295 new_events.append(&mut self.synthesize_input_events(
296 event.clone(),
297 KeyEventType::Pressed,
298 device_descriptor.clone(),
299 event_time,
300 None,
301 keys_to_release.iter(),
302 |kp: &KeyPair| kp.without_search,
303 ));
304
305 let search_key_only =
312 !self.has_other_key_events() && event_type == KeyEventType::Released;
313 if search_key_only {
316 new_events.push(into_unhandled_input_event(
317 event.clone().into_with_event_type(KeyEventType::Pressed),
318 device_descriptor.clone(),
319 self.next_event_time(event_time),
320 None,
321 ));
322 }
323 if search_key_only || self.is_regular_keys_pressed() {
328 new_events.push(into_unhandled_input_event(
329 event.into_with_event_type(KeyEventType::Released),
330 device_descriptor,
331 self.next_event_time(event_time),
332 None,
333 ));
334 }
335
336 self.set_search_key_actuated(false);
338 self.set_other_key_events(false);
339 self.set_regular_keys_pressed(false);
340
341 return new_events;
342 } else {
343 }
345 }
346 false => {
347 if key == SEARCH_KEY && event_type == KeyEventType::Pressed {
348 let keys_to_release = self.get_ordered_keys();
350
351 let mut new_events = self.synthesize_input_events(
352 event.clone(),
353 KeyEventType::Released,
354 device_descriptor.clone(),
355 event_time,
356 None,
357 keys_to_release.iter().rev(),
358 |kp: &KeyPair| kp.without_search,
359 );
360 new_events.append(&mut self.synthesize_input_events(
361 event,
362 KeyEventType::Pressed,
363 device_descriptor,
364 event_time,
365 None,
366 keys_to_release.iter(),
367 |kp: &KeyPair| kp.with_search,
368 ));
369
370 self.set_search_key_actuated(true);
371 if !keys_to_release.is_empty() {
372 self.set_other_key_events(true);
373 }
374 return new_events;
375 }
376 }
377 }
378
379 self.update_key_state(event_type, key);
380 let maybe_remapped_key = REMAPPED_KEYS.get(&key);
381 let return_events = if let Some(remapped_keypair) = maybe_remapped_key {
382 let key = if is_search_key_actuated {
383 remapped_keypair.with_search
384 } else {
385 remapped_keypair.without_search
386 };
387 vec![into_unhandled_input_event(
388 event.into_with_key(key),
389 device_descriptor,
390 self.next_event_time(event_time),
391 trace_id,
392 )]
393 } else {
394 let mut events = vec![];
395 if self.is_search_key_actuated()
399 && !self.has_other_key_events()
400 && event_type == KeyEventType::Pressed
401 {
402 let new_event = event
403 .clone()
404 .into_with_key(SEARCH_KEY)
405 .into_with_event_type(KeyEventType::Pressed);
406 events.push(into_unhandled_input_event(
407 new_event,
408 device_descriptor.clone(),
409 self.next_event_time(event_time),
410 None,
411 ));
412 self.set_regular_keys_pressed(true);
413 }
414 events.push(into_unhandled_input_event(
415 event,
416 device_descriptor,
417 self.next_event_time(event_time),
418 trace_id,
419 ));
420 events
423 };
424
425 if event_type == KeyEventType::Pressed && key != SEARCH_KEY && is_search_key_actuated {
428 self.set_other_key_events(true);
429 }
430
431 return_events
432 }
433}
434
435fn into_unhandled_input_event(
436 event: KeyboardEvent,
437 device_descriptor: KeyboardDeviceDescriptor,
438 event_time: zx::MonotonicInstant,
439 trace_id: Option<ftrace::Id>,
440) -> InputEvent {
441 InputEvent {
442 device_event: InputDeviceEvent::Keyboard(event),
443 device_descriptor: device_descriptor.into(),
444 event_time,
445 handled: Handled::No,
446 trace_id,
447 }
448}
449
450#[cfg(test)]
451mod tests {
452 use super::*;
453 use crate::testing_utilities::create_input_event;
454 use std::sync::LazyLock;
455 use test_case::test_case;
456
457 static MATCHING_KEYBOARD_DESCRIPTOR: LazyLock<InputDeviceDescriptor> = LazyLock::new(|| {
458 InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
459 keys: vec![],
460 device_information: fidl_fuchsia_input_report::DeviceInformation {
461 vendor_id: Some(VENDOR_ID),
462 product_id: Some(PRODUCT_ID),
463 version: Some(42),
464 polling_rate: Some(1000),
465 ..Default::default()
466 },
467 device_id: 43,
468 })
469 });
470 static MISMATCHING_KEYBOARD_DESCRIPTOR: LazyLock<InputDeviceDescriptor> = LazyLock::new(|| {
471 InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
472 keys: vec![],
473 device_information: fidl_fuchsia_input_report::DeviceInformation {
474 vendor_id: Some(VENDOR_ID + 10),
475 product_id: Some(PRODUCT_ID),
476 version: Some(42),
477 polling_rate: Some(1000),
478 ..Default::default()
479 },
480 device_id: 43,
481 })
482 });
483
484 async fn run_all_events<T: UnhandledInputHandler>(
485 handler: &Rc<T>,
486 events: Vec<InputEvent>,
487 ) -> Vec<InputEvent> {
488 let handler_clone = || handler.clone();
489 let events_futs = events
490 .into_iter()
491 .map(|e| e.try_into().expect("events are always convertible in tests"))
492 .map(|e| handler_clone().handle_unhandled_input_event(e));
493 let mut events_set = vec![];
495 for events_fut in events_futs.into_iter() {
496 events_set.push(events_fut.await);
497 }
498 events_set.into_iter().flatten().collect()
499 }
500
501 fn new_key_sequence(
505 mut event_time: zx::MonotonicInstant,
506 descriptor: &InputDeviceDescriptor,
507 handled: Handled,
508 keys: Vec<(Key, KeyEventType)>,
509 ) -> Vec<InputEvent> {
510 let mut ret = vec![];
511 for (k, t) in keys {
512 ret.push(create_input_event(KeyboardEvent::new(k, t), descriptor, event_time, handled));
513 event_time = event_time + zx::MonotonicDuration::from_nanos(1);
514 }
515 ret
516 }
517
518 #[test]
519 fn next_event_time() {
520 let inspector = fuchsia_inspect::Inspector::default();
521 let test_node = inspector.root().create_child("test_node");
522 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
523 assert_eq!(
524 zx::MonotonicInstant::from_nanos(10),
525 handler.next_event_time(zx::MonotonicInstant::from_nanos(10))
526 );
527 assert_eq!(
528 zx::MonotonicInstant::from_nanos(11),
529 handler.next_event_time(zx::MonotonicInstant::from_nanos(10))
530 );
531 assert_eq!(
532 zx::MonotonicInstant::from_nanos(12),
533 handler.next_event_time(zx::MonotonicInstant::from_nanos(10))
534 );
535 assert_eq!(
536 zx::MonotonicInstant::from_nanos(13),
537 handler.next_event_time(zx::MonotonicInstant::from_nanos(13))
538 );
539 assert_eq!(
540 zx::MonotonicInstant::from_nanos(14),
541 handler.next_event_time(zx::MonotonicInstant::from_nanos(13))
542 );
543 }
544
545 #[test_case(Key::F1, Key::AcBack; "convert F1")]
548 #[test_case(Key::F2, Key::AcRefresh; "convert F2")]
549 #[test_case(Key::F3, Key::AcFullScreenView; "convert F3")]
550 #[test_case(Key::F4, Key::AcSelectTaskApplication; "convert F4")]
551 #[test_case(Key::F5, Key::BrightnessDown; "convert F5")]
552 #[test_case(Key::F6, Key::BrightnessUp; "convert F6")]
553 #[test_case(Key::F7, Key::PlayPause; "convert F7")]
554 #[test_case(Key::F8, Key::Mute; "convert F8")]
555 #[test_case(Key::F9, Key::VolumeDown; "convert F9")]
556 #[test_case(Key::F10, Key::VolumeUp; "convert F10")]
557 #[test_case(Key::A, Key::A; "do not convert A")]
558 #[test_case(Key::Up, Key::Up; "do not convert Up")]
559 #[test_case(Key::Down, Key::Down; "do not convert Down")]
560 #[test_case(Key::Left, Key::Left; "do not convert Left")]
561 #[test_case(Key::Right, Key::Right; "do not convert Right")]
562 #[test_case(Key::Dot, Key::Dot; "do not convert Dot")]
563 #[test_case(Key::Backspace, Key::Backspace; "do not convert Backspace")]
564 #[fuchsia::test]
565 async fn conversion_matching_keyboard(input_key: Key, output_key: Key) {
566 let inspector = fuchsia_inspect::Inspector::default();
567 let test_node = inspector.root().create_child("test_node");
568 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
569 let input = new_key_sequence(
570 zx::MonotonicInstant::from_nanos(42),
571 &MATCHING_KEYBOARD_DESCRIPTOR,
572 Handled::No,
573 vec![(input_key, KeyEventType::Pressed), (input_key, KeyEventType::Released)],
574 );
575 let actual = run_all_events(&handler, input).await;
576 let expected = new_key_sequence(
577 zx::MonotonicInstant::from_nanos(42),
578 &MATCHING_KEYBOARD_DESCRIPTOR,
579 Handled::No,
580 vec![(output_key, KeyEventType::Pressed), (output_key, KeyEventType::Released)],
581 );
582 pretty_assertions::assert_eq!(expected, actual);
583 }
584
585 #[test_case(Key::F1, Key::F1; "do not convert F1")]
588 #[test_case(Key::F2, Key::F2; "do not convert F2")]
589 #[test_case(Key::F3, Key::F3; "do not convert F3")]
590 #[test_case(Key::F4, Key::F4; "do not convert F4")]
591 #[test_case(Key::F5, Key::F5; "do not convert F5")]
592 #[test_case(Key::F6, Key::F6; "do not convert F6")]
593 #[test_case(Key::F7, Key::F7; "do not convert F7")]
594 #[test_case(Key::F8, Key::F8; "do not convert F8")]
595 #[test_case(Key::F9, Key::F9; "do not convert F9")]
596 #[test_case(Key::F10, Key::F10; "do not convert F10")]
597 #[test_case(Key::A, Key::A; "do not convert A")]
598 #[fuchsia::test]
599 async fn conversion_mismatching_keyboard(input_key: Key, output_key: Key) {
600 let inspector = fuchsia_inspect::Inspector::default();
601 let test_node = inspector.root().create_child("test_node");
602 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
603 let input = new_key_sequence(
604 zx::MonotonicInstant::from_nanos(42),
605 &MISMATCHING_KEYBOARD_DESCRIPTOR,
606 Handled::No,
607 vec![(input_key, KeyEventType::Pressed), (input_key, KeyEventType::Released)],
608 );
609 let actual = run_all_events(&handler, input).await;
610 let expected = new_key_sequence(
611 zx::MonotonicInstant::from_nanos(42),
612 &MISMATCHING_KEYBOARD_DESCRIPTOR,
613 Handled::No,
614 vec![(output_key, KeyEventType::Pressed), (output_key, KeyEventType::Released)],
615 );
616 pretty_assertions::assert_eq!(expected, actual);
617 }
618
619 #[fuchsia::test]
626 async fn search_key_only() {
627 let inspector = fuchsia_inspect::Inspector::default();
628 let test_node = inspector.root().create_child("test_node");
629 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
630 let input = new_key_sequence(
631 zx::MonotonicInstant::from_nanos(42),
632 &MATCHING_KEYBOARD_DESCRIPTOR,
633 Handled::No,
634 vec![(SEARCH_KEY, KeyEventType::Pressed), (SEARCH_KEY, KeyEventType::Released)],
635 );
636 let actual = run_all_events(&handler, input).await;
637 let expected = new_key_sequence(
638 zx::MonotonicInstant::from_nanos(43),
639 &MATCHING_KEYBOARD_DESCRIPTOR,
640 Handled::No,
641 vec![(SEARCH_KEY, KeyEventType::Pressed), (SEARCH_KEY, KeyEventType::Released)],
642 );
643 pretty_assertions::assert_eq!(expected, actual);
644 }
645
646 #[fuchsia::test]
655 async fn f1_conversion() {
656 let inspector = fuchsia_inspect::Inspector::default();
657 let test_node = inspector.root().create_child("test_node");
658 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
659 let input = new_key_sequence(
660 zx::MonotonicInstant::from_nanos(42),
661 &MATCHING_KEYBOARD_DESCRIPTOR,
662 Handled::No,
663 vec![
664 (SEARCH_KEY, KeyEventType::Pressed),
665 (Key::F1, KeyEventType::Pressed),
666 (Key::F1, KeyEventType::Released),
667 (SEARCH_KEY, KeyEventType::Released),
668 ],
669 );
670 let actual = run_all_events(&handler, input).await;
671 let expected = new_key_sequence(
672 zx::MonotonicInstant::from_nanos(43),
673 &MATCHING_KEYBOARD_DESCRIPTOR,
674 Handled::No,
675 vec![(Key::F1, KeyEventType::Pressed), (Key::F1, KeyEventType::Released)],
676 );
677 pretty_assertions::assert_eq!(expected, actual);
678 }
679
680 #[test_case(Key::F1, Key::F1; "do not convert F1")]
691 #[test_case(Key::F2, Key::F2; "do not convert F2")]
692 #[test_case(Key::F3, Key::F3; "do not convert F3")]
693 #[test_case(Key::F4, Key::F4; "do not convert F4")]
694 #[test_case(Key::F5, Key::F5; "do not convert F5")]
695 #[test_case(Key::F6, Key::F6; "do not convert F6")]
696 #[test_case(Key::F7, Key::F7; "do not convert F7")]
697 #[test_case(Key::F8, Key::F8; "do not convert F8")]
698 #[test_case(Key::F9, Key::F9; "do not convert F9")]
699 #[test_case(Key::F10, Key::F10; "do not convert F10")]
700 #[test_case(Key::Up, Key::PageUp; "convert Up")]
701 #[test_case(Key::Down, Key::PageDown; "convert Down")]
702 #[test_case(Key::Left, Key::Home; "convert Left")]
703 #[test_case(Key::Right, Key::End; "convert Right")]
704 #[test_case(Key::Dot, Key::Insert; "convert Dot")]
705 #[test_case(Key::Backspace, Key::Delete; "convert Backspace")]
706 #[fuchsia::test]
707 async fn with_search_key_pressed(input_key: Key, output_key: Key) {
708 let inspector = fuchsia_inspect::Inspector::default();
709 let test_node = inspector.root().create_child("test_node");
710 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
711 let input = new_key_sequence(
712 zx::MonotonicInstant::from_nanos(42),
713 &MATCHING_KEYBOARD_DESCRIPTOR,
714 Handled::No,
715 vec![
716 (SEARCH_KEY, KeyEventType::Pressed),
717 (input_key, KeyEventType::Pressed),
718 (input_key, KeyEventType::Released),
719 (SEARCH_KEY, KeyEventType::Released),
720 ],
721 );
722 let actual = run_all_events(&handler, input).await;
723 let expected = new_key_sequence(
724 zx::MonotonicInstant::from_nanos(43),
725 &MATCHING_KEYBOARD_DESCRIPTOR,
726 Handled::No,
727 vec![(output_key, KeyEventType::Pressed), (output_key, KeyEventType::Released)],
728 );
729 pretty_assertions::assert_eq!(expected, actual);
730 }
731
732 #[fuchsia::test]
739 async fn search_released_before_f1() {
740 let inspector = fuchsia_inspect::Inspector::default();
741 let test_node = inspector.root().create_child("test_node");
742 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
743 let input = new_key_sequence(
744 zx::MonotonicInstant::from_nanos(42),
745 &MATCHING_KEYBOARD_DESCRIPTOR,
746 Handled::No,
747 vec![
748 (SEARCH_KEY, KeyEventType::Pressed),
749 (Key::F1, KeyEventType::Pressed),
750 (SEARCH_KEY, KeyEventType::Released),
751 (Key::F1, KeyEventType::Released),
752 ],
753 );
754 let actual = run_all_events(&handler, input).await;
755 let expected = new_key_sequence(
756 zx::MonotonicInstant::from_nanos(43),
757 &MATCHING_KEYBOARD_DESCRIPTOR,
758 Handled::No,
759 vec![
760 (Key::F1, KeyEventType::Pressed),
761 (Key::F1, KeyEventType::Released),
762 (Key::AcBack, KeyEventType::Pressed),
763 (Key::AcBack, KeyEventType::Released),
764 ],
765 );
766 pretty_assertions::assert_eq!(expected, actual);
767 }
768
769 #[fuchsia::test]
778 async fn search_key_a_is_delayed_leftmeta_a() {
779 let inspector = fuchsia_inspect::Inspector::default();
780 let test_node = inspector.root().create_child("test_node");
781 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
782 let input = new_key_sequence(
783 zx::MonotonicInstant::from_nanos(42),
784 &MATCHING_KEYBOARD_DESCRIPTOR,
785 Handled::No,
786 vec![
787 (SEARCH_KEY, KeyEventType::Pressed),
788 (Key::A, KeyEventType::Pressed),
789 (Key::A, KeyEventType::Released),
790 (SEARCH_KEY, KeyEventType::Released),
791 ],
792 );
793 let actual = run_all_events(&handler, input).await;
794 let expected = new_key_sequence(
795 zx::MonotonicInstant::from_nanos(43),
796 &MATCHING_KEYBOARD_DESCRIPTOR,
797 Handled::No,
798 vec![
799 (Key::LeftMeta, KeyEventType::Pressed),
800 (Key::A, KeyEventType::Pressed),
801 (Key::A, KeyEventType::Released),
802 (Key::LeftMeta, KeyEventType::Released),
803 ],
804 );
805 pretty_assertions::assert_eq!(expected, actual);
806 }
807
808 #[fuchsia::test]
816 async fn f1_and_f2_interleaved_conversion() {
817 let inspector = fuchsia_inspect::Inspector::default();
818 let test_node = inspector.root().create_child("test_node");
819 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
820 let input = new_key_sequence(
821 zx::MonotonicInstant::from_nanos(42),
822 &MATCHING_KEYBOARD_DESCRIPTOR,
823 Handled::No,
824 vec![
825 (SEARCH_KEY, KeyEventType::Pressed),
826 (Key::F1, KeyEventType::Pressed),
827 (Key::F2, KeyEventType::Pressed),
828 (Key::F1, KeyEventType::Released),
829 (Key::F2, KeyEventType::Released),
830 (SEARCH_KEY, KeyEventType::Released),
831 ],
832 );
833 let actual = run_all_events(&handler, input).await;
834 let expected = new_key_sequence(
835 zx::MonotonicInstant::from_nanos(43),
836 &MATCHING_KEYBOARD_DESCRIPTOR,
837 Handled::No,
838 vec![
839 (Key::F1, KeyEventType::Pressed),
840 (Key::F2, KeyEventType::Pressed),
841 (Key::F1, KeyEventType::Released),
842 (Key::F2, KeyEventType::Released),
843 ],
844 );
845 pretty_assertions::assert_eq!(expected, actual);
846 }
847
848 #[fuchsia::test]
855 async fn search_pressed_before_f1_released() {
856 let inspector = fuchsia_inspect::Inspector::default();
857 let test_node = inspector.root().create_child("test_node");
858 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
859 let input = new_key_sequence(
860 zx::MonotonicInstant::from_nanos(42),
861 &MATCHING_KEYBOARD_DESCRIPTOR,
862 Handled::No,
863 vec![
864 (Key::F1, KeyEventType::Pressed),
865 (SEARCH_KEY, KeyEventType::Pressed),
866 (Key::F1, KeyEventType::Released),
867 (SEARCH_KEY, KeyEventType::Released),
868 ],
869 );
870 let actual = run_all_events(&handler, input).await;
871 let expected = new_key_sequence(
872 zx::MonotonicInstant::from_nanos(42),
873 &MATCHING_KEYBOARD_DESCRIPTOR,
874 Handled::No,
875 vec![
876 (Key::AcBack, KeyEventType::Pressed),
877 (Key::AcBack, KeyEventType::Released),
878 (Key::F1, KeyEventType::Pressed),
879 (Key::F1, KeyEventType::Released),
880 ],
881 );
882 pretty_assertions::assert_eq!(expected, actual);
883 }
884
885 #[fuchsia::test]
899 async fn search_pressed_while_f1_and_f2_pressed() {
900 let inspector = fuchsia_inspect::Inspector::default();
901 let test_node = inspector.root().create_child("test_node");
902 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
903 let input = new_key_sequence(
904 zx::MonotonicInstant::from_nanos(42),
905 &MATCHING_KEYBOARD_DESCRIPTOR,
906 Handled::No,
907 vec![
908 (Key::F1, KeyEventType::Pressed),
909 (Key::F2, KeyEventType::Pressed),
910 (SEARCH_KEY, KeyEventType::Pressed),
911 (Key::F1, KeyEventType::Released),
912 (Key::F2, KeyEventType::Released),
913 (SEARCH_KEY, KeyEventType::Released),
914 ],
915 );
916 let actual = run_all_events(&handler, input).await;
917 let expected = new_key_sequence(
918 zx::MonotonicInstant::from_nanos(42),
919 &MATCHING_KEYBOARD_DESCRIPTOR,
920 Handled::No,
921 vec![
922 (Key::AcBack, KeyEventType::Pressed),
923 (Key::AcRefresh, KeyEventType::Pressed),
924 (Key::AcRefresh, KeyEventType::Released),
925 (Key::AcBack, KeyEventType::Released),
926 (Key::F1, KeyEventType::Pressed),
927 (Key::F2, KeyEventType::Pressed),
928 (Key::F1, KeyEventType::Released),
929 (Key::F2, KeyEventType::Released),
930 ],
931 );
932 pretty_assertions::assert_eq!(expected, actual);
933 }
934
935 #[fuchsia::test]
951 async fn key_combination() {
952 let inspector = fuchsia_inspect::Inspector::default();
953 let test_node = inspector.root().create_child("test_node");
954 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
955 let input = new_key_sequence(
956 zx::MonotonicInstant::from_nanos(42),
957 &MATCHING_KEYBOARD_DESCRIPTOR,
958 Handled::No,
959 vec![
960 (Key::F1, KeyEventType::Pressed),
961 (SEARCH_KEY, KeyEventType::Pressed),
962 (Key::A, KeyEventType::Pressed),
963 (Key::F1, KeyEventType::Released),
964 (Key::F2, KeyEventType::Pressed),
965 (Key::A, KeyEventType::Released),
966 (SEARCH_KEY, KeyEventType::Released),
967 (Key::F2, KeyEventType::Released),
968 ],
969 );
970 let actual = run_all_events(&handler, input).await;
971 let expected = new_key_sequence(
972 zx::MonotonicInstant::from_nanos(42),
973 &MATCHING_KEYBOARD_DESCRIPTOR,
974 Handled::No,
975 vec![
976 (Key::AcBack, KeyEventType::Pressed),
977 (Key::AcBack, KeyEventType::Released),
978 (Key::F1, KeyEventType::Pressed),
979 (Key::A, KeyEventType::Pressed),
980 (Key::F1, KeyEventType::Released),
981 (Key::F2, KeyEventType::Pressed),
982 (Key::A, KeyEventType::Released),
983 (Key::F2, KeyEventType::Released),
984 (Key::AcRefresh, KeyEventType::Pressed),
985 (Key::AcRefresh, KeyEventType::Released),
986 ],
987 );
988 pretty_assertions::assert_eq!(expected, actual);
989 }
990
991 #[fuchsia::test]
992 async fn chromebook_keyboard_handler_initialized_with_inspect_node() {
993 let inspector = fuchsia_inspect::Inspector::default();
994 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
995 let _handler =
996 ChromebookKeyboardHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
997 diagnostics_assertions::assert_data_tree!(inspector, root: {
998 input_handlers_node: {
999 chromebook_keyboard_handler: {
1000 events_received_count: 0u64,
1001 events_handled_count: 0u64,
1002 last_received_timestamp_ns: 0u64,
1003 "fuchsia.inspect.Health": {
1004 status: "STARTING_UP",
1005 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1008 },
1009 }
1010 }
1011 });
1012 }
1013
1014 #[fuchsia::test]
1015 async fn chromebook_keyboard_handler_inspect_counts_events() {
1016 let inspector = fuchsia_inspect::Inspector::default();
1017 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
1018 let handler =
1019 ChromebookKeyboardHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
1020 let events = new_key_sequence(
1021 zx::MonotonicInstant::from_nanos(42),
1022 &MATCHING_KEYBOARD_DESCRIPTOR,
1023 Handled::No,
1024 vec![
1025 (Key::F1, KeyEventType::Pressed),
1026 (Key::F1, KeyEventType::Released),
1027 (Key::Down, KeyEventType::Pressed),
1028 (Key::Down, KeyEventType::Released),
1029 ],
1030 );
1031 let _ = run_all_events(&handler, events).await;
1032 diagnostics_assertions::assert_data_tree!(inspector, root: {
1033 input_handlers_node: {
1034 chromebook_keyboard_handler: {
1035 events_received_count: 4u64,
1036 events_handled_count: 0u64,
1037 last_received_timestamp_ns: 45u64,
1038 "fuchsia.inspect.Health": {
1039 status: "STARTING_UP",
1040 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1043 },
1044 }
1045 }
1046 });
1047 }
1048}