1use crate::input_device::{self, Handled, InputDeviceBinding, InputDeviceStatus, InputEvent};
6use crate::{Dispatcher, Transport, metrics, utils};
7use anyhow::{Error, Result, format_err};
8use async_trait::async_trait;
9use fidl_fuchsia_ui_input3 as fidl_ui_input3;
10use fidl_fuchsia_ui_input3::KeyEventType;
11use fidl_next_fuchsia_input_report::InputReport;
12use fuchsia_inspect::health::Reporter;
13use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
14use metrics_registry::*;
15
16#[derive(Clone, Debug, PartialEq)]
32pub struct KeyboardEvent {
33 key: fidl_fuchsia_input::Key,
35
36 event_type: KeyEventType,
38
39 modifiers: Option<fidl_ui_input3::Modifiers>,
41
42 lock_state: Option<fidl_ui_input3::LockState>,
44
45 keymap: Option<String>,
48
49 key_meaning: Option<fidl_fuchsia_ui_input3::KeyMeaning>,
54
55 repeat_sequence: u32,
61}
62
63impl KeyboardEvent {
64 pub fn new(key: fidl_fuchsia_input::Key, event_type: KeyEventType) -> Self {
67 KeyboardEvent {
68 key,
69 event_type,
70 modifiers: None,
71 lock_state: None,
72 keymap: None,
73 key_meaning: None,
74 repeat_sequence: 0,
75 }
76 }
77
78 pub fn get_key(&self) -> fidl_fuchsia_input::Key {
79 self.key
80 }
81
82 pub fn into_with_key(self, key: fidl_fuchsia_input::Key) -> Self {
84 Self { key, ..self }
85 }
86
87 pub fn get_event_type(&self) -> KeyEventType {
88 self.event_type
89 }
90
91 pub fn into_with_event_type(self, event_type: KeyEventType) -> Self {
93 Self { event_type, ..self }
94 }
95
96 pub fn into_with_folded_event(self) -> Self {
98 Self { event_type: self.get_event_type_folded(), ..self }
99 }
100
101 pub fn get_event_type_folded(&self) -> KeyEventType {
103 match self.event_type {
104 KeyEventType::Pressed | KeyEventType::Sync => KeyEventType::Pressed,
105 KeyEventType::Released | KeyEventType::Cancel => KeyEventType::Released,
106 }
107 }
108
109 pub fn into_with_modifiers(self, modifiers: Option<fidl_ui_input3::Modifiers>) -> Self {
111 Self { modifiers, ..self }
112 }
113
114 pub fn get_modifiers(&self) -> Option<fidl_ui_input3::Modifiers> {
116 self.modifiers
117 }
118
119 pub fn get_unsided_modifiers(&self) -> fidl_fuchsia_ui_input3::Modifiers {
123 use fidl_fuchsia_ui_input3::Modifiers;
124 let mut modifiers = self.modifiers.unwrap_or(Modifiers::empty());
125 modifiers.set(
126 Modifiers::LEFT_ALT
127 | Modifiers::LEFT_CTRL
128 | Modifiers::LEFT_SHIFT
129 | Modifiers::LEFT_META
130 | Modifiers::RIGHT_ALT
131 | Modifiers::RIGHT_CTRL
132 | Modifiers::RIGHT_SHIFT
133 | Modifiers::RIGHT_META,
134 false,
135 );
136 modifiers
137 }
138
139 pub fn into_with_lock_state(self, lock_state: Option<fidl_ui_input3::LockState>) -> Self {
141 Self { lock_state, ..self }
142 }
143
144 pub fn get_lock_state(&self) -> Option<fidl_ui_input3::LockState> {
146 self.lock_state
147 }
148
149 pub fn into_with_keymap(self, keymap: Option<String>) -> Self {
152 Self { keymap, ..self }
153 }
154
155 pub fn get_keymap(&self) -> Option<String> {
157 self.keymap.clone()
158 }
159
160 pub fn into_with_key_meaning(
162 self,
163 key_meaning: Option<fidl_fuchsia_ui_input3::KeyMeaning>,
164 ) -> Self {
165 Self { key_meaning, ..self }
166 }
167
168 pub fn get_key_meaning(&self) -> Option<fidl_fuchsia_ui_input3::KeyMeaning> {
170 self.key_meaning
171 }
172
173 pub fn get_repeat_sequence(&self) -> u32 {
177 self.repeat_sequence
178 }
179
180 pub fn into_with_repeat_sequence(self, repeat_sequence: u32) -> Self {
183 Self { repeat_sequence, ..self }
184 }
185
186 #[cfg(test)]
188 pub(crate) fn from_key_event_at_time(
189 &self,
190 event_time: zx::MonotonicInstant,
191 ) -> fidl_ui_input3::KeyEvent {
192 fidl_ui_input3::KeyEvent {
193 timestamp: Some(event_time.into_nanos()),
194 type_: Some(self.event_type),
195 key: Some(self.key),
196 modifiers: self.modifiers,
197 lock_state: self.lock_state,
198 repeat_sequence: Some(self.repeat_sequence),
199 key_meaning: self.key_meaning,
200 ..Default::default()
201 }
202 }
203}
204
205impl KeyboardEvent {
206 pub fn same_key(this: &KeyboardEvent, that: &KeyboardEvent) -> bool {
208 this.get_key() == that.get_key()
209 }
210}
211
212#[derive(Clone, Debug, PartialEq)]
214pub struct KeyboardDeviceDescriptor {
215 pub keys: Vec<fidl_fuchsia_input::Key>,
217
218 pub device_information: fidl_fuchsia_input_report::DeviceInformation,
220
221 pub device_id: u32,
223}
224
225#[cfg(test)]
226impl Default for KeyboardDeviceDescriptor {
227 fn default() -> Self {
228 KeyboardDeviceDescriptor {
229 keys: vec![],
230 device_information: fidl_fuchsia_input_report::DeviceInformation {
231 vendor_id: Some(0),
232 product_id: Some(0),
233 version: Some(0),
234 polling_rate: Some(0),
235 ..Default::default()
236 },
237 device_id: 0,
238 }
239 }
240}
241
242pub struct KeyboardBinding {
248 event_sender: UnboundedSender<Vec<InputEvent>>,
250
251 device_descriptor: KeyboardDeviceDescriptor,
253}
254
255#[async_trait]
256impl input_device::InputDeviceBinding for KeyboardBinding {
257 fn input_event_sender(&self) -> UnboundedSender<Vec<InputEvent>> {
258 self.event_sender.clone()
259 }
260
261 fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor {
262 input_device::InputDeviceDescriptor::Keyboard(self.device_descriptor.clone())
263 }
264}
265
266impl KeyboardBinding {
267 pub async fn new(
282 device_proxy: fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
283 device_id: u32,
284 input_event_sender: UnboundedSender<Vec<InputEvent>>,
285 device_node: fuchsia_inspect::Node,
286 feature_flags: input_device::InputPipelineFeatureFlags,
287 metrics_logger: metrics::MetricsLogger,
288 ) -> Result<Self, Error> {
289 let (device_binding, mut inspect_status) = Self::bind_device(
290 &device_proxy,
291 input_event_sender,
292 device_id,
293 device_node,
294 metrics_logger.clone(),
295 )
296 .await?;
297 inspect_status.health_node.set_ok();
298 input_device::initialize_report_stream(
299 device_proxy,
300 device_binding.get_device_descriptor(),
301 device_binding.input_event_sender(),
302 inspect_status,
303 metrics_logger.clone(),
304 feature_flags,
305 Self::process_reports,
306 );
307
308 Ok(device_binding)
309 }
310
311 pub fn to_modifiers(keys: &[&fidl_fuchsia_input::Key]) -> Option<fidl_ui_input3::Modifiers> {
322 let mut modifiers = fidl_ui_input3::Modifiers::empty();
323 for key in keys {
324 let modifier = match key {
325 fidl_fuchsia_input::Key::CapsLock => Some(fidl_ui_input3::Modifiers::CAPS_LOCK),
326 fidl_fuchsia_input::Key::NumLock => Some(fidl_ui_input3::Modifiers::NUM_LOCK),
327 fidl_fuchsia_input::Key::ScrollLock => Some(fidl_ui_input3::Modifiers::SCROLL_LOCK),
328 _ => None,
329 };
330 if let Some(modifier) = modifier {
331 modifiers.insert(modifier);
332 };
333 }
334 if modifiers.is_empty() {
335 return None;
336 }
337 Some(modifiers)
338 }
339
340 async fn bind_device(
352 device: &fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
353 input_event_sender: UnboundedSender<Vec<InputEvent>>,
354 device_id: u32,
355 device_node: fuchsia_inspect::Node,
356 metrics_logger: metrics::MetricsLogger,
357 ) -> Result<(Self, InputDeviceStatus), Error> {
358 let mut input_device_status = InputDeviceStatus::new(device_node);
359 let descriptor = match device.get_descriptor().await {
360 Ok(descriptor) => descriptor.descriptor,
361 Err(_) => {
362 input_device_status.health_node.set_unhealthy("Could not get device descriptor.");
363 return Err(format_err!("Could not get descriptor for device_id: {}", device_id));
364 }
365 };
366
367 let device_info = descriptor.device_information.ok_or_else(|| {
368 input_device_status.health_node.set_unhealthy("Empty device_information in descriptor");
369 metrics_logger.log_error(
372 InputPipelineErrorMetricDimensionEvent::KeyboardEmptyDeviceInfo,
373 std::format!("DRIVER BUG: empty device_information for device_id: {}", device_id),
374 );
375 format_err!("empty device info for device_id: {}", device_id)
376 })?;
377 match descriptor.keyboard {
378 Some(fidl_next_fuchsia_input_report::KeyboardDescriptor {
379 input: Some(fidl_next_fuchsia_input_report::KeyboardInputDescriptor { keys3, .. }),
380 output: _,
381 ..
382 }) => Ok((
383 KeyboardBinding {
384 event_sender: input_event_sender,
385 device_descriptor: KeyboardDeviceDescriptor {
386 keys: keys3
387 .unwrap_or_default()
388 .into_iter()
389 .map(|k| utils::key_to_old(&k))
390 .collect(),
391 device_information: fidl_fuchsia_input_report::DeviceInformation {
392 vendor_id: device_info.vendor_id,
393 product_id: device_info.product_id,
394 version: device_info.version,
395 polling_rate: device_info.polling_rate,
396 ..Default::default()
397 },
398 device_id,
399 },
400 },
401 input_device_status,
402 )),
403 device_descriptor => {
404 input_device_status
405 .health_node
406 .set_unhealthy("Keyboard Device Descriptor failed to parse.");
407 Err(format_err!(
408 "Keyboard Device Descriptor failed to parse: \n {:?}",
409 device_descriptor
410 ))
411 }
412 }
413 }
414
415 fn process_reports(
437 reports: &[fidl_next_fuchsia_input_report::wire::InputReport<'_>],
438 mut previous_report: Option<InputReport>,
439 device_descriptor: &input_device::InputDeviceDescriptor,
440 input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
441 inspect_status: &InputDeviceStatus,
442 metrics_logger: &metrics::MetricsLogger,
443 _feature_flags: &input_device::InputPipelineFeatureFlags,
444 ) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
445 fuchsia_trace::duration!("input", "keyboard-binding-process-report", "num_reports" => reports.len());
446 let (inspect_sender, inspect_receiver) = futures::channel::mpsc::unbounded();
447
448 for report in reports {
449 previous_report = Self::process_report(
450 report,
451 previous_report,
452 device_descriptor,
453 input_event_sender,
454 inspect_status,
455 metrics_logger,
456 inspect_sender.clone(),
457 );
458 }
459 (previous_report, Some(inspect_receiver))
460 }
461
462 fn process_report(
463 report: &fidl_next_fuchsia_input_report::wire::InputReport<'_>,
464 previous_report: Option<InputReport>,
465 device_descriptor: &input_device::InputDeviceDescriptor,
466 input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
467 inspect_status: &InputDeviceStatus,
468 metrics_logger: &metrics::MetricsLogger,
469 inspect_sender: UnboundedSender<InputEvent>,
470 ) -> Option<InputReport> {
471 if let Some(trace_id) = report.trace_id() {
472 fuchsia_trace::flow_end!("input", "input_report", trace_id.0.into());
473 }
474
475 let tracing_id = fuchsia_trace::Id::new();
476 fuchsia_trace::flow_begin!("input", "key_event_thread", tracing_id);
477
478 inspect_status.count_received_report_wire(report);
479 match report.keyboard() {
481 None => {
482 inspect_status.count_filtered_report();
483 return previous_report;
484 }
485 _ => (),
486 };
487
488 let new_keys = match KeyboardBinding::parse_pressed_keys_wire(report) {
489 Some(keys) => keys,
490 None => {
491 metrics_logger.log_error(
497 InputPipelineErrorMetricDimensionEvent::KeyboardFailedToParse,
498 std::format!("Failed to parse keyboard keys: {:?}", report),
499 );
500 inspect_status.count_filtered_report();
501 return previous_report;
502 }
503 };
504
505 let previous_keys: Vec<fidl_fuchsia_input::Key> = previous_report
506 .as_ref()
507 .and_then(|unwrapped_report| KeyboardBinding::parse_pressed_keys(&unwrapped_report))
508 .unwrap_or_default();
509
510 KeyboardBinding::send_key_events(
511 &new_keys,
512 &previous_keys,
513 device_descriptor.clone(),
514 zx::MonotonicInstant::get(),
515 input_event_sender.clone(),
516 inspect_sender,
517 metrics_logger,
518 tracing_id,
519 );
520
521 let natural_report = utils::input_report_to_natural(report);
522 Some(natural_report)
523 }
524
525 fn parse_pressed_keys(input_report: &InputReport) -> Option<Vec<fidl_fuchsia_input::Key>> {
535 let keyboard = input_report.keyboard.as_ref()?;
536 let keys = keyboard.pressed_keys3.as_ref()?;
537 Some(keys.iter().map(utils::key_to_old).collect())
538 }
539
540 fn parse_pressed_keys_wire(
541 input_report: &fidl_next_fuchsia_input_report::wire::InputReport<'_>,
542 ) -> Option<Vec<fidl_fuchsia_input::Key>> {
543 input_report
544 .keyboard()
545 .and_then(|unwrapped_keyboard| unwrapped_keyboard.pressed_keys3())
546 .map(|unwrapped_keys| {
547 unwrapped_keys
548 .iter()
549 .map(|&k| {
550 let natural_key = fidl_next::FromWire::from_wire(k);
551 utils::key_to_old(&natural_key)
552 })
553 .collect()
554 })
555 }
556
557 fn send_key_events(
566 new_keys: &Vec<fidl_fuchsia_input::Key>,
567 previous_keys: &Vec<fidl_fuchsia_input::Key>,
568 device_descriptor: input_device::InputDeviceDescriptor,
569 event_time: zx::MonotonicInstant,
570 input_event_sender: UnboundedSender<Vec<InputEvent>>,
571 inspect_sender: UnboundedSender<input_device::InputEvent>,
572 metrics_logger: &metrics::MetricsLogger,
573 tracing_id: fuchsia_trace::Id,
574 ) {
575 fn dispatch_events(
579 key_events: Vec<(fidl_fuchsia_input::Key, fidl_fuchsia_ui_input3::KeyEventType)>,
580 device_descriptor: input_device::InputDeviceDescriptor,
581 event_time: zx::MonotonicInstant,
582 input_event_sender: UnboundedSender<Vec<input_device::InputEvent>>,
583 inspect_sender: UnboundedSender<input_device::InputEvent>,
584 metrics_logger: metrics::MetricsLogger,
585 tracing_id: fuchsia_trace::Id,
586 ) {
587 Dispatcher::spawn_local(async move {
588 fuchsia_trace::duration!("input", "key_event_thread");
589 fuchsia_trace::flow_end!("input", "key_event_thread", tracing_id);
590
591 let mut event_time = event_time;
592 for (key, event_type) in key_events.into_iter() {
593 let trace_id = fuchsia_trace::Id::new();
594 fuchsia_trace::duration!("input", "keyboard_event_in_binding");
595 fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
596
597 let event = input_device::InputEvent {
598 device_event: input_device::InputDeviceEvent::Keyboard(KeyboardEvent::new(
599 key, event_type,
600 )),
601 device_descriptor: device_descriptor.clone(),
602 event_time,
603 handled: Handled::No,
604 trace_id: Some(trace_id),
605 };
606 match input_event_sender.unbounded_send(vec![event.clone()]) {
607 Err(error) => {
608 metrics_logger.log_error(
609 InputPipelineErrorMetricDimensionEvent::KeyboardFailedToSendKeyboardEvent,
610 std::format!(
611 "Failed to send KeyboardEvent for key: {:?}, event_type: {:?}: {:?}",
612 key,
613 event_type,
614 error));
615 }
616 _ => {
617 let _ = inspect_sender.unbounded_send(event).expect("Failed to count generated KeyboardEvent in Input Pipeline Inspect tree.");
618 }
619 }
620 event_time = event_time + zx::MonotonicDuration::from_nanos(1);
625 }
626 }).detach();
627 }
628
629 let pressed_keys = new_keys
632 .iter()
633 .cloned()
634 .filter(|key| !previous_keys.contains(key))
635 .map(|k| (k, fidl_fuchsia_ui_input3::KeyEventType::Pressed));
636
637 let released_keys = previous_keys
640 .iter()
641 .cloned()
642 .filter(|key| !new_keys.contains(key))
643 .map(|k| (k, fidl_fuchsia_ui_input3::KeyEventType::Released));
644
645 let all_keys = released_keys.chain(pressed_keys).collect::<Vec<_>>();
650
651 dispatch_events(
652 all_keys,
653 device_descriptor,
654 event_time,
655 input_event_sender,
656 inspect_sender,
657 metrics_logger.clone(),
658 tracing_id,
659 );
660 }
661}
662
663#[cfg(test)]
664mod tests {
665 use super::*;
666 use crate::testing_utilities;
667 use fuchsia_async as fasync;
668 use futures::StreamExt;
669
670 #[fasync::run_singlethreaded(test)]
673 async fn pressed_key() {
674 let descriptor = input_device::InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
675 keys: vec![fidl_fuchsia_input::Key::A],
676 ..Default::default()
677 });
678 let (event_time_i64, _) = testing_utilities::event_times();
679
680 let reports = vec![testing_utilities::create_keyboard_input_report(
681 vec![fidl_fuchsia_input::Key::A],
682 event_time_i64,
683 )];
684 let expected_events = vec![testing_utilities::create_keyboard_event(
685 fidl_fuchsia_input::Key::A,
686 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
687 None,
688 &descriptor,
689 None,
690 )];
691
692 assert_input_report_sequence_generates_events!(
693 input_reports: reports,
694 expected_events: expected_events,
695 device_descriptor: descriptor,
696 device_type: KeyboardBinding,
697 );
698 }
699
700 #[fasync::run_singlethreaded(test)]
703 async fn released_key() {
704 let descriptor = input_device::InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
705 keys: vec![fidl_fuchsia_input::Key::A],
706 ..Default::default()
707 });
708 let (event_time_i64, _) = testing_utilities::event_times();
709
710 let reports = vec![
711 testing_utilities::create_keyboard_input_report(
712 vec![fidl_fuchsia_input::Key::A],
713 event_time_i64,
714 ),
715 testing_utilities::create_keyboard_input_report(vec![], event_time_i64),
716 ];
717
718 let expected_events = vec![
719 testing_utilities::create_keyboard_event(
720 fidl_fuchsia_input::Key::A,
721 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
722 None,
723 &descriptor,
724 None,
725 ),
726 testing_utilities::create_keyboard_event(
727 fidl_fuchsia_input::Key::A,
728 fidl_fuchsia_ui_input3::KeyEventType::Released,
729 None,
730 &descriptor,
731 None,
732 ),
733 ];
734
735 assert_input_report_sequence_generates_events!(
736 input_reports: reports,
737 expected_events: expected_events,
738 device_descriptor: descriptor.clone(),
739 device_type: KeyboardBinding,
740 );
741 }
742
743 #[fasync::run_singlethreaded(test)]
746 async fn multiple_pressed_event_filtering() {
747 let descriptor = input_device::InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
748 keys: vec![fidl_fuchsia_input::Key::A],
749 ..Default::default()
750 });
751 let (event_time_i64, _) = testing_utilities::event_times();
752
753 let reports = vec![
754 testing_utilities::create_keyboard_input_report(
755 vec![fidl_fuchsia_input::Key::A],
756 event_time_i64,
757 ),
758 testing_utilities::create_keyboard_input_report(
759 vec![fidl_fuchsia_input::Key::A],
760 event_time_i64,
761 ),
762 ];
763
764 let expected_events = vec![testing_utilities::create_keyboard_event(
765 fidl_fuchsia_input::Key::A,
766 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
767 None,
768 &descriptor,
769 None,
770 )];
771
772 assert_input_report_sequence_generates_events!(
773 input_reports: reports,
774 expected_events: expected_events,
775 device_descriptor: descriptor,
776 device_type: KeyboardBinding,
777 );
778 }
779
780 #[fasync::run_singlethreaded(test)]
782 async fn pressed_and_released_keys() {
783 let descriptor = input_device::InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
784 keys: vec![fidl_fuchsia_input::Key::A, fidl_fuchsia_input::Key::B],
785 ..Default::default()
786 });
787 let (event_time_i64, _) = testing_utilities::event_times();
788
789 let reports = vec![
790 testing_utilities::create_keyboard_input_report(
791 vec![fidl_fuchsia_input::Key::A],
792 event_time_i64,
793 ),
794 testing_utilities::create_keyboard_input_report(
795 vec![fidl_fuchsia_input::Key::B],
796 event_time_i64,
797 ),
798 ];
799
800 let expected_events = vec![
801 testing_utilities::create_keyboard_event(
802 fidl_fuchsia_input::Key::A,
803 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
804 None,
805 &descriptor,
806 None,
807 ),
808 testing_utilities::create_keyboard_event(
809 fidl_fuchsia_input::Key::A,
810 fidl_fuchsia_ui_input3::KeyEventType::Released,
811 None,
812 &descriptor,
813 None,
814 ),
815 testing_utilities::create_keyboard_event(
816 fidl_fuchsia_input::Key::B,
817 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
818 None,
819 &descriptor,
820 None,
821 ),
822 ];
823
824 assert_input_report_sequence_generates_events!(
825 input_reports: reports,
826 expected_events: expected_events,
827 device_descriptor: descriptor,
828 device_type: KeyboardBinding,
829 );
830 }
831
832 #[fuchsia::test]
833 fn get_unsided_modifiers() {
834 use fidl_ui_input3::Modifiers;
835 let event = KeyboardEvent::new(fidl_fuchsia_input::Key::A, KeyEventType::Pressed)
836 .into_with_modifiers(Some(Modifiers::all()));
837 assert_eq!(
838 event.get_unsided_modifiers(),
839 Modifiers::CAPS_LOCK
840 | Modifiers::NUM_LOCK
841 | Modifiers::SCROLL_LOCK
842 | Modifiers::FUNCTION
843 | Modifiers::SYMBOL
844 | Modifiers::SHIFT
845 | Modifiers::ALT
846 | Modifiers::ALT_GRAPH
847 | Modifiers::META
848 | Modifiers::CTRL
849 )
850 }
851
852 #[fuchsia::test]
853 fn conversion_fills_out_all_fields() {
854 use fidl_fuchsia_input::Key;
855 use fidl_ui_input3::{KeyMeaning, LockState, Modifiers, NonPrintableKey};
856 let event = KeyboardEvent::new(Key::A, KeyEventType::Pressed)
857 .into_with_modifiers(Some(Modifiers::all()))
858 .into_with_lock_state(Some(LockState::all()))
859 .into_with_repeat_sequence(42)
860 .into_with_key_meaning(Some(KeyMeaning::NonPrintableKey(NonPrintableKey::Tab)));
861
862 let actual = event.from_key_event_at_time(zx::MonotonicInstant::from_nanos(42));
863 assert_eq!(
864 actual,
865 fidl_fuchsia_ui_input3::KeyEvent {
866 timestamp: Some(42),
867 type_: Some(KeyEventType::Pressed),
868 key: Some(Key::A),
869 modifiers: Some(Modifiers::all()),
870 key_meaning: Some(KeyMeaning::NonPrintableKey(NonPrintableKey::Tab)),
871 repeat_sequence: Some(42),
872 lock_state: Some(LockState::all()),
873 ..Default::default()
874 }
875 );
876 }
877}