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 self.metrics_logger.log_error(
162 InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
163 std::format!("uninterested input event: {:?}", input_event.get_event_type()),
164 );
165 vec![InputEvent::from(input_event)]
166 }
167 }
168 }
169}
170
171impl ChromebookKeyboardHandler {
172 pub fn new(
174 input_handlers_node: &fuchsia_inspect::Node,
175 metrics_logger: metrics::MetricsLogger,
176 ) -> Rc<Self> {
177 let inspect_status = InputHandlerStatus::new(
178 input_handlers_node,
179 "chromebook_keyboard_handler",
180 true,
181 );
182 Rc::new(Self { state: RefCell::new(Default::default()), metrics_logger, inspect_status })
183 }
184
185 fn next_event_time(self: &Rc<Self>, event_time: zx::MonotonicInstant) -> zx::MonotonicInstant {
188 let proposed = self.state.borrow().next_event_time;
189 let returned = if event_time < proposed { proposed } else { event_time };
190 self.state.borrow_mut().next_event_time = returned + zx::MonotonicDuration::from_nanos(1);
191 returned
192 }
193
194 fn update_key_state(self: &Rc<Self>, event_type: KeyEventType, key: Key) {
196 if REMAPPED_KEYS.contains_key(&key) {
197 self.state.borrow_mut().key_state.update(event_type, key)
198 }
199 }
200
201 fn get_ordered_keys(self: &Rc<Self>) -> Vec<Key> {
203 self.state.borrow().key_state.get_ordered_keys()
204 }
205
206 fn is_search_key_actuated(self: &Rc<Self>) -> bool {
207 self.state.borrow().is_search_key_actuated
208 }
209
210 fn set_search_key_actuated(self: &Rc<Self>, value: bool) {
211 self.state.borrow_mut().is_search_key_actuated = value;
212 }
213
214 fn has_other_key_events(self: &Rc<Self>) -> bool {
215 self.state.borrow().other_key_events
216 }
217
218 fn set_other_key_events(self: &Rc<Self>, value: bool) {
219 self.state.borrow_mut().other_key_events = value;
220 }
221
222 fn is_regular_keys_pressed(self: &Rc<Self>) -> bool {
223 self.state.borrow().regular_keys_pressed
224 }
225
226 fn set_regular_keys_pressed(self: &Rc<Self>, value: bool) {
227 self.state.borrow_mut().regular_keys_pressed = value;
228 }
229
230 fn synthesize_input_events<'a, I: Iterator<Item = &'a Key>>(
231 self: &Rc<Self>,
232 event: KeyboardEvent,
233 event_type: KeyEventType,
234 descriptor: KeyboardDeviceDescriptor,
235 event_time: zx::MonotonicInstant,
236 trace_id: Option<ftrace::Id>,
237 keys: I,
238 mapfn: fn(&KeyPair) -> Key,
239 ) -> Vec<InputEvent> {
240 keys.map(|key| {
241 mapfn(REMAPPED_KEYS.get(key).expect("released_key must be in REMAPPED_KEYS"))
242 })
243 .map(|key| {
244 into_unhandled_input_event(
245 event.clone().into_with_key(key).into_with_event_type(event_type),
246 descriptor.clone(),
247 self.next_event_time(event_time),
248 trace_id,
249 )
250 })
251 .collect()
252 }
253
254 fn process_keyboard_event(
256 self: &Rc<Self>,
257 event: KeyboardEvent,
258 device_descriptor: KeyboardDeviceDescriptor,
259 event_time: zx::MonotonicInstant,
260 trace_id: Option<ftrace::Id>,
261 ) -> Vec<InputEvent> {
262 let is_search_key_actuated = self.is_search_key_actuated();
266
267 let key = event.get_key();
268 let event_type_folded = event.get_event_type_folded();
269 let event_type = event.get_event_type();
270
271 match is_search_key_actuated {
272 true => {
273 if key == SEARCH_KEY && event_type_folded == KeyEventType::Released {
276 let keys_to_release = self.get_ordered_keys();
278
279 let mut new_events = self.synthesize_input_events(
281 event.clone(),
282 KeyEventType::Released,
283 device_descriptor.clone(),
284 event_time,
285 None,
286 keys_to_release.iter().rev(),
287 |kp: &KeyPair| kp.with_search,
288 );
289 new_events.append(&mut self.synthesize_input_events(
290 event.clone(),
291 KeyEventType::Pressed,
292 device_descriptor.clone(),
293 event_time,
294 None,
295 keys_to_release.iter(),
296 |kp: &KeyPair| kp.without_search,
297 ));
298
299 let search_key_only =
306 !self.has_other_key_events() && event_type == KeyEventType::Released;
307 if search_key_only {
310 new_events.push(into_unhandled_input_event(
311 event.clone().into_with_event_type(KeyEventType::Pressed),
312 device_descriptor.clone(),
313 self.next_event_time(event_time),
314 None,
315 ));
316 }
317 if search_key_only || self.is_regular_keys_pressed() {
322 new_events.push(into_unhandled_input_event(
323 event.into_with_event_type(KeyEventType::Released),
324 device_descriptor,
325 self.next_event_time(event_time),
326 None,
327 ));
328 }
329
330 self.set_search_key_actuated(false);
332 self.set_other_key_events(false);
333 self.set_regular_keys_pressed(false);
334
335 return new_events;
336 } else {
337 }
339 }
340 false => {
341 if key == SEARCH_KEY && event_type == KeyEventType::Pressed {
342 let keys_to_release = self.get_ordered_keys();
344
345 let mut new_events = self.synthesize_input_events(
346 event.clone(),
347 KeyEventType::Released,
348 device_descriptor.clone(),
349 event_time,
350 None,
351 keys_to_release.iter().rev(),
352 |kp: &KeyPair| kp.without_search,
353 );
354 new_events.append(&mut self.synthesize_input_events(
355 event,
356 KeyEventType::Pressed,
357 device_descriptor,
358 event_time,
359 None,
360 keys_to_release.iter(),
361 |kp: &KeyPair| kp.with_search,
362 ));
363
364 self.set_search_key_actuated(true);
365 if !keys_to_release.is_empty() {
366 self.set_other_key_events(true);
367 }
368 return new_events;
369 }
370 }
371 }
372
373 self.update_key_state(event_type, key);
374 let maybe_remapped_key = REMAPPED_KEYS.get(&key);
375 let return_events = if let Some(remapped_keypair) = maybe_remapped_key {
376 let key = if is_search_key_actuated {
377 remapped_keypair.with_search
378 } else {
379 remapped_keypair.without_search
380 };
381 vec![into_unhandled_input_event(
382 event.into_with_key(key),
383 device_descriptor,
384 self.next_event_time(event_time),
385 trace_id,
386 )]
387 } else {
388 let mut events = vec![];
389 if self.is_search_key_actuated()
393 && !self.has_other_key_events()
394 && event_type == KeyEventType::Pressed
395 {
396 let new_event = event
397 .clone()
398 .into_with_key(SEARCH_KEY)
399 .into_with_event_type(KeyEventType::Pressed);
400 events.push(into_unhandled_input_event(
401 new_event,
402 device_descriptor.clone(),
403 self.next_event_time(event_time),
404 None,
405 ));
406 self.set_regular_keys_pressed(true);
407 }
408 events.push(into_unhandled_input_event(
409 event,
410 device_descriptor,
411 self.next_event_time(event_time),
412 trace_id,
413 ));
414 events
417 };
418
419 if event_type == KeyEventType::Pressed && key != SEARCH_KEY && is_search_key_actuated {
422 self.set_other_key_events(true);
423 }
424
425 return_events
426 }
427}
428
429fn into_unhandled_input_event(
430 event: KeyboardEvent,
431 device_descriptor: KeyboardDeviceDescriptor,
432 event_time: zx::MonotonicInstant,
433 trace_id: Option<ftrace::Id>,
434) -> InputEvent {
435 InputEvent {
436 device_event: InputDeviceEvent::Keyboard(event),
437 device_descriptor: device_descriptor.into(),
438 event_time,
439 handled: Handled::No,
440 trace_id,
441 }
442}
443
444#[cfg(test)]
445mod tests {
446 use super::*;
447 use crate::testing_utilities::create_input_event;
448 use std::sync::LazyLock;
449 use test_case::test_case;
450
451 static MATCHING_KEYBOARD_DESCRIPTOR: LazyLock<InputDeviceDescriptor> = LazyLock::new(|| {
452 InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
453 keys: vec![],
454 device_information: fidl_fuchsia_input_report::DeviceInformation {
455 vendor_id: Some(VENDOR_ID),
456 product_id: Some(PRODUCT_ID),
457 version: Some(42),
458 polling_rate: Some(1000),
459 ..Default::default()
460 },
461 device_id: 43,
462 })
463 });
464 static MISMATCHING_KEYBOARD_DESCRIPTOR: LazyLock<InputDeviceDescriptor> = LazyLock::new(|| {
465 InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
466 keys: vec![],
467 device_information: fidl_fuchsia_input_report::DeviceInformation {
468 vendor_id: Some(VENDOR_ID + 10),
469 product_id: Some(PRODUCT_ID),
470 version: Some(42),
471 polling_rate: Some(1000),
472 ..Default::default()
473 },
474 device_id: 43,
475 })
476 });
477
478 async fn run_all_events<T: UnhandledInputHandler>(
479 handler: &Rc<T>,
480 events: Vec<InputEvent>,
481 ) -> Vec<InputEvent> {
482 let handler_clone = || handler.clone();
483 let events_futs = events
484 .into_iter()
485 .map(|e| e.try_into().expect("events are always convertible in tests"))
486 .map(|e| handler_clone().handle_unhandled_input_event(e));
487 let mut events_set = vec![];
489 for events_fut in events_futs.into_iter() {
490 events_set.push(events_fut.await);
491 }
492 events_set.into_iter().flatten().collect()
493 }
494
495 fn new_key_sequence(
499 mut event_time: zx::MonotonicInstant,
500 descriptor: &InputDeviceDescriptor,
501 handled: Handled,
502 keys: Vec<(Key, KeyEventType)>,
503 ) -> Vec<InputEvent> {
504 let mut ret = vec![];
505 for (k, t) in keys {
506 ret.push(create_input_event(KeyboardEvent::new(k, t), descriptor, event_time, handled));
507 event_time = event_time + zx::MonotonicDuration::from_nanos(1);
508 }
509 ret
510 }
511
512 #[test]
513 fn next_event_time() {
514 let inspector = fuchsia_inspect::Inspector::default();
515 let test_node = inspector.root().create_child("test_node");
516 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
517 assert_eq!(
518 zx::MonotonicInstant::from_nanos(10),
519 handler.next_event_time(zx::MonotonicInstant::from_nanos(10))
520 );
521 assert_eq!(
522 zx::MonotonicInstant::from_nanos(11),
523 handler.next_event_time(zx::MonotonicInstant::from_nanos(10))
524 );
525 assert_eq!(
526 zx::MonotonicInstant::from_nanos(12),
527 handler.next_event_time(zx::MonotonicInstant::from_nanos(10))
528 );
529 assert_eq!(
530 zx::MonotonicInstant::from_nanos(13),
531 handler.next_event_time(zx::MonotonicInstant::from_nanos(13))
532 );
533 assert_eq!(
534 zx::MonotonicInstant::from_nanos(14),
535 handler.next_event_time(zx::MonotonicInstant::from_nanos(13))
536 );
537 }
538
539 #[test_case(Key::F1, Key::AcBack; "convert F1")]
542 #[test_case(Key::F2, Key::AcRefresh; "convert F2")]
543 #[test_case(Key::F3, Key::AcFullScreenView; "convert F3")]
544 #[test_case(Key::F4, Key::AcSelectTaskApplication; "convert F4")]
545 #[test_case(Key::F5, Key::BrightnessDown; "convert F5")]
546 #[test_case(Key::F6, Key::BrightnessUp; "convert F6")]
547 #[test_case(Key::F7, Key::PlayPause; "convert F7")]
548 #[test_case(Key::F8, Key::Mute; "convert F8")]
549 #[test_case(Key::F9, Key::VolumeDown; "convert F9")]
550 #[test_case(Key::F10, Key::VolumeUp; "convert F10")]
551 #[test_case(Key::A, Key::A; "do not convert A")]
552 #[test_case(Key::Up, Key::Up; "do not convert Up")]
553 #[test_case(Key::Down, Key::Down; "do not convert Down")]
554 #[test_case(Key::Left, Key::Left; "do not convert Left")]
555 #[test_case(Key::Right, Key::Right; "do not convert Right")]
556 #[test_case(Key::Dot, Key::Dot; "do not convert Dot")]
557 #[test_case(Key::Backspace, Key::Backspace; "do not convert Backspace")]
558 #[fuchsia::test]
559 async fn conversion_matching_keyboard(input_key: Key, output_key: Key) {
560 let inspector = fuchsia_inspect::Inspector::default();
561 let test_node = inspector.root().create_child("test_node");
562 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
563 let input = new_key_sequence(
564 zx::MonotonicInstant::from_nanos(42),
565 &MATCHING_KEYBOARD_DESCRIPTOR,
566 Handled::No,
567 vec![(input_key, KeyEventType::Pressed), (input_key, KeyEventType::Released)],
568 );
569 let actual = run_all_events(&handler, input).await;
570 let expected = new_key_sequence(
571 zx::MonotonicInstant::from_nanos(42),
572 &MATCHING_KEYBOARD_DESCRIPTOR,
573 Handled::No,
574 vec![(output_key, KeyEventType::Pressed), (output_key, KeyEventType::Released)],
575 );
576 pretty_assertions::assert_eq!(expected, actual);
577 }
578
579 #[test_case(Key::F1, Key::F1; "do not convert F1")]
582 #[test_case(Key::F2, Key::F2; "do not convert F2")]
583 #[test_case(Key::F3, Key::F3; "do not convert F3")]
584 #[test_case(Key::F4, Key::F4; "do not convert F4")]
585 #[test_case(Key::F5, Key::F5; "do not convert F5")]
586 #[test_case(Key::F6, Key::F6; "do not convert F6")]
587 #[test_case(Key::F7, Key::F7; "do not convert F7")]
588 #[test_case(Key::F8, Key::F8; "do not convert F8")]
589 #[test_case(Key::F9, Key::F9; "do not convert F9")]
590 #[test_case(Key::F10, Key::F10; "do not convert F10")]
591 #[test_case(Key::A, Key::A; "do not convert A")]
592 #[fuchsia::test]
593 async fn conversion_mismatching_keyboard(input_key: Key, output_key: Key) {
594 let inspector = fuchsia_inspect::Inspector::default();
595 let test_node = inspector.root().create_child("test_node");
596 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
597 let input = new_key_sequence(
598 zx::MonotonicInstant::from_nanos(42),
599 &MISMATCHING_KEYBOARD_DESCRIPTOR,
600 Handled::No,
601 vec![(input_key, KeyEventType::Pressed), (input_key, KeyEventType::Released)],
602 );
603 let actual = run_all_events(&handler, input).await;
604 let expected = new_key_sequence(
605 zx::MonotonicInstant::from_nanos(42),
606 &MISMATCHING_KEYBOARD_DESCRIPTOR,
607 Handled::No,
608 vec![(output_key, KeyEventType::Pressed), (output_key, KeyEventType::Released)],
609 );
610 pretty_assertions::assert_eq!(expected, actual);
611 }
612
613 #[fuchsia::test]
620 async fn search_key_only() {
621 let inspector = fuchsia_inspect::Inspector::default();
622 let test_node = inspector.root().create_child("test_node");
623 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
624 let input = new_key_sequence(
625 zx::MonotonicInstant::from_nanos(42),
626 &MATCHING_KEYBOARD_DESCRIPTOR,
627 Handled::No,
628 vec![(SEARCH_KEY, KeyEventType::Pressed), (SEARCH_KEY, KeyEventType::Released)],
629 );
630 let actual = run_all_events(&handler, input).await;
631 let expected = new_key_sequence(
632 zx::MonotonicInstant::from_nanos(43),
633 &MATCHING_KEYBOARD_DESCRIPTOR,
634 Handled::No,
635 vec![(SEARCH_KEY, KeyEventType::Pressed), (SEARCH_KEY, KeyEventType::Released)],
636 );
637 pretty_assertions::assert_eq!(expected, actual);
638 }
639
640 #[fuchsia::test]
649 async fn f1_conversion() {
650 let inspector = fuchsia_inspect::Inspector::default();
651 let test_node = inspector.root().create_child("test_node");
652 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
653 let input = new_key_sequence(
654 zx::MonotonicInstant::from_nanos(42),
655 &MATCHING_KEYBOARD_DESCRIPTOR,
656 Handled::No,
657 vec![
658 (SEARCH_KEY, KeyEventType::Pressed),
659 (Key::F1, KeyEventType::Pressed),
660 (Key::F1, KeyEventType::Released),
661 (SEARCH_KEY, KeyEventType::Released),
662 ],
663 );
664 let actual = run_all_events(&handler, input).await;
665 let expected = new_key_sequence(
666 zx::MonotonicInstant::from_nanos(43),
667 &MATCHING_KEYBOARD_DESCRIPTOR,
668 Handled::No,
669 vec![(Key::F1, KeyEventType::Pressed), (Key::F1, KeyEventType::Released)],
670 );
671 pretty_assertions::assert_eq!(expected, actual);
672 }
673
674 #[test_case(Key::F1, Key::F1; "do not convert F1")]
685 #[test_case(Key::F2, Key::F2; "do not convert F2")]
686 #[test_case(Key::F3, Key::F3; "do not convert F3")]
687 #[test_case(Key::F4, Key::F4; "do not convert F4")]
688 #[test_case(Key::F5, Key::F5; "do not convert F5")]
689 #[test_case(Key::F6, Key::F6; "do not convert F6")]
690 #[test_case(Key::F7, Key::F7; "do not convert F7")]
691 #[test_case(Key::F8, Key::F8; "do not convert F8")]
692 #[test_case(Key::F9, Key::F9; "do not convert F9")]
693 #[test_case(Key::F10, Key::F10; "do not convert F10")]
694 #[test_case(Key::Up, Key::PageUp; "convert Up")]
695 #[test_case(Key::Down, Key::PageDown; "convert Down")]
696 #[test_case(Key::Left, Key::Home; "convert Left")]
697 #[test_case(Key::Right, Key::End; "convert Right")]
698 #[test_case(Key::Dot, Key::Insert; "convert Dot")]
699 #[test_case(Key::Backspace, Key::Delete; "convert Backspace")]
700 #[fuchsia::test]
701 async fn with_search_key_pressed(input_key: Key, output_key: Key) {
702 let inspector = fuchsia_inspect::Inspector::default();
703 let test_node = inspector.root().create_child("test_node");
704 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
705 let input = new_key_sequence(
706 zx::MonotonicInstant::from_nanos(42),
707 &MATCHING_KEYBOARD_DESCRIPTOR,
708 Handled::No,
709 vec![
710 (SEARCH_KEY, KeyEventType::Pressed),
711 (input_key, KeyEventType::Pressed),
712 (input_key, KeyEventType::Released),
713 (SEARCH_KEY, KeyEventType::Released),
714 ],
715 );
716 let actual = run_all_events(&handler, input).await;
717 let expected = new_key_sequence(
718 zx::MonotonicInstant::from_nanos(43),
719 &MATCHING_KEYBOARD_DESCRIPTOR,
720 Handled::No,
721 vec![(output_key, KeyEventType::Pressed), (output_key, KeyEventType::Released)],
722 );
723 pretty_assertions::assert_eq!(expected, actual);
724 }
725
726 #[fuchsia::test]
733 async fn search_released_before_f1() {
734 let inspector = fuchsia_inspect::Inspector::default();
735 let test_node = inspector.root().create_child("test_node");
736 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
737 let input = new_key_sequence(
738 zx::MonotonicInstant::from_nanos(42),
739 &MATCHING_KEYBOARD_DESCRIPTOR,
740 Handled::No,
741 vec![
742 (SEARCH_KEY, KeyEventType::Pressed),
743 (Key::F1, KeyEventType::Pressed),
744 (SEARCH_KEY, KeyEventType::Released),
745 (Key::F1, KeyEventType::Released),
746 ],
747 );
748 let actual = run_all_events(&handler, input).await;
749 let expected = new_key_sequence(
750 zx::MonotonicInstant::from_nanos(43),
751 &MATCHING_KEYBOARD_DESCRIPTOR,
752 Handled::No,
753 vec![
754 (Key::F1, KeyEventType::Pressed),
755 (Key::F1, KeyEventType::Released),
756 (Key::AcBack, KeyEventType::Pressed),
757 (Key::AcBack, KeyEventType::Released),
758 ],
759 );
760 pretty_assertions::assert_eq!(expected, actual);
761 }
762
763 #[fuchsia::test]
772 async fn search_key_a_is_delayed_leftmeta_a() {
773 let inspector = fuchsia_inspect::Inspector::default();
774 let test_node = inspector.root().create_child("test_node");
775 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
776 let input = new_key_sequence(
777 zx::MonotonicInstant::from_nanos(42),
778 &MATCHING_KEYBOARD_DESCRIPTOR,
779 Handled::No,
780 vec![
781 (SEARCH_KEY, KeyEventType::Pressed),
782 (Key::A, KeyEventType::Pressed),
783 (Key::A, KeyEventType::Released),
784 (SEARCH_KEY, KeyEventType::Released),
785 ],
786 );
787 let actual = run_all_events(&handler, input).await;
788 let expected = new_key_sequence(
789 zx::MonotonicInstant::from_nanos(43),
790 &MATCHING_KEYBOARD_DESCRIPTOR,
791 Handled::No,
792 vec![
793 (Key::LeftMeta, KeyEventType::Pressed),
794 (Key::A, KeyEventType::Pressed),
795 (Key::A, KeyEventType::Released),
796 (Key::LeftMeta, KeyEventType::Released),
797 ],
798 );
799 pretty_assertions::assert_eq!(expected, actual);
800 }
801
802 #[fuchsia::test]
810 async fn f1_and_f2_interleaved_conversion() {
811 let inspector = fuchsia_inspect::Inspector::default();
812 let test_node = inspector.root().create_child("test_node");
813 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
814 let input = new_key_sequence(
815 zx::MonotonicInstant::from_nanos(42),
816 &MATCHING_KEYBOARD_DESCRIPTOR,
817 Handled::No,
818 vec![
819 (SEARCH_KEY, KeyEventType::Pressed),
820 (Key::F1, KeyEventType::Pressed),
821 (Key::F2, KeyEventType::Pressed),
822 (Key::F1, KeyEventType::Released),
823 (Key::F2, KeyEventType::Released),
824 (SEARCH_KEY, KeyEventType::Released),
825 ],
826 );
827 let actual = run_all_events(&handler, input).await;
828 let expected = new_key_sequence(
829 zx::MonotonicInstant::from_nanos(43),
830 &MATCHING_KEYBOARD_DESCRIPTOR,
831 Handled::No,
832 vec![
833 (Key::F1, KeyEventType::Pressed),
834 (Key::F2, KeyEventType::Pressed),
835 (Key::F1, KeyEventType::Released),
836 (Key::F2, KeyEventType::Released),
837 ],
838 );
839 pretty_assertions::assert_eq!(expected, actual);
840 }
841
842 #[fuchsia::test]
849 async fn search_pressed_before_f1_released() {
850 let inspector = fuchsia_inspect::Inspector::default();
851 let test_node = inspector.root().create_child("test_node");
852 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
853 let input = new_key_sequence(
854 zx::MonotonicInstant::from_nanos(42),
855 &MATCHING_KEYBOARD_DESCRIPTOR,
856 Handled::No,
857 vec![
858 (Key::F1, KeyEventType::Pressed),
859 (SEARCH_KEY, KeyEventType::Pressed),
860 (Key::F1, KeyEventType::Released),
861 (SEARCH_KEY, KeyEventType::Released),
862 ],
863 );
864 let actual = run_all_events(&handler, input).await;
865 let expected = new_key_sequence(
866 zx::MonotonicInstant::from_nanos(42),
867 &MATCHING_KEYBOARD_DESCRIPTOR,
868 Handled::No,
869 vec![
870 (Key::AcBack, KeyEventType::Pressed),
871 (Key::AcBack, KeyEventType::Released),
872 (Key::F1, KeyEventType::Pressed),
873 (Key::F1, KeyEventType::Released),
874 ],
875 );
876 pretty_assertions::assert_eq!(expected, actual);
877 }
878
879 #[fuchsia::test]
893 async fn search_pressed_while_f1_and_f2_pressed() {
894 let inspector = fuchsia_inspect::Inspector::default();
895 let test_node = inspector.root().create_child("test_node");
896 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
897 let input = new_key_sequence(
898 zx::MonotonicInstant::from_nanos(42),
899 &MATCHING_KEYBOARD_DESCRIPTOR,
900 Handled::No,
901 vec![
902 (Key::F1, KeyEventType::Pressed),
903 (Key::F2, KeyEventType::Pressed),
904 (SEARCH_KEY, KeyEventType::Pressed),
905 (Key::F1, KeyEventType::Released),
906 (Key::F2, KeyEventType::Released),
907 (SEARCH_KEY, KeyEventType::Released),
908 ],
909 );
910 let actual = run_all_events(&handler, input).await;
911 let expected = new_key_sequence(
912 zx::MonotonicInstant::from_nanos(42),
913 &MATCHING_KEYBOARD_DESCRIPTOR,
914 Handled::No,
915 vec![
916 (Key::AcBack, KeyEventType::Pressed),
917 (Key::AcRefresh, KeyEventType::Pressed),
918 (Key::AcRefresh, KeyEventType::Released),
919 (Key::AcBack, KeyEventType::Released),
920 (Key::F1, KeyEventType::Pressed),
921 (Key::F2, KeyEventType::Pressed),
922 (Key::F1, KeyEventType::Released),
923 (Key::F2, KeyEventType::Released),
924 ],
925 );
926 pretty_assertions::assert_eq!(expected, actual);
927 }
928
929 #[fuchsia::test]
945 async fn key_combination() {
946 let inspector = fuchsia_inspect::Inspector::default();
947 let test_node = inspector.root().create_child("test_node");
948 let handler = ChromebookKeyboardHandler::new(&test_node, metrics::MetricsLogger::default());
949 let input = new_key_sequence(
950 zx::MonotonicInstant::from_nanos(42),
951 &MATCHING_KEYBOARD_DESCRIPTOR,
952 Handled::No,
953 vec![
954 (Key::F1, KeyEventType::Pressed),
955 (SEARCH_KEY, KeyEventType::Pressed),
956 (Key::A, KeyEventType::Pressed),
957 (Key::F1, KeyEventType::Released),
958 (Key::F2, KeyEventType::Pressed),
959 (Key::A, KeyEventType::Released),
960 (SEARCH_KEY, KeyEventType::Released),
961 (Key::F2, KeyEventType::Released),
962 ],
963 );
964 let actual = run_all_events(&handler, input).await;
965 let expected = new_key_sequence(
966 zx::MonotonicInstant::from_nanos(42),
967 &MATCHING_KEYBOARD_DESCRIPTOR,
968 Handled::No,
969 vec![
970 (Key::AcBack, KeyEventType::Pressed),
971 (Key::AcBack, KeyEventType::Released),
972 (Key::F1, KeyEventType::Pressed),
973 (Key::A, KeyEventType::Pressed),
974 (Key::F1, KeyEventType::Released),
975 (Key::F2, KeyEventType::Pressed),
976 (Key::A, KeyEventType::Released),
977 (Key::F2, KeyEventType::Released),
978 (Key::AcRefresh, KeyEventType::Pressed),
979 (Key::AcRefresh, KeyEventType::Released),
980 ],
981 );
982 pretty_assertions::assert_eq!(expected, actual);
983 }
984
985 #[fuchsia::test]
986 async fn chromebook_keyboard_handler_initialized_with_inspect_node() {
987 let inspector = fuchsia_inspect::Inspector::default();
988 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
989 let _handler =
990 ChromebookKeyboardHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
991 diagnostics_assertions::assert_data_tree!(inspector, root: {
992 input_handlers_node: {
993 chromebook_keyboard_handler: {
994 events_received_count: 0u64,
995 events_handled_count: 0u64,
996 last_received_timestamp_ns: 0u64,
997 "fuchsia.inspect.Health": {
998 status: "STARTING_UP",
999 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1002 },
1003 }
1004 }
1005 });
1006 }
1007
1008 #[fuchsia::test]
1009 async fn chromebook_keyboard_handler_inspect_counts_events() {
1010 let inspector = fuchsia_inspect::Inspector::default();
1011 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
1012 let handler =
1013 ChromebookKeyboardHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
1014 let events = new_key_sequence(
1015 zx::MonotonicInstant::from_nanos(42),
1016 &MATCHING_KEYBOARD_DESCRIPTOR,
1017 Handled::No,
1018 vec![
1019 (Key::F1, KeyEventType::Pressed),
1020 (Key::F1, KeyEventType::Released),
1021 (Key::Down, KeyEventType::Pressed),
1022 (Key::Down, KeyEventType::Released),
1023 ],
1024 );
1025 let _ = run_all_events(&handler, events).await;
1026 diagnostics_assertions::assert_data_tree!(inspector, root: {
1027 input_handlers_node: {
1028 chromebook_keyboard_handler: {
1029 events_received_count: 4u64,
1030 events_handled_count: 0u64,
1031 last_received_timestamp_ns: 45u64,
1032 "fuchsia.inspect.Health": {
1033 status: "STARTING_UP",
1034 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1037 },
1038 }
1039 }
1040 });
1041 }
1042}