blob: 98b5e3fdaa1f78ca35440ff4fc73761b7c55fc20 [file] [log] [blame]
// 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::{modern_backend::input_device::InputDevice, synthesizer},
anyhow::{Context as _, Error},
fidl::endpoints,
fidl_fuchsia_input::Key,
fidl_fuchsia_input_injection::InputDeviceRegistryProxy,
fidl_fuchsia_input_report::{
Axis, ConsumerControlButton, ConsumerControlDescriptor, ConsumerControlInputDescriptor,
ContactInputDescriptor, DeviceDescriptor, DeviceInfo, InputDeviceMarker,
KeyboardDescriptor, KeyboardInputDescriptor, MouseDescriptor, MouseInputDescriptor, Range,
TouchDescriptor, TouchInputDescriptor, TouchType, Unit, UnitType, TOUCH_MAX_CONTACTS,
},
};
// Use this to place required DeviceInfo into DeviceDescriptor.
fn new_fake_device_info() -> DeviceInfo {
DeviceInfo { product_id: 42, vendor_id: 43, version: u32::MAX, polling_rate: 1000 }
}
/// Implements the `synthesizer::InputDeviceRegistry` trait, and the client side
/// of the `fuchsia.input.injection.InputDeviceRegistry` protocol.
pub struct InputDeviceRegistry {
proxy: InputDeviceRegistryProxy,
}
impl synthesizer::InputDeviceRegistry for self::InputDeviceRegistry {
fn add_touchscreen_device(
&mut self,
width: u32,
height: u32,
) -> Result<Box<dyn synthesizer::InputDevice>, Error> {
self.add_device(DeviceDescriptor {
// Required for DeviceDescriptor.
device_info: Some(new_fake_device_info()),
touch: Some(TouchDescriptor {
input: Some(TouchInputDescriptor {
contacts: Some(
std::iter::repeat(ContactInputDescriptor {
position_x: Some(Axis {
range: Range { min: 0, max: i64::from(width) },
unit: Unit { type_: UnitType::Other, exponent: 0 },
}),
position_y: Some(Axis {
range: Range { min: 0, max: i64::from(height) },
unit: Unit { type_: UnitType::Other, exponent: 0 },
}),
contact_width: Some(Axis {
range: Range { min: 0, max: i64::from(width) },
unit: Unit { type_: UnitType::Other, exponent: 0 },
}),
contact_height: Some(Axis {
range: Range { min: 0, max: i64::from(height) },
unit: Unit { type_: UnitType::Other, exponent: 0 },
}),
..Default::default()
})
.take(
usize::try_from(TOUCH_MAX_CONTACTS)
.context("usize is impossibly small")?,
)
.collect(),
),
max_contacts: Some(TOUCH_MAX_CONTACTS),
touch_type: Some(TouchType::Touchscreen),
buttons: Some(vec![]),
..Default::default()
}),
..Default::default()
}),
..Default::default()
})
}
fn add_keyboard_device(&mut self) -> Result<Box<dyn synthesizer::InputDevice>, Error> {
// Generate a `Vec` of all known keys.
// * Because there is no direct way to iterate over enum values, we iterate
// over the primitives corresponding to `Key::A` and `Key::MediaVolumeDecrement`.
// * Some primitive values in the range have no corresponding enum value. For
// example, the value 0x00070065 sits between `NonUsBackslash` (0x00070064), and
// `KeypadEquals` (0x00070067). Such primitives are removed by `filter_map()`.
let all_keys: Vec<Key> = (Key::A.into_primitive()
..=Key::MediaVolumeDecrement.into_primitive())
.filter_map(Key::from_primitive)
.collect();
self.add_device(DeviceDescriptor {
// Required for DeviceDescriptor.
device_info: Some(new_fake_device_info()),
keyboard: Some(KeyboardDescriptor {
input: Some(KeyboardInputDescriptor {
keys3: Some(all_keys.clone()),
..Default::default()
}),
..Default::default()
}),
..Default::default()
})
}
fn add_media_buttons_device(&mut self) -> Result<Box<dyn synthesizer::InputDevice>, Error> {
self.add_device(DeviceDescriptor {
// Required for DeviceDescriptor.
device_info: Some(new_fake_device_info()),
consumer_control: Some(ConsumerControlDescriptor {
input: Some(ConsumerControlInputDescriptor {
buttons: Some(vec![
ConsumerControlButton::VolumeUp,
ConsumerControlButton::VolumeDown,
ConsumerControlButton::Pause,
ConsumerControlButton::FactoryReset,
ConsumerControlButton::MicMute,
ConsumerControlButton::Reboot,
ConsumerControlButton::CameraDisable,
]),
..Default::default()
}),
..Default::default()
}),
..Default::default()
})
}
fn add_mouse_device(
&mut self,
width: u32,
height: u32,
) -> Result<Box<dyn synthesizer::InputDevice>, Error> {
self.add_device(DeviceDescriptor {
// Required for DeviceDescriptor.
device_info: Some(new_fake_device_info()),
mouse: Some(MouseDescriptor {
input: Some(MouseInputDescriptor {
movement_x: Some(Axis {
range: Range { min: 0, max: i64::from(width) },
unit: Unit { type_: UnitType::Other, exponent: 0 },
}),
movement_y: Some(Axis {
range: Range { min: 0, max: i64::from(height) },
unit: Unit { type_: UnitType::Other, exponent: 0 },
}),
// `scroll_v` and `scroll_h` are range of tick number on
// driver's report. [-100, 100] should be enough for
// testing.
scroll_v: Some(Axis {
range: Range { min: -100, max: 100 },
unit: Unit { type_: UnitType::Other, exponent: 0 },
}),
scroll_h: Some(Axis {
range: Range { min: -100, max: 100 },
unit: Unit { type_: UnitType::Other, exponent: 0 },
}),
buttons: Some(vec![0, 1, 2]),
position_x: None,
position_y: None,
..Default::default()
}),
..Default::default()
}),
..Default::default()
})
}
}
impl InputDeviceRegistry {
pub fn new(proxy: InputDeviceRegistryProxy) -> Self {
Self { proxy }
}
/// Adds a device to the `InputDeviceRegistry` FIDL server connected to this
/// `InputDeviceRegistry` struct.
///
/// # Returns
/// A `synthesizer::InputDevice`, which can be used to send events to the
/// `fuchsia.input.report.InputDevice` that has been registered with the
/// `fuchsia.input.injection.InputDeviceRegistry` service.
fn add_device(
&self,
descriptor: DeviceDescriptor,
) -> Result<Box<dyn synthesizer::InputDevice>, Error> {
let (client_end, request_stream) = endpoints::create_request_stream::<InputDeviceMarker>()?;
self.proxy.register(client_end)?;
Ok(Box::new(InputDevice::new(request_stream, descriptor)))
}
}
#[cfg(test)]
mod tests {
use {
super::{synthesizer::InputDeviceRegistry as _, *},
anyhow::format_err,
fidl_fuchsia_input_injection::{InputDeviceRegistryMarker, InputDeviceRegistryRequest},
fuchsia_async as fasync,
futures::{pin_mut, task::Poll, StreamExt},
test_case::test_case,
};
#[test_case(&super::InputDeviceRegistry::add_keyboard_device; "keyboard_device")]
#[test_case(&super::InputDeviceRegistry::add_media_buttons_device; "media_button_device")]
#[test_case(&|registry| InputDeviceRegistry::add_touchscreen_device(registry, 640, 480);
"touchscreen_device")]
#[test_case(&|registry| InputDeviceRegistry::add_mouse_device(registry, 640, 480);
"mouse_device")]
fn add_device_invokes_fidl_register_method_exactly_once(
add_device_method: &dyn Fn(
&mut super::InputDeviceRegistry,
) -> Result<Box<dyn synthesizer::InputDevice>, Error>,
) -> Result<(), Error> {
let mut executor = fasync::TestExecutor::new();
let (proxy, request_stream) =
endpoints::create_proxy_and_stream::<InputDeviceRegistryMarker>()
.context("failed to create proxy and stream for InputDeviceRegistry")?;
add_device_method(&mut InputDeviceRegistry { proxy }).context("adding device")?;
let requests = match executor.run_until_stalled(&mut request_stream.collect::<Vec<_>>()) {
Poll::Ready(reqs) => reqs,
Poll::Pending => return Err(format_err!("request_stream did not terminate")),
};
assert_matches::assert_matches!(
requests.as_slice(),
[Ok(InputDeviceRegistryRequest::Register { .. })]
);
Ok(())
}
#[test_case(&super::InputDeviceRegistry::add_keyboard_device =>
matches Ok(DeviceDescriptor { keyboard: Some(_), .. });
"keyboard_device")]
#[test_case(&super::InputDeviceRegistry::add_media_buttons_device =>
matches Ok(DeviceDescriptor { consumer_control: Some(_), .. });
"media_button_device")]
#[test_case(&|registry| InputDeviceRegistry::add_touchscreen_device(registry, 640, 480) =>
matches Ok(DeviceDescriptor {
touch: Some(TouchDescriptor {
input: Some(TouchInputDescriptor { .. }),
..
}),
.. });
"touchscreen_device")]
#[test_case(&|registry| InputDeviceRegistry::add_mouse_device(registry, 640, 480) =>
matches Ok(DeviceDescriptor {
mouse: Some(MouseDescriptor {
input: Some(MouseInputDescriptor { .. }),
..
}),
.. });
"mouse_device")]
fn add_device_registers_correct_device_type(
add_device_method: &dyn Fn(
&mut super::InputDeviceRegistry,
) -> Result<Box<dyn synthesizer::InputDevice>, Error>,
) -> Result<DeviceDescriptor, Error> {
let mut executor = fasync::TestExecutor::new();
// Create an `InputDeviceRegistry`, and add a keyboard to it.
let (registry_proxy, mut registry_request_stream) =
endpoints::create_proxy_and_stream::<InputDeviceRegistryMarker>()
.context("failed to create proxy and stream for InputDeviceRegistry")?;
let mut input_device_registry = InputDeviceRegistry { proxy: registry_proxy };
let input_device =
add_device_method(&mut input_device_registry).context("adding keyboard")?;
let test_fut = async {
// `input_device_registry` should send a `Register` messgage to `registry_request_stream`.
// Use `registry_request_stream` to grab the `ClientEnd` of the keyboard added above,
// and convert the `ClientEnd` into an `InputDeviceProxy`.
let input_device_proxy = match registry_request_stream
.next()
.await
.context("stream read should yield Some")?
.context("fidl read")?
{
InputDeviceRegistryRequest::Register { device, .. } => device,
}
.into_proxy()
.context("converting client_end to proxy")?;
// Send a `GetDescriptor` request to `input_device`, and verify that the device
// is as keyboard.
let input_device_get_descriptor_fut = input_device_proxy.get_descriptor();
let input_device_server_fut = input_device.flush();
std::mem::drop(input_device_proxy); // Terminate stream served by `input_device_server_fut`.
let (_server_result, get_descriptor_result) =
futures::future::join(input_device_server_fut, input_device_get_descriptor_fut)
.await;
get_descriptor_result.map_err(anyhow::Error::from)
};
pin_mut!(test_fut);
match executor.run_until_stalled(&mut test_fut) {
Poll::Ready(r) => r,
Poll::Pending => Err(format_err!("test did not complete")),
}
}
}