| // Copyright 2019 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 { |
| anyhow::{Context as _, Error}, |
| fidl_fuchsia_ui_input as ui_input, |
| fidl_fuchsia_ui_input3::{self as ui_input3, KeyMeaning, NonPrintableKey}, |
| fuchsia_zircon as zx, |
| futures::{TryFutureExt, TryStreamExt}, |
| }; |
| |
| use fidl_fuchsia_ui_keyboard_focus as fidl_focus; |
| |
| use crate::keyboard::{events::KeyEvent, keyboard3}; |
| use crate::text_manager::TextManager; |
| |
| /// Keyboard service router. |
| /// Starts providers of input3.Keyboard. |
| /// Handles keyboard events, routing them to one of the following: |
| /// - other fuchsia.ui.input.ImeService events to text_manager |
| pub struct Service { |
| text_manager: TextManager, |
| keyboard3: keyboard3::KeyboardService, |
| clock: std::sync::Arc<zx::Clock>, |
| } |
| |
| impl Service { |
| pub async fn new(text_manager: TextManager) -> Result<Service, Error> { |
| let keyboard3 = keyboard3::KeyboardService::new(); |
| let clock = std::sync::Arc::new(zx::Clock::create(zx::ClockOpts::MONOTONIC, None).unwrap()); |
| Ok(Service { text_manager, keyboard3, clock }) |
| } |
| |
| /// Starts a task that processes `fuchsia.ui.keyboard.focus.Controller`. |
| /// This method returns immediately without blocking. |
| pub fn spawn_focus_controller(&self, mut stream: fidl_focus::ControllerRequestStream) { |
| let keyboard3 = self.keyboard3.clone(); |
| let clock = self.clock.clone(); |
| fuchsia_async::Task::spawn( |
| async move { |
| while let Some(msg) = stream.try_next().await.context(concat!( |
| "keyboard::Service::spawn_focus_controller: ", |
| "error while reading the request stream for ", |
| "fuchsia.ui.keyboard.focus.Controller" |
| ))? { |
| match msg { |
| fidl_focus::ControllerRequest::Notify { view_ref, responder, .. } => { |
| let view_ref = keyboard3::ViewRef::new(view_ref); |
| let now = clock |
| .read() |
| .map_err(|e| { |
| tracing::error!("couldn't read clock: {:?}", e); |
| e |
| }) |
| .unwrap_or(zx::Time::ZERO); |
| keyboard3.handle_focus_change(view_ref, now).await; |
| responder.send()?; |
| } |
| } |
| } |
| Ok(()) |
| } |
| .unwrap_or_else(|e: anyhow::Error| tracing::error!("couldn't run: {:?}", e)), |
| ) |
| .detach(); |
| } |
| |
| /// Starts a task that processes `fuchsia.ui.input3.KeyEventInjector` requests. |
| /// This method returns immediately without blocking. |
| pub fn spawn_key_event_injector(&self, mut stream: ui_input3::KeyEventInjectorRequestStream) { |
| let mut keyboard3 = self.keyboard3.clone(); |
| let mut text_manager = self.text_manager.clone(); |
| fuchsia_async::Task::spawn( |
| async move { |
| while let Some(msg) = stream.try_next().await.context(concat!( |
| "keyboard::Service::spawn_key_event_injector: ", |
| "error while reading the request stream for ", |
| "fuchsia.ui.input3.KeyEventInjector" |
| ))? { |
| match msg { |
| ui_input3::KeyEventInjectorRequest::Inject { |
| mut key_event, |
| responder, |
| .. |
| } => { |
| key_event.key = key_event.key.or_else(|| match key_event.key_meaning { |
| Some(KeyMeaning::NonPrintableKey(k)) => { |
| key_from_non_printable_key(k) |
| } |
| Some(KeyMeaning::Codepoint(_)) => None, |
| None => None, |
| }); |
| text_manager |
| .inject_input(KeyEvent::new( |
| &key_event, |
| keyboard3.get_keys_pressed().await, |
| )?) |
| .await |
| .unwrap_or_else(|e| { |
| // Most of the time this is not a real error: what it actually |
| // means is that we tried to offer text input to a text edit |
| // field, but no such field is currently in focus. Therefore |
| // increasing the verbosity of the message, so that it's only |
| // printed when you *really* need it for debugging. |
| tracing::debug!( |
| concat!( |
| "keyboard::Service::spawn_key_event_injector: ", |
| "error injecting input into IME: {:?}" |
| ), |
| e |
| ) |
| }); |
| let was_handled = if keyboard3 |
| .handle_key_event(key_event) |
| .await |
| .context("error handling input3 keyboard event")? |
| { |
| ui_input3::KeyEventStatus::Handled |
| } else { |
| ui_input3::KeyEventStatus::NotHandled |
| }; |
| responder.send(was_handled).context(concat!( |
| "keyboard::Service::spawn_key_event_injector: ", |
| "error while sending response" |
| ))?; |
| } |
| } |
| } |
| Ok(()) |
| } |
| .unwrap_or_else(|e: anyhow::Error| tracing::error!("couldn't run: {:?}", e)), |
| ) |
| .detach(); |
| } |
| |
| pub fn spawn_ime_service(&self, mut stream: ui_input::ImeServiceRequestStream) { |
| let mut text_manager = self.text_manager.clone(); |
| fuchsia_async::Task::spawn( |
| async move { |
| while let Some(msg) = |
| stream.try_next().await.context("error running keyboard service")? |
| { |
| text_manager |
| .handle_ime_service_msg(msg) |
| .await |
| .context("Handle IME service messages")?; |
| } |
| Ok(()) |
| } |
| .unwrap_or_else(|e: anyhow::Error| tracing::error!("couldn't run: {:?}", e)), |
| ) |
| .detach(); |
| } |
| |
| pub fn spawn_keyboard3_service(&self, stream: ui_input3::KeyboardRequestStream) { |
| let keyboard3 = self.keyboard3.clone(); |
| fuchsia_async::Task::spawn( |
| async move { keyboard3.spawn_service(stream).await } |
| .unwrap_or_else(|e: anyhow::Error| tracing::error!("couldn't run: {:?}", e)), |
| ) |
| .detach(); |
| } |
| } |
| |
| fn key_from_non_printable_key( |
| non_printable_key: NonPrintableKey, |
| ) -> Option<fidl_fuchsia_input::Key> { |
| match non_printable_key { |
| NonPrintableKey::Enter => Some(fidl_fuchsia_input::Key::Enter), |
| NonPrintableKey::Tab => Some(fidl_fuchsia_input::Key::Tab), |
| NonPrintableKey::Backspace => Some(fidl_fuchsia_input::Key::Backspace), |
| unrecognized => { |
| tracing::warn!("received unrecognized NonPrintableKey {:?}", unrecognized); |
| None |
| } |
| } |
| } |