input_synthesis/modern_backend/
input_device_registry.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::modern_backend::input_device::InputDevice;
6use crate::synthesizer;
7use anyhow::{Context as _, Error};
8use fidl::endpoints;
9use fidl_fuchsia_input::Key;
10use fidl_fuchsia_input_injection::InputDeviceRegistryProxy;
11use fidl_fuchsia_input_report::{
12    Axis, ConsumerControlButton, ConsumerControlDescriptor, ConsumerControlInputDescriptor,
13    ContactInputDescriptor, DeviceDescriptor, DeviceInformation, InputDeviceMarker,
14    KeyboardDescriptor, KeyboardInputDescriptor, MouseDescriptor, MouseInputDescriptor, Range,
15    TouchDescriptor, TouchInputDescriptor, TouchType, Unit, UnitType, TOUCH_MAX_CONTACTS,
16};
17
18// Use this to place required DeviceInfo into DeviceDescriptor.
19fn new_fake_device_info() -> DeviceInformation {
20    DeviceInformation {
21        product_id: Some(42),
22        vendor_id: Some(43),
23        version: Some(u32::MAX),
24        polling_rate: Some(1000),
25        ..Default::default()
26    }
27}
28
29/// Implements the `synthesizer::InputDeviceRegistry` trait, and the client side
30/// of the `fuchsia.input.injection.InputDeviceRegistry` protocol.
31pub struct InputDeviceRegistry {
32    proxy: InputDeviceRegistryProxy,
33}
34
35impl synthesizer::InputDeviceRegistry for self::InputDeviceRegistry {
36    fn add_touchscreen_device(
37        &mut self,
38        width: u32,
39        height: u32,
40    ) -> Result<Box<dyn synthesizer::InputDevice>, Error> {
41        self.add_device(DeviceDescriptor {
42            // Required for DeviceDescriptor.
43            device_information: Some(new_fake_device_info()),
44            touch: Some(TouchDescriptor {
45                input: Some(TouchInputDescriptor {
46                    contacts: Some(
47                        std::iter::repeat(ContactInputDescriptor {
48                            position_x: Some(Axis {
49                                range: Range { min: 0, max: i64::from(width) },
50                                unit: Unit { type_: UnitType::Other, exponent: 0 },
51                            }),
52                            position_y: Some(Axis {
53                                range: Range { min: 0, max: i64::from(height) },
54                                unit: Unit { type_: UnitType::Other, exponent: 0 },
55                            }),
56                            contact_width: Some(Axis {
57                                range: Range { min: 0, max: i64::from(width) },
58                                unit: Unit { type_: UnitType::Other, exponent: 0 },
59                            }),
60                            contact_height: Some(Axis {
61                                range: Range { min: 0, max: i64::from(height) },
62                                unit: Unit { type_: UnitType::Other, exponent: 0 },
63                            }),
64                            ..Default::default()
65                        })
66                        .take(
67                            usize::try_from(TOUCH_MAX_CONTACTS)
68                                .context("usize is impossibly small")?,
69                        )
70                        .collect(),
71                    ),
72                    max_contacts: Some(TOUCH_MAX_CONTACTS),
73                    touch_type: Some(TouchType::Touchscreen),
74                    buttons: Some(vec![]),
75                    ..Default::default()
76                }),
77                ..Default::default()
78            }),
79            ..Default::default()
80        })
81    }
82
83    fn add_keyboard_device(&mut self) -> Result<Box<dyn synthesizer::InputDevice>, Error> {
84        // Generate a `Vec` of all known keys.
85        // * Because there is no direct way to iterate over enum values, we iterate
86        //   over the primitives corresponding to `Key::A` and `Key::MediaVolumeDecrement`.
87        // * Some primitive values in the range have no corresponding enum value. For
88        //   example, the value 0x00070065 sits between `NonUsBackslash` (0x00070064), and
89        //   `KeypadEquals` (0x00070067). Such primitives are removed by `filter_map()`.
90        let all_keys: Vec<Key> = (Key::A.into_primitive()
91            ..=Key::MediaVolumeDecrement.into_primitive())
92            .filter_map(Key::from_primitive)
93            .collect();
94        self.add_device(DeviceDescriptor {
95            // Required for DeviceDescriptor.
96            device_information: Some(new_fake_device_info()),
97            keyboard: Some(KeyboardDescriptor {
98                input: Some(KeyboardInputDescriptor {
99                    keys3: Some(all_keys),
100                    ..Default::default()
101                }),
102                ..Default::default()
103            }),
104            ..Default::default()
105        })
106    }
107
108    fn add_media_buttons_device(&mut self) -> Result<Box<dyn synthesizer::InputDevice>, Error> {
109        self.add_device(DeviceDescriptor {
110            // Required for DeviceDescriptor.
111            device_information: Some(new_fake_device_info()),
112            consumer_control: Some(ConsumerControlDescriptor {
113                input: Some(ConsumerControlInputDescriptor {
114                    buttons: Some(vec![
115                        ConsumerControlButton::VolumeUp,
116                        ConsumerControlButton::VolumeDown,
117                        ConsumerControlButton::Pause,
118                        ConsumerControlButton::FactoryReset,
119                        ConsumerControlButton::MicMute,
120                        ConsumerControlButton::Reboot,
121                        ConsumerControlButton::CameraDisable,
122                    ]),
123                    ..Default::default()
124                }),
125                ..Default::default()
126            }),
127            ..Default::default()
128        })
129    }
130
131    fn add_mouse_device(
132        &mut self,
133        width: u32,
134        height: u32,
135    ) -> Result<Box<dyn synthesizer::InputDevice>, Error> {
136        self.add_device(DeviceDescriptor {
137            // Required for DeviceDescriptor.
138            device_information: Some(new_fake_device_info()),
139            mouse: Some(MouseDescriptor {
140                input: Some(MouseInputDescriptor {
141                    movement_x: Some(Axis {
142                        range: Range { min: 0, max: i64::from(width) },
143                        unit: Unit { type_: UnitType::Other, exponent: 0 },
144                    }),
145                    movement_y: Some(Axis {
146                        range: Range { min: 0, max: i64::from(height) },
147                        unit: Unit { type_: UnitType::Other, exponent: 0 },
148                    }),
149                    // `scroll_v` and `scroll_h` are range of tick number on
150                    // driver's report. [-100, 100] should be enough for
151                    // testing.
152                    scroll_v: Some(Axis {
153                        range: Range { min: -100, max: 100 },
154                        unit: Unit { type_: UnitType::Other, exponent: 0 },
155                    }),
156                    scroll_h: Some(Axis {
157                        range: Range { min: -100, max: 100 },
158                        unit: Unit { type_: UnitType::Other, exponent: 0 },
159                    }),
160                    buttons: Some(vec![0, 1, 2]),
161                    position_x: None,
162                    position_y: None,
163                    ..Default::default()
164                }),
165                ..Default::default()
166            }),
167            ..Default::default()
168        })
169    }
170}
171
172impl InputDeviceRegistry {
173    pub fn new(proxy: InputDeviceRegistryProxy) -> Self {
174        Self { proxy }
175    }
176
177    /// Adds a device to the `InputDeviceRegistry` FIDL server connected to this
178    /// `InputDeviceRegistry` struct.
179    ///
180    /// # Returns
181    /// A `synthesizer::InputDevice`, which can be used to send events to the
182    /// `fuchsia.input.report.InputDevice` that has been registered with the
183    /// `fuchsia.input.injection.InputDeviceRegistry` service.
184    fn add_device(
185        &self,
186        descriptor: DeviceDescriptor,
187    ) -> Result<Box<dyn synthesizer::InputDevice>, Error> {
188        let (client_end, request_stream) = endpoints::create_request_stream::<InputDeviceMarker>();
189        self.proxy.register(client_end)?;
190        Ok(Box::new(InputDevice::new(request_stream, descriptor)))
191    }
192}
193
194#[cfg(test)]
195mod tests {
196    use super::synthesizer::InputDeviceRegistry as _;
197    use super::*;
198    use anyhow::format_err;
199    use fidl_fuchsia_input_injection::{InputDeviceRegistryMarker, InputDeviceRegistryRequest};
200    use fuchsia_async as fasync;
201    use futures::task::Poll;
202    use futures::{pin_mut, StreamExt};
203    use test_case::test_case;
204
205    #[test_case(&super::InputDeviceRegistry::add_keyboard_device; "keyboard_device")]
206    #[test_case(&super::InputDeviceRegistry::add_media_buttons_device; "media_button_device")]
207    #[test_case(&|registry| InputDeviceRegistry::add_touchscreen_device(registry, 640, 480);
208                "touchscreen_device")]
209    #[test_case(&|registry| InputDeviceRegistry::add_mouse_device(registry, 640, 480);
210                "mouse_device")]
211    fn add_device_invokes_fidl_register_method_exactly_once(
212        add_device_method: &dyn Fn(
213            &mut super::InputDeviceRegistry,
214        ) -> Result<Box<dyn synthesizer::InputDevice>, Error>,
215    ) -> Result<(), Error> {
216        let mut executor = fasync::TestExecutor::new();
217        let (proxy, request_stream) =
218            endpoints::create_proxy_and_stream::<InputDeviceRegistryMarker>();
219        add_device_method(&mut InputDeviceRegistry { proxy }).context("adding device")?;
220
221        let requests = match executor.run_until_stalled(&mut request_stream.collect::<Vec<_>>()) {
222            Poll::Ready(reqs) => reqs,
223            Poll::Pending => return Err(format_err!("request_stream did not terminate")),
224        };
225        assert_matches::assert_matches!(
226            requests.as_slice(),
227            [Ok(InputDeviceRegistryRequest::Register { .. })]
228        );
229
230        Ok(())
231    }
232
233    #[test_case(&super::InputDeviceRegistry::add_keyboard_device =>
234                matches Ok(DeviceDescriptor { keyboard: Some(_), .. });
235                "keyboard_device")]
236    #[test_case(&super::InputDeviceRegistry::add_media_buttons_device =>
237                matches Ok(DeviceDescriptor { consumer_control: Some(_), .. });
238                "media_button_device")]
239    #[test_case(&|registry| InputDeviceRegistry::add_touchscreen_device(registry, 640, 480) =>
240                matches Ok(DeviceDescriptor {
241                    touch: Some(TouchDescriptor {
242                        input: Some(TouchInputDescriptor { .. }),
243                        ..
244                    }),
245                    .. });
246                "touchscreen_device")]
247    #[test_case(&|registry| InputDeviceRegistry::add_mouse_device(registry, 640, 480) =>
248                matches Ok(DeviceDescriptor {
249                    mouse: Some(MouseDescriptor {
250                        input: Some(MouseInputDescriptor { .. }),
251                        ..
252                    }),
253                    .. });
254                "mouse_device")]
255    fn add_device_registers_correct_device_type(
256        add_device_method: &dyn Fn(
257            &mut super::InputDeviceRegistry,
258        ) -> Result<Box<dyn synthesizer::InputDevice>, Error>,
259    ) -> Result<DeviceDescriptor, Error> {
260        let mut executor = fasync::TestExecutor::new();
261        // Create an `InputDeviceRegistry`, and add a keyboard to it.
262        let (registry_proxy, mut registry_request_stream) =
263            endpoints::create_proxy_and_stream::<InputDeviceRegistryMarker>();
264        let mut input_device_registry = InputDeviceRegistry { proxy: registry_proxy };
265        let input_device =
266            add_device_method(&mut input_device_registry).context("adding keyboard")?;
267
268        let test_fut = async {
269            // `input_device_registry` should send a `Register` messgage to `registry_request_stream`.
270            // Use `registry_request_stream` to grab the `ClientEnd` of the keyboard added above,
271            // and convert the `ClientEnd` into an `InputDeviceProxy`.
272            let input_device_proxy = match registry_request_stream
273                .next()
274                .await
275                .context("stream read should yield Some")?
276                .context("fidl read")?
277            {
278                InputDeviceRegistryRequest::Register { device, .. } => device,
279                InputDeviceRegistryRequest::RegisterAndGetDeviceInfo { device, .. } => device,
280            }
281            .into_proxy();
282
283            // Send a `GetDescriptor` request to `input_device`, and verify that the device
284            // is as keyboard.
285            let input_device_get_descriptor_fut = input_device_proxy.get_descriptor();
286            let input_device_server_fut = input_device.flush();
287            std::mem::drop(input_device_proxy); // Terminate stream served by `input_device_server_fut`.
288
289            let (_server_result, get_descriptor_result) =
290                futures::future::join(input_device_server_fut, input_device_get_descriptor_fut)
291                    .await;
292            get_descriptor_result.map_err(anyhow::Error::from)
293        };
294        pin_mut!(test_fut);
295
296        match executor.run_until_stalled(&mut test_fut) {
297            Poll::Ready(r) => r,
298            Poll::Pending => Err(format_err!("test did not complete")),
299        }
300    }
301}