| // Copyright 2022 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::{self, InputEvent}; |
| use crate::keyboard_binding::{KeyboardDeviceDescriptor, KeyboardEvent}; |
| use anyhow::{Context, Result}; |
| use fidl_fuchsia_ui_input3::KeyEventType; |
| use fidl_fuchsia_ui_scenic as fscenic; |
| use fuchsia_async::{OnSignals, Task}; |
| use fuchsia_syslog::{fx_log_debug, fx_log_info, fx_log_warn}; |
| use fuchsia_zircon::{AsHandleRef, Duration, Signals, Status, Time}; |
| use futures::{ |
| channel::mpsc::{self, UnboundedReceiver, UnboundedSender}, |
| select, StreamExt, |
| }; |
| use keymaps::KeyState; |
| use lazy_static::lazy_static; |
| use std::{cell::RefCell, rc::Rc}; |
| |
| lazy_static! { |
| // The signal value corresponding to the `DISPLAY_OWNED_SIGNAL`. Same as zircon's signal |
| // USER_0. |
| static ref DISPLAY_OWNED: Signals = Signals::from_bits(fscenic::DISPLAY_OWNED_SIGNAL) |
| .expect("static init should not fail") ; |
| |
| // The signal value corresponding to the `DISPLAY_NOT_OWNED_SIGNAL`. Same as zircon's signal |
| // USER_1. |
| static ref DISPLAY_UNOWNED: Signals = Signals::from_bits(fscenic::DISPLAY_NOT_OWNED_SIGNAL) |
| .expect("static init should not fail") ; |
| |
| // Any display-related signal. |
| static ref ANY_DISPLAY_EVENT: Signals = *DISPLAY_OWNED | *DISPLAY_UNOWNED; |
| } |
| |
| // Stores the last received ownership signals. |
| #[derive(Debug, Clone, PartialEq)] |
| struct Ownership { |
| signals: Signals, |
| } |
| |
| impl std::convert::From<Signals> for Ownership { |
| fn from(signals: Signals) -> Self { |
| Ownership { signals } |
| } |
| } |
| |
| impl Ownership { |
| // Returns true if the display is currently indicated to be not owned by |
| // Scenic. |
| fn is_display_ownership_lost(&self) -> bool { |
| self.signals.contains(*DISPLAY_UNOWNED) |
| } |
| |
| // Returns the mask of the next signal to watch. |
| // |
| // Since the ownership alternates, so does the next signal to wait on. |
| fn next_signal(&self) -> Signals { |
| match self.is_display_ownership_lost() { |
| true => *DISPLAY_OWNED, |
| false => *DISPLAY_UNOWNED, |
| } |
| } |
| |
| /// Waits for the next signal change. |
| /// |
| /// If the display is owned, it will wait for display to become unowned. |
| /// If the display is unowned, it will wait for the display to become owned. |
| async fn wait_ownership_change<'a, T: AsHandleRef>( |
| &self, |
| event: &'a T, |
| ) -> Result<Signals, Status> { |
| OnSignals::new(event, self.next_signal()).await |
| } |
| } |
| |
| /// A handler that turns the input pipeline off or on based on whether |
| /// the Scenic owns the display. |
| /// |
| /// This allows us to turn off keyboard processing when the user switches away |
| /// from the product (e.g. terminal) into virtual console. |
| /// |
| /// See the `README.md` file in this crate for details. |
| pub struct DisplayOwnership { |
| /// The current view of the display ownership. It is mutated by the |
| /// display ownership task when appropriate signals arrive. |
| ownership: Rc<RefCell<Ownership>>, |
| |
| /// The registry of currently pressed keys. |
| key_state: RefCell<KeyState>, |
| |
| /// The source of ownership change events for the main loop. |
| display_ownership_change_receiver: RefCell<UnboundedReceiver<Ownership>>, |
| |
| /// A background task that watches for display ownership changes. We keep |
| /// it alive to ensure that it keeps running. |
| _display_ownership_task: Task<()>, |
| |
| /// The event processing loop will do an `unbounded_send(())` on this |
| /// channel once at the end of each loop pass, in test configurations only. |
| /// The test fixture uses this channel to execute test fixture in |
| /// lock-step with the event processing loop for test cases where the |
| /// precise event sequencing is relevant. |
| #[cfg(test)] |
| loop_done: RefCell<Option<UnboundedSender<()>>>, |
| } |
| |
| impl DisplayOwnership { |
| /// Creates a new handler that watches `display_ownership_event` for events. |
| /// |
| /// The `display_ownership_event` is assumed to be an [Event] obtained from |
| /// Scenic using `fuchsia.ui.scenic.Scenic/GetDisplayOwnershipEvent`. There |
| /// isn't really a way for this code to know here whether this is true or |
| /// not, so implementor beware. |
| pub fn new(display_ownership_event: impl AsHandleRef + 'static) -> Rc<Self> { |
| DisplayOwnership::new_internal(display_ownership_event, None) |
| } |
| |
| #[cfg(test)] |
| pub fn new_for_test( |
| display_ownership_event: impl AsHandleRef + 'static, |
| loop_done: UnboundedSender<()>, |
| ) -> Rc<Self> { |
| DisplayOwnership::new_internal(display_ownership_event, Some(loop_done)) |
| } |
| |
| fn new_internal( |
| display_ownership_event: impl AsHandleRef + 'static, |
| _loop_done: Option<UnboundedSender<()>>, |
| ) -> Rc<Self> { |
| let initial_state = display_ownership_event |
| // scenic guarantees that ANY_DISPLAY_EVENT is asserted. If it is |
| // not, this will fail with a timeout error. |
| .wait_handle(*ANY_DISPLAY_EVENT, Time::INFINITE_PAST) |
| .expect("unable to set the initial display state"); |
| fx_log_debug!("setting initial display ownership to: {:?}", &initial_state); |
| let initial_ownership: Ownership = initial_state.into(); |
| let ownership = Rc::new(RefCell::new(initial_ownership.clone())); |
| |
| let mut ownership_clone = initial_ownership.clone(); |
| let (ownership_sender, ownership_receiver) = mpsc::unbounded(); |
| let display_ownership_task = Task::local(async move { |
| loop { |
| let signals = ownership_clone.wait_ownership_change(&display_ownership_event).await; |
| match signals { |
| Err(e) => { |
| fx_log_warn!("could not read display state: {:?}", e); |
| break; |
| } |
| Ok(signals) => { |
| fx_log_debug!("setting display ownership to: {:?}", &signals); |
| ownership_sender.unbounded_send(signals.into()).unwrap(); |
| ownership_clone = signals.into(); |
| } |
| } |
| } |
| fx_log_warn!("display loop exiting and will no longer monitor display changes - this is not expected"); |
| }); |
| fx_log_info!("Display ownership handler installed"); |
| Rc::new(Self { |
| ownership, |
| key_state: RefCell::new(KeyState::new()), |
| display_ownership_change_receiver: RefCell::new(ownership_receiver), |
| _display_ownership_task: display_ownership_task, |
| #[cfg(test)] |
| loop_done: RefCell::new(_loop_done), |
| }) |
| } |
| |
| /// Returns true if the display is currently *not* owned by Scenic. |
| fn is_display_ownership_lost(&self) -> bool { |
| self.ownership.borrow().is_display_ownership_lost() |
| } |
| |
| /// Run this function in an executor to handle events. |
| pub async fn handle_input_events( |
| self: &Rc<Self>, |
| mut input: UnboundedReceiver<InputEvent>, |
| output: UnboundedSender<InputEvent>, |
| ) -> Result<()> { |
| loop { |
| let mut ownership_source = self.display_ownership_change_receiver.borrow_mut(); |
| select! { |
| // Display ownership changed. |
| new_ownership = ownership_source.select_next_some() => { |
| let is_display_ownership_lost = new_ownership.is_display_ownership_lost(); |
| // When the ownership is modified, float a set of cancel or sync |
| // events to scoop up stale keyboard state, treating it the same |
| // as loss of focus. |
| let event_type = match is_display_ownership_lost { |
| true => KeyEventType::Cancel, |
| false => KeyEventType::Sync, |
| }; |
| let keys = self.key_state.borrow().get_set(); |
| let mut event_time = Time::get_monotonic(); |
| for key in keys.into_iter() { |
| let key_event = KeyboardEvent::new(key, event_type); |
| output.unbounded_send(into_input_event(key_event, event_time)) |
| .context("unable to send display updates")?; |
| event_time = event_time + Duration::from_nanos(1); |
| } |
| *(self.ownership.borrow_mut()) = new_ownership; |
| }, |
| |
| // An input event arrived. |
| event = input.select_next_some() => { |
| if event.is_handled() { |
| // Forward handled events unmodified. |
| output.unbounded_send(event).context("unable to send handled event")?; |
| continue; |
| } |
| match event.device_event { |
| input_device::InputDeviceEvent::Keyboard(ref event) => { |
| self.key_state.borrow_mut().update(event.get_event_type(), event.get_key()); |
| }, |
| _ => {}, |
| } |
| let is_display_ownership_lost = self.is_display_ownership_lost(); |
| output.unbounded_send( |
| input_device::InputEvent::from(event) |
| .into_handled_if(is_display_ownership_lost) |
| ).context("unable to send input event updates")?; |
| }, |
| }; |
| #[cfg(test)] |
| { |
| self.loop_done.borrow_mut().as_ref().unwrap().unbounded_send(()).unwrap(); |
| } |
| } |
| } |
| } |
| |
| fn empty_keyboard_device_descriptor() -> input_device::InputDeviceDescriptor { |
| input_device::InputDeviceDescriptor::Keyboard( |
| // Should descriptor be something sensible? |
| KeyboardDeviceDescriptor { keys: vec![] }, |
| ) |
| } |
| |
| fn into_input_event(keyboard_event: KeyboardEvent, event_time: Time) -> input_device::InputEvent { |
| input_device::InputEvent { |
| device_event: input_device::InputDeviceEvent::Keyboard(keyboard_event), |
| device_descriptor: empty_keyboard_device_descriptor(), |
| event_time, |
| handled: input_device::Handled::No, |
| trace_id: None, |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use crate::input_device::InputEvent; |
| use crate::testing_utilities::{create_fake_input_event, create_input_event}; |
| use fidl_fuchsia_input::Key; |
| use fidl_fuchsia_ui_input3::KeyEventType; |
| use fuchsia_async as fasync; |
| use fuchsia_zircon::{EventPair, Peered, Time}; |
| use futures::channel::mpsc; |
| use pretty_assertions::assert_eq; |
| |
| // Manages losing and regaining display, since manual management is error-prone: |
| // if signal_peer does not change the signal state, the waiting process will block |
| // forever, which makes tests run longer than needed. |
| struct DisplayWrangler { |
| event: EventPair, |
| last: Signals, |
| } |
| |
| impl DisplayWrangler { |
| fn new(event: EventPair) -> Self { |
| let mut instance = DisplayWrangler { event, last: *DISPLAY_OWNED }; |
| // Signal needs to be initialized before the handlers attempts to read it. |
| // This is normally always the case in production. |
| // Else, the `new_for_test` below will panic with a TIMEOUT error. |
| instance.set_unowned(); |
| instance |
| } |
| |
| fn set_unowned(&mut self) { |
| assert!(self.last != *DISPLAY_UNOWNED, "display is already unowned"); |
| self.event.signal_peer(*DISPLAY_OWNED, *DISPLAY_UNOWNED).unwrap(); |
| self.last = *DISPLAY_UNOWNED; |
| } |
| |
| fn set_owned(&mut self) { |
| assert!(self.last != *DISPLAY_OWNED, "display is already owned"); |
| self.event.signal_peer(*DISPLAY_UNOWNED, *DISPLAY_OWNED).unwrap(); |
| self.last = *DISPLAY_OWNED; |
| } |
| } |
| |
| #[fuchsia::test] |
| async fn display_ownership_change() { |
| // handler_event is the event that the unit under test will examine for |
| // display ownership changes. test_event is used to set the appropriate |
| // signals. |
| let (test_event, handler_event) = EventPair::create().unwrap(); |
| |
| // test_sender is used to pipe input events into the handler. |
| let (test_sender, handler_receiver) = mpsc::unbounded::<InputEvent>(); |
| |
| // test_receiver is used to pipe input events out of the handler. |
| let (handler_sender, test_receiver) = mpsc::unbounded::<InputEvent>(); |
| |
| // The unit under test adds a () each time it completes one pass through |
| // its event loop. Use to ensure synchronization. |
| let (loop_done_sender, mut loop_done) = mpsc::unbounded::<()>(); |
| |
| // We use a wrapper to signal test_event correctly, since doing it wrong |
| // by hand causes tests to hang, which isn't the best dev experience. |
| let mut wrangler = DisplayWrangler::new(test_event); |
| let handler = DisplayOwnership::new_for_test(handler_event, loop_done_sender); |
| |
| let _task = fasync::Task::local(async move { |
| handler.handle_input_events(handler_receiver, handler_sender).await.unwrap(); |
| }); |
| |
| let fake_time = Time::from_nanos(42); |
| |
| // Go two full circles of signaling. |
| |
| // 1 |
| wrangler.set_owned(); |
| loop_done.next().await; |
| test_sender.unbounded_send(create_fake_input_event(fake_time)).unwrap(); |
| loop_done.next().await; |
| |
| // 2 |
| wrangler.set_unowned(); |
| loop_done.next().await; |
| test_sender.unbounded_send(create_fake_input_event(fake_time)).unwrap(); |
| loop_done.next().await; |
| |
| // 3 |
| wrangler.set_owned(); |
| loop_done.next().await; |
| test_sender.unbounded_send(create_fake_input_event(fake_time)).unwrap(); |
| loop_done.next().await; |
| |
| // 4 |
| wrangler.set_unowned(); |
| loop_done.next().await; |
| test_sender.unbounded_send(create_fake_input_event(fake_time)).unwrap(); |
| loop_done.next().await; |
| |
| let actual: Vec<InputEvent> = |
| test_receiver.take(4).map(|e| e.into_with_event_time(fake_time)).collect().await; |
| |
| assert_eq!( |
| actual, |
| vec![ |
| // Event received while we owned the display. |
| create_fake_input_event(fake_time), |
| // Event received when we lost the display. |
| create_fake_input_event(fake_time).into_handled(), |
| // Display ownership regained. |
| create_fake_input_event(fake_time), |
| // Display ownership lost. |
| create_fake_input_event(fake_time).into_handled(), |
| ] |
| ); |
| } |
| |
| fn new_keyboard_input_event(key: Key, event_type: KeyEventType) -> InputEvent { |
| let fake_time = Time::from_nanos(42); |
| create_input_event( |
| KeyboardEvent::new(key, event_type), |
| &input_device::InputDeviceDescriptor::Fake, |
| fake_time, |
| input_device::Handled::No, |
| ) |
| } |
| |
| #[fuchsia::test] |
| async fn basic_key_state_handling() { |
| let (test_event, handler_event) = EventPair::create().unwrap(); |
| let (test_sender, handler_receiver) = mpsc::unbounded::<InputEvent>(); |
| let (handler_sender, test_receiver) = mpsc::unbounded::<InputEvent>(); |
| let (loop_done_sender, mut loop_done) = mpsc::unbounded::<()>(); |
| let mut wrangler = DisplayWrangler::new(test_event); |
| let handler = DisplayOwnership::new_for_test(handler_event, loop_done_sender); |
| let _task = fasync::Task::local(async move { |
| handler.handle_input_events(handler_receiver, handler_sender).await.unwrap(); |
| }); |
| |
| let fake_time = Time::from_nanos(42); |
| |
| // Gain the display, and press a key. |
| wrangler.set_owned(); |
| loop_done.next().await; |
| test_sender |
| .unbounded_send(new_keyboard_input_event(Key::A, KeyEventType::Pressed)) |
| .unwrap(); |
| loop_done.next().await; |
| |
| // Lose display. |
| wrangler.set_unowned(); |
| loop_done.next().await; |
| |
| // Regain display |
| wrangler.set_owned(); |
| loop_done.next().await; |
| |
| // Key event after regaining. |
| test_sender |
| .unbounded_send(new_keyboard_input_event(Key::A, KeyEventType::Released)) |
| .unwrap(); |
| loop_done.next().await; |
| |
| let actual: Vec<InputEvent> = |
| test_receiver.take(4).map(|e| e.into_with_event_time(fake_time)).collect().await; |
| |
| assert_eq!( |
| actual, |
| vec![ |
| new_keyboard_input_event(Key::A, KeyEventType::Pressed), |
| new_keyboard_input_event(Key::A, KeyEventType::Cancel) |
| .into_with_device_descriptor(empty_keyboard_device_descriptor()), |
| new_keyboard_input_event(Key::A, KeyEventType::Sync) |
| .into_with_device_descriptor(empty_keyboard_device_descriptor()), |
| new_keyboard_input_event(Key::A, KeyEventType::Released), |
| ] |
| ); |
| } |
| |
| #[fuchsia::test] |
| async fn more_key_state_handling() { |
| let (test_event, handler_event) = EventPair::create().unwrap(); |
| let (test_sender, handler_receiver) = mpsc::unbounded::<InputEvent>(); |
| let (handler_sender, test_receiver) = mpsc::unbounded::<InputEvent>(); |
| let (loop_done_sender, mut loop_done) = mpsc::unbounded::<()>(); |
| let mut wrangler = DisplayWrangler::new(test_event); |
| let handler = DisplayOwnership::new_for_test(handler_event, loop_done_sender); |
| let _task = fasync::Task::local(async move { |
| handler.handle_input_events(handler_receiver, handler_sender).await.unwrap(); |
| }); |
| |
| let fake_time = Time::from_nanos(42); |
| |
| wrangler.set_owned(); |
| loop_done.next().await; |
| test_sender |
| .unbounded_send(new_keyboard_input_event(Key::A, KeyEventType::Pressed)) |
| .unwrap(); |
| loop_done.next().await; |
| test_sender |
| .unbounded_send(new_keyboard_input_event(Key::B, KeyEventType::Pressed)) |
| .unwrap(); |
| loop_done.next().await; |
| |
| // Lose display, release a key, press a key. |
| wrangler.set_unowned(); |
| loop_done.next().await; |
| test_sender |
| .unbounded_send(new_keyboard_input_event(Key::B, KeyEventType::Released)) |
| .unwrap(); |
| loop_done.next().await; |
| test_sender |
| .unbounded_send(new_keyboard_input_event(Key::C, KeyEventType::Pressed)) |
| .unwrap(); |
| loop_done.next().await; |
| |
| // Regain display |
| wrangler.set_owned(); |
| loop_done.next().await; |
| |
| // Key event after regaining. |
| test_sender |
| .unbounded_send(new_keyboard_input_event(Key::A, KeyEventType::Released)) |
| .unwrap(); |
| loop_done.next().await; |
| test_sender |
| .unbounded_send(new_keyboard_input_event(Key::C, KeyEventType::Released)) |
| .unwrap(); |
| loop_done.next().await; |
| |
| let actual: Vec<InputEvent> = |
| test_receiver.take(10).map(|e| e.into_with_event_time(fake_time)).collect().await; |
| |
| assert_eq!( |
| actual, |
| vec![ |
| new_keyboard_input_event(Key::A, KeyEventType::Pressed), |
| new_keyboard_input_event(Key::B, KeyEventType::Pressed), |
| new_keyboard_input_event(Key::A, KeyEventType::Cancel) |
| .into_with_device_descriptor(empty_keyboard_device_descriptor()), |
| new_keyboard_input_event(Key::B, KeyEventType::Cancel) |
| .into_with_device_descriptor(empty_keyboard_device_descriptor()), |
| new_keyboard_input_event(Key::B, KeyEventType::Released).into_handled(), |
| new_keyboard_input_event(Key::C, KeyEventType::Pressed).into_handled(), |
| // The CANCEL and SYNC events are emitted in the sort ordering of the |
| // `Key` enum values. Perhaps they should be emitted instead in the order |
| // they have been received for SYNC, and in reverse order for CANCEL. |
| new_keyboard_input_event(Key::A, KeyEventType::Sync) |
| .into_with_device_descriptor(empty_keyboard_device_descriptor()), |
| new_keyboard_input_event(Key::C, KeyEventType::Sync) |
| .into_with_device_descriptor(empty_keyboard_device_descriptor()), |
| new_keyboard_input_event(Key::A, KeyEventType::Released), |
| new_keyboard_input_event(Key::C, KeyEventType::Released), |
| ] |
| ); |
| } |
| } |