| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| crate::input_device, |
| crate::input_handler::UnhandledInputHandler, |
| crate::keyboard_binding, |
| anyhow::Error, |
| async_trait::async_trait, |
| fidl_fuchsia_ui_input3::{self as fidl_ui_input3, LockState, Modifiers}, |
| fuchsia_component::client::connect_to_protocol, |
| fuchsia_syslog::{fx_log_debug, fx_log_err}, |
| fuchsia_zircon as zx, |
| keymaps::{self, LockStateChecker, ModifierChecker}, |
| std::rc::Rc, |
| }; |
| |
| #[derive(Debug)] |
| pub struct FrozenLockState { |
| lock_state: LockState, |
| } |
| |
| impl From<LockState> for FrozenLockState { |
| fn from(lock_state: LockState) -> Self { |
| FrozenLockState { lock_state } |
| } |
| } |
| |
| impl LockStateChecker for FrozenLockState { |
| fn test(&self, value: LockState) -> bool { |
| self.lock_state.contains(value) |
| } |
| } |
| |
| /// Modifier state plus a tester method. |
| #[derive(Debug)] |
| pub struct FrozenModifierState { |
| state: Modifiers, |
| } |
| |
| impl From<fidl_fuchsia_ui_input3::Modifiers> for FrozenModifierState { |
| fn from(state: Modifiers) -> Self { |
| FrozenModifierState { state } |
| } |
| } |
| |
| impl ModifierChecker for FrozenModifierState { |
| fn test(&self, value: Modifiers) -> bool { |
| self.state.contains(value) |
| } |
| } |
| |
| /// [`ImeHandler`] is responsible for dispatching key events to the IME service, thus making sure |
| /// that key events are delivered to application runtimes (e.g., web, Flutter). |
| /// |
| /// > NOTE: The [ImeHandler] requires [ModifierHandler] to be installed upstream to apply the keymaps correctly. |
| pub struct ImeHandler { |
| /// The FIDL proxy (client-side stub) to the service for key event injection. |
| key_event_injector: fidl_ui_input3::KeyEventInjectorProxy, |
| } |
| |
| #[async_trait(?Send)] |
| impl UnhandledInputHandler for ImeHandler { |
| async fn handle_unhandled_input_event( |
| self: Rc<Self>, |
| unhandled_input_event: input_device::UnhandledInputEvent, |
| ) -> Vec<input_device::InputEvent> { |
| match unhandled_input_event { |
| input_device::UnhandledInputEvent { |
| device_event: input_device::InputDeviceEvent::Keyboard(ref keyboard_device_event), |
| device_descriptor: input_device::InputDeviceDescriptor::Keyboard(_), |
| event_time, |
| trace_id: _, |
| } => { |
| let key_event = create_key_event(&keyboard_device_event, event_time); |
| self.dispatch_key(key_event).await; |
| // Consume the input event. |
| vec![input_device::InputEvent::from(unhandled_input_event).into_handled()] |
| } |
| _ => vec![input_device::InputEvent::from(unhandled_input_event)], |
| } |
| } |
| } |
| |
| #[allow(dead_code)] |
| impl ImeHandler { |
| /// Creates a new [`ImeHandler`] by connecting out to the key event injector. |
| pub async fn new() -> Result<Rc<Self>, Error> { |
| let key_event_injector = connect_to_protocol::<fidl_ui_input3::KeyEventInjectorMarker>()?; |
| |
| Self::new_handler(key_event_injector).await |
| } |
| |
| /// Creates a new [`ImeHandler`]. |
| /// |
| /// # Parameters |
| /// `key_event_injector`: A proxy (FIDL client-side stub) to the key event |
| /// injector FIDL service. |
| async fn new_handler( |
| key_event_injector: fidl_ui_input3::KeyEventInjectorProxy, |
| ) -> Result<Rc<Self>, Error> { |
| let handler = ImeHandler { key_event_injector }; |
| |
| Ok(Rc::new(handler)) |
| } |
| |
| /// Dispatches key events to IME and returns KeyboardEvents for unhandled events. |
| /// |
| /// # Parameters |
| /// `key_events`: The key events to dispatch. |
| /// `event_time`: The time in nanoseconds when the events were first recorded. |
| async fn dispatch_key(self: &Rc<Self>, key_event: fidl_ui_input3::KeyEvent) { |
| assert!( |
| key_event.timestamp.is_some(), |
| "dispatch_key: got a key_event without a timestamp: {:?}", |
| &key_event |
| ); |
| match self.key_event_injector.inject(key_event).await { |
| Err(err) => fx_log_err!("Failed to dispatch key to IME: {:?}", err), |
| _ => {} |
| }; |
| } |
| } |
| |
| /// Returns a KeyEvent with the given parameters. |
| /// |
| /// # Parameters |
| /// * `event`: The keyboard event to process. |
| /// * `event_time`: The time in nanoseconds when the event was first recorded. |
| fn create_key_event( |
| event: &keyboard_binding::KeyboardEvent, |
| event_time: zx::Time, |
| ) -> fidl_ui_input3::KeyEvent { |
| let modifier_state: FrozenModifierState = |
| event.get_modifiers().unwrap_or(Modifiers::from_bits_allow_unknown(0)).into(); |
| let lock_state: FrozenLockState = |
| event.get_lock_state().unwrap_or(LockState::from_bits_allow_unknown(0)).into(); |
| fx_log_debug!( |
| "ImeHandler::create_key_event: key:{:?}, modifier_state: {:?}, lock_state: {:?}, event_type: {:?}", |
| event.get_key(), |
| modifier_state, |
| lock_state, |
| event.get_event_type(), |
| ); |
| // Don't override the key meaning if already set, e.g. by prior stage. |
| let key_meaning = event.get_key_meaning().or(keymaps::US_QWERTY.apply( |
| event.get_key(), |
| &modifier_state, |
| &lock_state, |
| )); |
| |
| // Don't insert a spurious Some(0). |
| let repeat_sequence = match event.get_repeat_sequence() { |
| 0 => None, |
| s => Some(s), |
| }; |
| |
| fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time.into_nanos()), |
| type_: event.get_event_type().into(), |
| key: event.get_key().into(), |
| modifiers: event.get_modifiers(), |
| lock_state: event.get_lock_state(), |
| key_meaning, |
| repeat_sequence, |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use { |
| super::*, |
| crate::keyboard_binding::{self, KeyboardEvent}, |
| crate::testing_utilities, |
| assert_matches::assert_matches, |
| fidl_fuchsia_input as fidl_input, fidl_fuchsia_ui_input3 as fidl_ui_input3, |
| fuchsia_async as fasync, fuchsia_zircon as zx, |
| futures::StreamExt, |
| std::convert::TryFrom as _, |
| test_case::test_case, |
| }; |
| |
| fn handle_events( |
| ime_handler: Rc<ImeHandler>, |
| input_events: Vec<input_device::UnhandledInputEvent>, |
| ) { |
| fasync::Task::local(async move { |
| for input_event in input_events { |
| assert_matches!( |
| ime_handler.clone().handle_unhandled_input_event(input_event).await.as_slice(), |
| [input_device::InputEvent { handled: input_device::Handled::Yes, .. }] |
| ); |
| } |
| }) |
| .detach(); |
| } |
| |
| async fn assert_ime_receives_events( |
| expected_events: Vec<fidl_ui_input3::KeyEvent>, |
| mut request_stream: fidl_ui_input3::KeyEventInjectorRequestStream, |
| ) { |
| let mut expected_events_iter = expected_events.iter().peekable(); |
| while let Some(Ok(fidl_ui_input3::KeyEventInjectorRequest::Inject { |
| key_event, |
| responder, |
| .. |
| })) = request_stream.next().await |
| { |
| pretty_assertions::assert_eq!(&key_event, expected_events_iter.next().unwrap()); |
| |
| // All the expected events have been received, so make sure no more events |
| // are present before returning. |
| if expected_events_iter.peek().is_none() { |
| responder |
| .send(fidl_ui_input3::KeyEventStatus::Handled) |
| .expect("error responding to DispatchKey"); |
| return; |
| } |
| responder |
| .send(fidl_ui_input3::KeyEventStatus::Handled) |
| .expect("error responding to DispatchKey"); |
| } |
| |
| assert!(false); |
| } |
| |
| fn connect_to_key_event_injector( |
| ) -> (fidl_ui_input3::KeyEventInjectorProxy, fidl_ui_input3::KeyEventInjectorRequestStream) |
| { |
| fidl::endpoints::create_proxy_and_stream::<fidl_ui_input3::KeyEventInjectorMarker>() |
| .expect("Failed to create proxy and stream for fuchsia.ui.input3.KeyEventInjector") |
| } |
| |
| fn create_unhandled_keyboard_event( |
| key: fidl_fuchsia_input::Key, |
| event_type: fidl_fuchsia_ui_input3::KeyEventType, |
| modifiers: Option<fidl_ui_input3::Modifiers>, |
| event_time: zx::Time, |
| device_descriptor: &input_device::InputDeviceDescriptor, |
| keymap: Option<String>, |
| ) -> input_device::UnhandledInputEvent { |
| create_unhandled_keyboard_event_with_key_meaning( |
| key, |
| event_type, |
| modifiers, |
| event_time, |
| device_descriptor, |
| keymap, |
| /* key_meaning */ None, |
| ) |
| } |
| |
| fn create_unhandled_keyboard_event_with_key_meaning( |
| key: fidl_fuchsia_input::Key, |
| event_type: fidl_fuchsia_ui_input3::KeyEventType, |
| modifiers: Option<fidl_ui_input3::Modifiers>, |
| event_time: zx::Time, |
| device_descriptor: &input_device::InputDeviceDescriptor, |
| keymap: Option<String>, |
| key_meaning: Option<fidl_fuchsia_ui_input3::KeyMeaning>, |
| ) -> input_device::UnhandledInputEvent { |
| input_device::UnhandledInputEvent::try_from( |
| testing_utilities::create_keyboard_event_with_key_meaning( |
| key, |
| event_type, |
| modifiers, |
| event_time, |
| device_descriptor, |
| keymap, |
| key_meaning, |
| ), |
| ) |
| .unwrap() |
| } |
| |
| fn create_unhandled_input_event( |
| keyboard_event: keyboard_binding::KeyboardEvent, |
| device_descriptor: &input_device::InputDeviceDescriptor, |
| event_time: zx::Time, |
| ) -> input_device::UnhandledInputEvent { |
| input_device::UnhandledInputEvent { |
| device_event: input_device::InputDeviceEvent::Keyboard(keyboard_event), |
| device_descriptor: device_descriptor.clone(), |
| event_time, |
| trace_id: None, |
| } |
| } |
| |
| /// Tests that a pressed key event is dispatched. |
| /// |
| /// > NOTE: The `device_descriptor` used in this test case and elsewhere |
| /// *must* be of type `KeyboardDeviceDescriptor` as this is required by the |
| /// pattern matching in `ImeHandler`. |
| #[fasync::run_singlethreaded(test)] |
| async fn pressed_key() { |
| let (proxy, request_stream) = connect_to_key_event_injector(); |
| let ime_handler = |
| ImeHandler::new_handler(proxy).await.expect("Failed to create ImeHandler."); |
| |
| let device_descriptor = input_device::InputDeviceDescriptor::Keyboard( |
| keyboard_binding::KeyboardDeviceDescriptor { keys: vec![fidl_input::Key::A] }, |
| ); |
| let (event_time_i64, event_time_u64) = testing_utilities::event_times(); |
| let input_events = vec![create_unhandled_keyboard_event( |
| fidl_input::Key::A, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| None, |
| event_time_u64, |
| &device_descriptor, |
| /* keymap= */ None, |
| )]; |
| |
| let expected_events = vec![fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Pressed), |
| key: Some(fidl_input::Key::A), |
| // A key "A" without shift is a lowercase 'a'. |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(97)), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }]; |
| |
| handle_events(ime_handler, input_events); |
| assert_ime_receives_events(expected_events, request_stream).await; |
| } |
| |
| /// Tests that a released key event is dispatched. |
| #[fasync::run_singlethreaded(test)] |
| async fn released_key() { |
| let (proxy, request_stream) = connect_to_key_event_injector(); |
| let ime_handler = |
| ImeHandler::new_handler(proxy).await.expect("Failed to create ImeHandler."); |
| |
| let device_descriptor = input_device::InputDeviceDescriptor::Keyboard( |
| keyboard_binding::KeyboardDeviceDescriptor { keys: vec![fidl_input::Key::A] }, |
| ); |
| let (event_time_i64, event_time_u64) = testing_utilities::event_times(); |
| let input_events = vec![create_unhandled_keyboard_event( |
| fidl_input::Key::A, |
| fidl_fuchsia_ui_input3::KeyEventType::Released, |
| None, |
| event_time_u64, |
| &device_descriptor, |
| /* keymap= */ None, |
| )]; |
| |
| let expected_events = vec![fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Released), |
| key: Some(fidl_input::Key::A), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(97)), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }]; |
| |
| handle_events(ime_handler, input_events); |
| assert_ime_receives_events(expected_events, request_stream).await; |
| } |
| |
| /// Tests that both pressed and released keys are dispatched appropriately. |
| #[fasync::run_singlethreaded(test)] |
| async fn pressed_and_released_key() { |
| let (proxy, request_stream) = connect_to_key_event_injector(); |
| let ime_handler = |
| ImeHandler::new_handler(proxy).await.expect("Failed to create ImeHandler."); |
| |
| let device_descriptor = input_device::InputDeviceDescriptor::Keyboard( |
| keyboard_binding::KeyboardDeviceDescriptor { |
| keys: vec![fidl_input::Key::A, fidl_input::Key::B], |
| }, |
| ); |
| let (event_time_i64, event_time_u64) = testing_utilities::event_times(); |
| let input_events = vec![ |
| create_unhandled_keyboard_event( |
| fidl_input::Key::A, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| None, |
| event_time_u64, |
| &device_descriptor, |
| /* keymap= */ None, |
| ), |
| create_unhandled_keyboard_event( |
| fidl_input::Key::A, |
| fidl_fuchsia_ui_input3::KeyEventType::Released, |
| None, |
| event_time_u64, |
| &device_descriptor, |
| /* keymap= */ None, |
| ), |
| create_unhandled_keyboard_event( |
| fidl_input::Key::B, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| None, |
| event_time_u64, |
| &device_descriptor, |
| /* keymap= */ None, |
| ), |
| create_unhandled_keyboard_event_with_key_meaning( |
| fidl_input::Key::C, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| None, |
| event_time_u64, |
| &device_descriptor, |
| /* keymap= */ None, |
| Some(fidl_fuchsia_ui_input3::KeyMeaning::Codepoint(42)), |
| ), |
| ]; |
| |
| let expected_events = vec![ |
| fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Pressed), |
| key: Some(fidl_input::Key::A), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(97)), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }, |
| fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Released), |
| key: Some(fidl_input::Key::A), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(97)), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }, |
| fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Pressed), |
| key: Some(fidl_input::Key::B), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(98)), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }, |
| fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Pressed), |
| key: Some(fidl_input::Key::C), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(42)), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }, |
| ]; |
| |
| handle_events(ime_handler, input_events); |
| assert_ime_receives_events(expected_events, request_stream).await; |
| } |
| |
| // Tests that modifier keys are dispatched appropriately. |
| // |
| // This test depends on the incoming event having correct modifier and lock |
| // state. Typically you'd do this by installing a ModifierHandler upstream |
| // of this pipeline stage. |
| #[fasync::run_singlethreaded(test)] |
| async fn repeated_modifier_key() { |
| let (proxy, request_stream) = connect_to_key_event_injector(); |
| let ime_handler = |
| ImeHandler::new_handler(proxy).await.expect("Failed to create ImeHandler."); |
| |
| let device_descriptor = input_device::InputDeviceDescriptor::Keyboard( |
| keyboard_binding::KeyboardDeviceDescriptor { |
| keys: vec![fidl_input::Key::A, fidl_input::Key::CapsLock], |
| }, |
| ); |
| let (event_time_i64, event_time_u64) = testing_utilities::event_times(); |
| let input_events = vec![ |
| create_unhandled_input_event( |
| KeyboardEvent::new( |
| fidl_input::Key::CapsLock, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| ) |
| .into_with_modifiers(Some(fidl_ui_input3::Modifiers::CAPS_LOCK)) |
| .into_with_lock_state(Some(fidl_ui_input3::LockState::CAPS_LOCK)), |
| &device_descriptor, |
| event_time_u64, |
| ), |
| create_unhandled_input_event( |
| KeyboardEvent::new( |
| fidl_input::Key::A, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| ) |
| .into_with_modifiers(Some(fidl_ui_input3::Modifiers::CAPS_LOCK)) |
| .into_with_lock_state(Some(fidl_ui_input3::LockState::CAPS_LOCK)), |
| &device_descriptor, |
| event_time_u64, |
| ), |
| create_unhandled_input_event( |
| KeyboardEvent::new( |
| fidl_input::Key::CapsLock, |
| fidl_fuchsia_ui_input3::KeyEventType::Released, |
| ) |
| .into_with_lock_state(Some(fidl_ui_input3::LockState::CAPS_LOCK)), |
| &device_descriptor, |
| event_time_u64, |
| ), |
| ]; |
| |
| let expected_events = vec![ |
| fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Pressed), |
| key: Some(fidl_input::Key::CapsLock), |
| modifiers: Some(fidl_ui_input3::Modifiers::CAPS_LOCK), |
| lock_state: Some(fidl_ui_input3::LockState::CAPS_LOCK), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(0)), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }, |
| fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Pressed), |
| key: Some(fidl_input::Key::A), |
| modifiers: Some(fidl_ui_input3::Modifiers::CAPS_LOCK), |
| lock_state: Some(fidl_ui_input3::LockState::CAPS_LOCK), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(65)), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }, |
| fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Released), |
| key: Some(fidl_input::Key::CapsLock), |
| lock_state: Some(fidl_ui_input3::LockState::CAPS_LOCK), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(0)), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }, |
| ]; |
| |
| handle_events(ime_handler, input_events); |
| assert_ime_receives_events(expected_events, request_stream).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn nonprintable_key_meanings_set_correctly() { |
| let (proxy, request_stream) = connect_to_key_event_injector(); |
| let ime_handler = |
| ImeHandler::new_handler(proxy).await.expect("Failed to create ImeHandler."); |
| |
| let device_descriptor = input_device::InputDeviceDescriptor::Keyboard( |
| keyboard_binding::KeyboardDeviceDescriptor { |
| keys: vec![ |
| fidl_input::Key::Enter, |
| fidl_input::Key::Tab, |
| fidl_input::Key::Backspace, |
| ], |
| }, |
| ); |
| let (event_time_i64, event_time_u64) = testing_utilities::event_times(); |
| let input_events = vec![ |
| create_unhandled_keyboard_event( |
| fidl_input::Key::Enter, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| None, |
| event_time_u64, |
| &device_descriptor, |
| /* keymap= */ None, |
| ), |
| create_unhandled_keyboard_event( |
| fidl_input::Key::Tab, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| None, |
| event_time_u64, |
| &device_descriptor, |
| /* keymap= */ None, |
| ), |
| create_unhandled_keyboard_event( |
| fidl_input::Key::Backspace, |
| fidl_fuchsia_ui_input3::KeyEventType::Released, |
| None, |
| event_time_u64, |
| &device_descriptor, |
| /* keymap= */ None, |
| ), |
| ]; |
| |
| let expected_events = vec![ |
| fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Pressed), |
| key: Some(fidl_input::Key::Enter), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::NonPrintableKey( |
| fidl_ui_input3::NonPrintableKey::Enter, |
| )), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }, |
| fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Pressed), |
| key: Some(fidl_input::Key::Tab), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::NonPrintableKey( |
| fidl_ui_input3::NonPrintableKey::Tab, |
| )), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }, |
| fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| // Test that things also work when a key is released. |
| type_: Some(fidl_ui_input3::KeyEventType::Released), |
| key: Some(fidl_input::Key::Backspace), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::NonPrintableKey( |
| fidl_ui_input3::NonPrintableKey::Backspace, |
| )), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }, |
| ]; |
| |
| handle_events(ime_handler, input_events); |
| assert_ime_receives_events(expected_events, request_stream).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn tab() { |
| let (proxy, request_stream) = connect_to_key_event_injector(); |
| let ime_handler = |
| ImeHandler::new_handler(proxy).await.expect("Failed to create ImeHandler."); |
| |
| let device_descriptor = input_device::InputDeviceDescriptor::Keyboard( |
| keyboard_binding::KeyboardDeviceDescriptor { |
| keys: vec![ |
| fidl_input::Key::Enter, |
| fidl_input::Key::Tab, |
| fidl_input::Key::Backspace, |
| ], |
| }, |
| ); |
| let (event_time_i64, event_time_u64) = testing_utilities::event_times(); |
| let input_events = vec![create_unhandled_keyboard_event( |
| fidl_input::Key::Tab, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| None, |
| event_time_u64, |
| &device_descriptor, |
| /* keymap= */ None, |
| )]; |
| |
| let expected_events = vec![fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Pressed), |
| key: Some(fidl_input::Key::Tab), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::NonPrintableKey( |
| fidl_ui_input3::NonPrintableKey::Tab, |
| )), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }]; |
| |
| handle_events(ime_handler, input_events); |
| assert_ime_receives_events(expected_events, request_stream).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn shift_shift_a() { |
| let (proxy, request_stream) = connect_to_key_event_injector(); |
| let ime_handler = |
| ImeHandler::new_handler(proxy).await.expect("Failed to create ImeHandler."); |
| |
| let device_descriptor = input_device::InputDeviceDescriptor::Keyboard( |
| keyboard_binding::KeyboardDeviceDescriptor { |
| keys: vec![fidl_input::Key::LeftCtrl, fidl_input::Key::Tab], |
| }, |
| ); |
| let (event_time_i64, event_time_u64) = testing_utilities::event_times(); |
| let input_events = vec![ |
| create_unhandled_keyboard_event( |
| fidl_input::Key::LeftShift, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| Some(Modifiers::LEFT_SHIFT | Modifiers::SHIFT), |
| event_time_u64, |
| &device_descriptor, |
| /* keymap= */ None, |
| ), |
| create_unhandled_keyboard_event( |
| fidl_input::Key::RightShift, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| Some(Modifiers::LEFT_SHIFT | Modifiers::RIGHT_SHIFT | Modifiers::SHIFT), |
| event_time_u64, |
| &device_descriptor, |
| /* keymap= */ None, |
| ), |
| create_unhandled_keyboard_event( |
| fidl_input::Key::A, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| Some(Modifiers::LEFT_SHIFT | Modifiers::RIGHT_SHIFT | Modifiers::SHIFT), |
| event_time_u64, |
| &device_descriptor, |
| /* keymap= */ None, |
| ), |
| ]; |
| |
| let expected_events = vec![ |
| fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Pressed), |
| key: Some(fidl_input::Key::LeftShift), |
| modifiers: Some(Modifiers::LEFT_SHIFT | Modifiers::SHIFT), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(0)), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }, |
| fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Pressed), |
| key: Some(fidl_input::Key::RightShift), |
| modifiers: Some(Modifiers::RIGHT_SHIFT | Modifiers::LEFT_SHIFT | Modifiers::SHIFT), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(0)), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }, |
| fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Pressed), |
| key: Some(fidl_input::Key::A), |
| modifiers: Some(Modifiers::RIGHT_SHIFT | Modifiers::LEFT_SHIFT | Modifiers::SHIFT), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(65)), // "A" |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }, |
| ]; |
| |
| handle_events(ime_handler, input_events); |
| assert_ime_receives_events(expected_events, request_stream).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn ctrl_tab() { |
| let (proxy, request_stream) = connect_to_key_event_injector(); |
| let ime_handler = |
| ImeHandler::new_handler(proxy).await.expect("Failed to create ImeHandler."); |
| |
| let device_descriptor = input_device::InputDeviceDescriptor::Keyboard( |
| keyboard_binding::KeyboardDeviceDescriptor { |
| keys: vec![fidl_input::Key::LeftCtrl, fidl_input::Key::Tab], |
| }, |
| ); |
| let (event_time_i64, event_time_u64) = testing_utilities::event_times(); |
| let input_events = vec![ |
| create_unhandled_keyboard_event( |
| fidl_input::Key::LeftCtrl, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| None, |
| event_time_u64, |
| &device_descriptor, |
| /* keymap= */ None, |
| ), |
| create_unhandled_keyboard_event( |
| fidl_input::Key::Tab, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| None, |
| event_time_u64, |
| &device_descriptor, |
| /* keymap= */ None, |
| ), |
| ]; |
| |
| let expected_events = vec![ |
| fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Pressed), |
| key: Some(fidl_input::Key::LeftCtrl), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(0)), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }, |
| fidl_ui_input3::KeyEvent { |
| timestamp: Some(event_time_i64), |
| type_: Some(fidl_ui_input3::KeyEventType::Pressed), |
| key: Some(fidl_input::Key::Tab), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::NonPrintableKey( |
| fidl_ui_input3::NonPrintableKey::Tab, |
| )), |
| ..fidl_ui_input3::KeyEvent::EMPTY |
| }, |
| ]; |
| |
| handle_events(ime_handler, input_events); |
| assert_ime_receives_events(expected_events, request_stream).await; |
| } |
| |
| #[test_case( |
| keyboard_binding::KeyboardEvent::new( |
| fidl_input::Key::A, |
| fidl_ui_input3::KeyEventType::Pressed), |
| zx::Time::from_nanos(42) => fidl_ui_input3::KeyEvent{ |
| timestamp: Some(42), |
| type_: Some(fidl_ui_input3::KeyEventType::Pressed), |
| key: Some(fidl_input::Key::A), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(97)), |
| ..fidl_ui_input3::KeyEvent::EMPTY}; "basic value copy")] |
| #[test_case( |
| keyboard_binding::KeyboardEvent::new( |
| fidl_input::Key::A, |
| fidl_ui_input3::KeyEventType::Pressed) |
| .into_with_repeat_sequence(13), |
| zx::Time::from_nanos(42) => fidl_ui_input3::KeyEvent{ |
| timestamp: Some(42), |
| type_: Some(fidl_ui_input3::KeyEventType::Pressed), |
| key: Some(fidl_input::Key::A), |
| key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(97)), |
| repeat_sequence: Some(13), |
| ..fidl_ui_input3::KeyEvent::EMPTY}; "repeat_sequence is honored")] |
| fn test_create_key_event( |
| event: keyboard_binding::KeyboardEvent, |
| event_time: zx::Time, |
| ) -> fidl_ui_input3::KeyEvent { |
| super::create_key_event(&event, event_time) |
| } |
| } |