| // Copyright 2021 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, Handled, InputDeviceBinding}, |
| anyhow::{format_err, Error}, |
| async_trait::async_trait, |
| fidl_fuchsia_input_report as fidl_input_report, |
| fidl_fuchsia_input_report::{InputDeviceProxy, InputReport}, |
| fuchsia_syslog::fx_log_err, |
| fuchsia_zircon as zx, |
| futures::channel::mpsc::Sender, |
| }; |
| |
| /// A [`ConsumerControlsEvent`] represents an event where one or more consumer control buttons |
| /// were pressed. |
| /// |
| /// # Example |
| /// The following ConsumerControlsEvents represents an event where the volume up button was pressed. |
| /// |
| /// ``` |
| /// let volume_event = input_device::InputDeviceEvent::ConsumerControls(ConsumerControlsEvent::new( |
| /// vec![fidl_input_report::ConsumerControlButton::VOLUME_UP], |
| /// )); |
| /// ``` |
| #[derive(Clone, Debug, PartialEq)] |
| pub struct ConsumerControlsEvent { |
| pub pressed_buttons: Vec<fidl_input_report::ConsumerControlButton>, |
| } |
| |
| impl ConsumerControlsEvent { |
| /// Creates a new [`ConsumerControlsEvent`] with the relevant buttons. |
| /// |
| /// # Parameters |
| /// - `pressed_buttons`: The buttons relevant to this event. |
| pub fn new(pressed_buttons: Vec<fidl_input_report::ConsumerControlButton>) -> Self { |
| Self { pressed_buttons } |
| } |
| } |
| |
| /// A [`ConsumerControlsBinding`] represents a connection to a consumer controls input device with |
| /// consumer controls. The buttons supported by this binding is returned by `supported_buttons()`. |
| /// |
| /// The [`ConsumerControlsBinding`] parses and exposes consumer control descriptor properties |
| /// for the device it is associated with. It also parses [`InputReport`]s |
| /// from the device, and sends them to the device binding owner over `event_sender`. |
| pub struct ConsumerControlsBinding { |
| /// The channel to stream InputEvents to. |
| event_sender: Sender<input_device::InputEvent>, |
| |
| /// Holds information about this device. |
| device_descriptor: ConsumerControlsDeviceDescriptor, |
| } |
| |
| #[derive(Clone, Debug, Eq, PartialEq)] |
| pub struct ConsumerControlsDeviceDescriptor { |
| /// The list of buttons that this device contains. |
| pub buttons: Vec<fidl_input_report::ConsumerControlButton>, |
| } |
| |
| #[async_trait] |
| impl input_device::InputDeviceBinding for ConsumerControlsBinding { |
| fn input_event_sender(&self) -> Sender<input_device::InputEvent> { |
| self.event_sender.clone() |
| } |
| |
| fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor { |
| input_device::InputDeviceDescriptor::ConsumerControls(self.device_descriptor.clone()) |
| } |
| } |
| |
| impl ConsumerControlsBinding { |
| /// Creates a new [`InputDeviceBinding`] from the `device_proxy`. |
| /// |
| /// The binding will start listening for input reports immediately and send new InputEvents |
| /// to the device binding owner over `input_event_sender`. |
| /// |
| /// # Parameters |
| /// - `device_proxy`: The proxy to bind the new [`InputDeviceBinding`] to. |
| /// - `input_event_sender`: The channel to send new InputEvents to. |
| /// |
| /// # Errors |
| /// If there was an error binding to the proxy. |
| pub async fn new( |
| device_proxy: InputDeviceProxy, |
| input_event_sender: Sender<input_device::InputEvent>, |
| ) -> Result<Self, Error> { |
| let device_binding = Self::bind_device(&device_proxy, input_event_sender).await?; |
| input_device::initialize_report_stream( |
| device_proxy, |
| device_binding.get_device_descriptor(), |
| device_binding.input_event_sender(), |
| Self::process_reports, |
| ); |
| |
| Ok(device_binding) |
| } |
| |
| /// Binds the provided input device to a new instance of `Self`. |
| /// |
| /// # Parameters |
| /// - `device`: The device to use to initialize the binding. |
| /// - `input_event_sender`: The channel to send new InputEvents to. |
| /// |
| /// # Errors |
| /// If the device descriptor could not be retrieved, or the descriptor could |
| /// not be parsed correctly. |
| async fn bind_device( |
| device: &InputDeviceProxy, |
| input_event_sender: Sender<input_device::InputEvent>, |
| ) -> Result<Self, Error> { |
| let device_descriptor: fidl_input_report::DeviceDescriptor = |
| device.get_descriptor().await?; |
| |
| let consumer_controls_descriptor = device_descriptor.consumer_control.ok_or_else(|| { |
| format_err!("DeviceDescriptor does not have a ConsumerControlDescriptor") |
| })?; |
| |
| let consumer_controls_input_descriptor = |
| consumer_controls_descriptor.input.ok_or_else(|| { |
| format_err!( |
| "ConsumerControlDescriptor does not have a ConsumerControlInputDescriptor" |
| ) |
| })?; |
| |
| let device_descriptor: ConsumerControlsDeviceDescriptor = |
| ConsumerControlsDeviceDescriptor { |
| buttons: consumer_controls_input_descriptor.buttons.unwrap_or_default(), |
| }; |
| |
| Ok(ConsumerControlsBinding { event_sender: input_event_sender, device_descriptor }) |
| } |
| |
| /// Parses an [`InputReport`] into one or more [`InputEvent`]s. Sends the [`InputEvent`]s |
| /// to the device binding owner via [`input_event_sender`]. |
| /// |
| /// # Parameters |
| /// `report`: The incoming [`InputReport`]. |
| /// `previous_report`: The previous [`InputReport`] seen for the same device. This can be |
| /// used to determine, for example, which keys are no longer present in |
| /// a keyboard report to generate key released events. If `None`, no |
| /// previous report was found. |
| /// `device_descriptor`: The descriptor for the input device generating the input reports. |
| /// `input_event_sender`: The sender for the device binding's input event stream. |
| /// |
| /// # Returns |
| /// An [`InputReport`] which will be passed to the next call to [`process_reports`], as |
| /// [`previous_report`]. If `None`, the next call's [`previous_report`] will be `None`. |
| fn process_reports( |
| report: InputReport, |
| previous_report: Option<InputReport>, |
| device_descriptor: &input_device::InputDeviceDescriptor, |
| input_event_sender: &mut Sender<input_device::InputEvent>, |
| ) -> Option<InputReport> { |
| // Input devices can have multiple types so ensure `report` is a ConsumerControlInputReport. |
| let pressed_buttons: Vec<fidl_input_report::ConsumerControlButton> = |
| match report.consumer_control { |
| Some(ref consumer_control_report) => consumer_control_report |
| .pressed_buttons |
| .as_ref() |
| .map(|buttons| buttons.iter().cloned().collect()) |
| .unwrap_or_default(), |
| None => return previous_report, |
| }; |
| |
| let event_time: zx::Time = input_device::event_time_or_now(report.event_time); |
| |
| send_consumer_controls_event( |
| pressed_buttons, |
| device_descriptor, |
| event_time, |
| input_event_sender, |
| ); |
| |
| Some(report) |
| } |
| } |
| |
| /// Sends an InputEvent over `sender`. |
| /// |
| /// # Parameters |
| /// - `pressed_buttons`: The buttons relevant to the event. |
| /// - `device_descriptor`: The descriptor for the input device generating the input reports. |
| /// - `event_time`: The time in nanoseconds when the event was first recorded. |
| /// - `sender`: The stream to send the InputEvent to. |
| fn send_consumer_controls_event( |
| pressed_buttons: Vec<fidl_input_report::ConsumerControlButton>, |
| device_descriptor: &input_device::InputDeviceDescriptor, |
| event_time: zx::Time, |
| sender: &mut Sender<input_device::InputEvent>, |
| ) { |
| if let Err(e) = sender.try_send(input_device::InputEvent { |
| device_event: input_device::InputDeviceEvent::ConsumerControls(ConsumerControlsEvent::new( |
| pressed_buttons, |
| )), |
| device_descriptor: device_descriptor.clone(), |
| event_time, |
| handled: Handled::No, |
| trace_id: None, |
| }) { |
| fx_log_err!("Failed to send ConsumerControlsEvent with error: {:?}", e); |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use {super::*, crate::testing_utilities, fuchsia_async as fasync, futures::StreamExt}; |
| |
| // Tests that an InputReport containing one consumer control button generates an InputEvent |
| // containing the same consumer control button. |
| #[fasync::run_singlethreaded(test)] |
| async fn volume_up_only() { |
| let (event_time_i64, event_time_u64) = testing_utilities::event_times(); |
| let pressed_buttons = vec![fidl_input_report::ConsumerControlButton::VolumeUp]; |
| let first_report = testing_utilities::create_consumer_control_input_report( |
| pressed_buttons.clone(), |
| event_time_i64, |
| ); |
| let descriptor = testing_utilities::consumer_controls_device_descriptor(); |
| |
| let input_reports = vec![first_report]; |
| let expected_events = vec![testing_utilities::create_consumer_controls_event( |
| pressed_buttons, |
| event_time_u64, |
| &descriptor, |
| )]; |
| |
| assert_input_report_sequence_generates_events!( |
| input_reports: input_reports, |
| expected_events: expected_events, |
| device_descriptor: descriptor, |
| device_type: ConsumerControlsBinding, |
| ); |
| } |
| |
| // Tests that an InputReport containing two consumer control buttons generates an InputEvent |
| // containing both consumer control buttons. |
| #[fasync::run_singlethreaded(test)] |
| async fn volume_up_and_down() { |
| let (event_time_i64, event_time_u64) = testing_utilities::event_times(); |
| let pressed_buttons = vec![ |
| fidl_input_report::ConsumerControlButton::VolumeUp, |
| fidl_input_report::ConsumerControlButton::VolumeDown, |
| ]; |
| let first_report = testing_utilities::create_consumer_control_input_report( |
| pressed_buttons.clone(), |
| event_time_i64, |
| ); |
| let descriptor = testing_utilities::consumer_controls_device_descriptor(); |
| |
| let input_reports = vec![first_report]; |
| let expected_events = vec![testing_utilities::create_consumer_controls_event( |
| pressed_buttons, |
| event_time_u64, |
| &descriptor, |
| )]; |
| |
| assert_input_report_sequence_generates_events!( |
| input_reports: input_reports, |
| expected_events: expected_events, |
| device_descriptor: descriptor, |
| device_type: ConsumerControlsBinding, |
| ); |
| } |
| |
| // Tests that three InputReports containing one consumer control button generates three |
| // InputEvents containing the same consumer control button. |
| #[fasync::run_singlethreaded(test)] |
| async fn sequence_of_buttons() { |
| let (event_time_i64, event_time_u64) = testing_utilities::event_times(); |
| let first_report = testing_utilities::create_consumer_control_input_report( |
| vec![fidl_input_report::ConsumerControlButton::VolumeUp], |
| event_time_i64, |
| ); |
| let second_report = testing_utilities::create_consumer_control_input_report( |
| vec![fidl_input_report::ConsumerControlButton::VolumeDown], |
| event_time_i64, |
| ); |
| let third_report = testing_utilities::create_consumer_control_input_report( |
| vec![fidl_input_report::ConsumerControlButton::CameraDisable], |
| event_time_i64, |
| ); |
| let descriptor = testing_utilities::consumer_controls_device_descriptor(); |
| |
| let input_reports = vec![first_report, second_report, third_report]; |
| let expected_events = vec![ |
| testing_utilities::create_consumer_controls_event( |
| vec![fidl_input_report::ConsumerControlButton::VolumeUp], |
| event_time_u64, |
| &descriptor, |
| ), |
| testing_utilities::create_consumer_controls_event( |
| vec![fidl_input_report::ConsumerControlButton::VolumeDown], |
| event_time_u64, |
| &descriptor, |
| ), |
| testing_utilities::create_consumer_controls_event( |
| vec![fidl_input_report::ConsumerControlButton::CameraDisable], |
| event_time_u64, |
| &descriptor, |
| ), |
| ]; |
| |
| assert_input_report_sequence_generates_events!( |
| input_reports: input_reports, |
| expected_events: expected_events, |
| device_descriptor: descriptor, |
| device_type: ConsumerControlsBinding, |
| ); |
| } |
| } |