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