1use crate::input_device::InputDevice;
6use crate::new_fake_device_info;
7use anyhow::{Context as _, Error};
8use async_utils::event::Event as AsyncEvent;
9use fidl::endpoints;
10use fidl_fuchsia_input::Key;
11use fidl_fuchsia_input_injection::InputDeviceRegistryProxy;
12use fidl_fuchsia_input_report::{
13 Axis, ConsumerControlButton, ConsumerControlDescriptor, ConsumerControlInputDescriptor,
14 ContactInputDescriptor, DeviceDescriptor, InputDeviceMarker, KeyboardDescriptor,
15 KeyboardInputDescriptor, MouseDescriptor, MouseInputDescriptor, Range, TOUCH_MAX_CONTACTS,
16 TouchDescriptor, TouchInputDescriptor, TouchType, Unit, UnitType,
17};
18use fidl_fuchsia_ui_test_input::MouseButton;
19
20pub(crate) struct InputDeviceRegistry {
22 proxy: InputDeviceRegistryProxy,
23 got_input_reports_reader: AsyncEvent,
24}
25
26impl InputDeviceRegistry {
27 pub fn new(proxy: InputDeviceRegistryProxy, got_input_reports_reader: AsyncEvent) -> Self {
28 Self { proxy, got_input_reports_reader }
29 }
30
31 pub async fn add_touchscreen_device(
38 &mut self,
39 min_x: i64,
40 max_x: i64,
41 min_y: i64,
42 max_y: i64,
43 ) -> Result<InputDevice, Error> {
44 self.add_device(DeviceDescriptor {
45 touch: Some(TouchDescriptor {
46 input: Some(TouchInputDescriptor {
47 contacts: Some(
48 std::iter::repeat(ContactInputDescriptor {
49 position_x: Some(Axis {
50 range: Range { min: min_x, max: max_x },
51 unit: Unit { type_: UnitType::Other, exponent: 0 },
52 }),
53 position_y: Some(Axis {
54 range: Range { min: min_y, max: max_y },
55 unit: Unit { type_: UnitType::Other, exponent: 0 },
56 }),
57 contact_width: Some(Axis {
58 range: Range { min: min_x, max: max_x },
59 unit: Unit { type_: UnitType::Other, exponent: 0 },
60 }),
61 contact_height: Some(Axis {
62 range: Range { min: min_y, max: max_y },
63 unit: Unit { type_: UnitType::Other, exponent: 0 },
64 }),
65 ..Default::default()
66 })
67 .take(
68 usize::try_from(TOUCH_MAX_CONTACTS)
69 .context("usize is impossibly small")?,
70 )
71 .collect(),
72 ),
73 max_contacts: Some(TOUCH_MAX_CONTACTS),
74 touch_type: Some(TouchType::Touchscreen),
75 buttons: Some(vec![]),
76 ..Default::default()
77 }),
78 ..Default::default()
79 }),
80 ..Default::default()
81 })
82 .await
83 }
84
85 pub async fn add_media_buttons_device(&mut self) -> Result<InputDevice, Error> {
91 self.add_device(DeviceDescriptor {
92 consumer_control: Some(ConsumerControlDescriptor {
93 input: Some(ConsumerControlInputDescriptor {
94 buttons: Some(vec![
95 ConsumerControlButton::VolumeUp,
96 ConsumerControlButton::VolumeDown,
97 ConsumerControlButton::Pause,
98 ConsumerControlButton::FactoryReset,
99 ConsumerControlButton::MicMute,
100 ConsumerControlButton::Reboot,
101 ConsumerControlButton::CameraDisable,
102 ConsumerControlButton::Power,
103 ConsumerControlButton::Function,
104 ]),
105 ..Default::default()
106 }),
107 ..Default::default()
108 }),
109 ..Default::default()
110 })
111 .await
112 }
113
114 pub async fn add_keyboard_device(&mut self) -> Result<InputDevice, Error> {
120 let all_keys: Vec<Key> = (Key::A.into_primitive()
129 ..=Key::MediaVolumeDecrement.into_primitive())
130 .filter_map(Key::from_primitive)
131 .collect();
132 self.add_device(DeviceDescriptor {
133 device_information: Some(new_fake_device_info()),
135 keyboard: Some(KeyboardDescriptor {
136 input: Some(KeyboardInputDescriptor {
137 keys3: Some(all_keys),
138 ..Default::default()
139 }),
140 ..Default::default()
141 }),
142 ..Default::default()
143 })
144 .await
145 }
146
147 pub async fn add_mouse_device(&mut self) -> Result<InputDevice, Error> {
148 self.add_device(DeviceDescriptor {
149 device_information: Some(new_fake_device_info()),
151 mouse: Some(MouseDescriptor {
152 input: Some(MouseInputDescriptor {
153 movement_x: Some(Axis {
154 range: Range { min: -1000, max: 1000 },
155 unit: Unit { type_: UnitType::Other, exponent: 0 },
156 }),
157 movement_y: Some(Axis {
158 range: Range { min: -1000, max: 1000 },
159 unit: Unit { type_: UnitType::Other, exponent: 0 },
160 }),
161 scroll_v: Some(Axis {
165 range: Range { min: -100, max: 100 },
166 unit: Unit { type_: UnitType::Other, exponent: 0 },
167 }),
168 scroll_h: Some(Axis {
169 range: Range { min: -100, max: 100 },
170 unit: Unit { type_: UnitType::Other, exponent: 0 },
171 }),
172 buttons: Some(
174 (MouseButton::First.into_primitive()..=MouseButton::Third.into_primitive())
175 .map(|b| {
176 b.try_into().expect("failed to convert mouse button to primitive")
177 })
178 .collect(),
179 ),
180 position_x: None,
181 position_y: None,
182 ..Default::default()
183 }),
184 ..Default::default()
185 }),
186 ..Default::default()
187 })
188 .await
189 }
190
191 async fn add_device(&self, descriptor: DeviceDescriptor) -> Result<InputDevice, Error> {
199 let (client_end, request_stream) = endpoints::create_request_stream::<InputDeviceMarker>();
200 let mut device: InputDevice =
201 InputDevice::new(request_stream, descriptor, self.got_input_reports_reader.clone());
202
203 let res = self.proxy.register_and_get_device_info(client_end).await?;
204 let device_id = res.device_id.expect("missing device_id");
205 device.device_id = device_id;
206
207 Ok(device)
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214 use fidl_fuchsia_input_injection::{
215 InputDeviceRegistryMarker, InputDeviceRegistryRegisterAndGetDeviceInfoResponse,
216 InputDeviceRegistryRequest,
217 };
218 use fidl_fuchsia_input_report::InputReportsReaderV2Marker;
219 use futures::StreamExt;
220 use test_case::test_case;
221
222 const MAX_UNACKNOWLEDGED_REPORTS_LIMIT: u16 = 120;
223
224 enum TestDeviceType {
225 TouchScreen,
226 MediaButtons,
227 Keyboard,
228 Mouse,
229 }
230
231 async fn add_device_for_test(
232 registry: &mut InputDeviceRegistry,
233 ty: TestDeviceType,
234 ) -> Result<InputDevice, Error> {
235 match ty {
236 TestDeviceType::TouchScreen => registry.add_touchscreen_device(1, 1000, 1, 1000).await,
237 TestDeviceType::MediaButtons => registry.add_media_buttons_device().await,
238 TestDeviceType::Keyboard => registry.add_keyboard_device().await,
239 TestDeviceType::Mouse => registry.add_mouse_device().await,
240 }
241 }
242
243 #[test_case(TestDeviceType::TouchScreen =>
244 matches Ok(DeviceDescriptor {
245 touch: Some(TouchDescriptor {
246 input: Some(TouchInputDescriptor { .. }),
247 ..
248 }),
249 .. });
250 "touchscreen_device")]
251 #[test_case(TestDeviceType::MediaButtons =>
252 matches Ok(DeviceDescriptor {
253 consumer_control: Some(ConsumerControlDescriptor {
254 input: Some(ConsumerControlInputDescriptor { .. }),
255 ..
256 }),
257 .. });
258 "media_buttons_device")]
259 #[test_case(TestDeviceType::Keyboard =>
260 matches Ok(DeviceDescriptor {
261 keyboard: Some(KeyboardDescriptor { .. }),
262 ..
263 });
264 "keyboard_device")]
265 #[test_case(TestDeviceType::Mouse =>
266 matches Ok(DeviceDescriptor {
267 mouse: Some(MouseDescriptor { .. }),
268 ..
269 });
270 "mouse_device")]
271 #[fuchsia::test]
272 async fn add_device_registers_correct_device_type(
273 device_type: TestDeviceType,
274 ) -> Result<DeviceDescriptor, Error> {
275 let (registry_proxy, mut registry_request_stream) =
276 endpoints::create_proxy_and_stream::<InputDeviceRegistryMarker>();
277 let mut input_device_registry = InputDeviceRegistry {
278 proxy: registry_proxy,
279 got_input_reports_reader: AsyncEvent::new(),
280 };
281
282 let add_device_fut = add_device_for_test(&mut input_device_registry, device_type);
283
284 let input_device_proxy_fut = async {
285 let input_device_proxy = match registry_request_stream
291 .next()
292 .await
293 .expect("stream read should yield Some")
294 .expect("fidl read")
295 {
296 InputDeviceRegistryRequest::Register { .. } => {
297 unreachable!("InputDeviceRegistryRequest::Register should not be called");
298 }
299 InputDeviceRegistryRequest::RegisterAndGetDeviceInfo {
300 device, responder, ..
301 } => {
302 responder
303 .send(InputDeviceRegistryRegisterAndGetDeviceInfoResponse {
304 device_id: Some(1),
305 ..Default::default()
306 })
307 .expect("RegisterAndGetDeviceInfo send response failed");
308
309 device
310 }
311 }
312 .into_proxy();
313
314 input_device_proxy
315 };
316
317 let (add_device_res, input_device_proxy) =
318 futures::join!(add_device_fut, input_device_proxy_fut);
319
320 let input_device = add_device_res.expect("add_device failed");
321 assert_ne!(input_device.device_id, 0);
322
323 let input_device_get_descriptor = input_device_proxy.get_descriptor().await;
324
325 let input_device_server_fut = input_device.flush();
326
327 let (_input_reports_reader_proxy, input_reports_reader_server_end) =
331 endpoints::create_proxy::<InputReportsReaderV2Marker>();
332 let _ = input_device_proxy
333 .get_input_reports_reader_v2(
334 input_reports_reader_server_end,
335 MAX_UNACKNOWLEDGED_REPORTS_LIMIT,
336 )
337 .await;
338
339 std::mem::drop(input_device_proxy); input_device_server_fut.await;
341
342 input_device_get_descriptor.map_err(anyhow::Error::from)
343 }
344}