Skip to main content

carnelian/
input.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::app::{InternalSender, MessageInternal};
6use crate::geometry::{IntPoint, IntSize};
7use anyhow::{Error, format_err};
8use euclid::default::Transform2D;
9use fidl::endpoints::create_proxy;
10use fidl_fuchsia_input_report as hid_input_report;
11use fuchsia_async::{self as fasync, MonotonicInstant, TimeoutExt};
12use futures::{TryFutureExt, TryStreamExt};
13use keymaps::usages::input3_key_to_hid_usage;
14use std::collections::HashSet;
15use std::fs;
16use std::hash::{Hash, Hasher};
17use std::path::{Path, PathBuf};
18use zx::{self as zx, MonotonicDuration};
19
20#[derive(Debug)]
21pub(crate) enum UserInputMessage {
22    ScenicKeyEvent(fidl_fuchsia_ui_input3::KeyEvent),
23    FlatlandMouseEvents(Vec<fidl_fuchsia_ui_pointer::MouseEvent>),
24    FlatlandTouchEvents(Vec<fidl_fuchsia_ui_pointer::TouchEvent>),
25}
26
27/// A button on a mouse
28#[derive(Clone, Debug, Default, Eq, PartialEq, Hash)]
29pub struct Button(pub u8);
30
31const PRIMARY_BUTTON: u8 = 1;
32
33impl Button {
34    /// Is this the primary button, usually the leftmost button on
35    /// a mouse.
36    pub fn is_primary(&self) -> bool {
37        self.0 == PRIMARY_BUTTON
38    }
39}
40
41/// A set of buttons.
42#[derive(Clone, Debug, Default, PartialEq)]
43pub struct ButtonSet {
44    buttons: HashSet<Button>,
45}
46
47impl ButtonSet {
48    /// Create a new set of buttons from input report flags.
49    pub fn new(buttons: &HashSet<u8>) -> ButtonSet {
50        ButtonSet { buttons: buttons.iter().map(|button| Button(*button)).collect() }
51    }
52
53    /// Create a new set of buttons from scenic flags.
54    pub fn new_from_flags(flags: u32) -> ButtonSet {
55        let buttons: HashSet<u8> = (0..2)
56            .filter_map(|index| {
57                let mask = 1 << index;
58                if flags & mask != 0 { Some(index + 1) } else { None }
59            })
60            .collect();
61        ButtonSet::new(&buttons)
62    }
63
64    /// Convenience function for checking if the primary button is down.
65    pub fn primary_button_is_down(&self) -> bool {
66        self.buttons.contains(&Button(PRIMARY_BUTTON))
67    }
68}
69
70/// Keyboard modifier keys.
71#[derive(Debug, Default, PartialEq, Clone, Copy)]
72pub struct Modifiers {
73    /// A shift key is down.
74    pub shift: bool,
75    /// An alt or option key is down.
76    pub alt: bool,
77    /// A control key is down.
78    pub control: bool,
79    /// A caps lock key is down.
80    pub caps_lock: bool,
81}
82
83impl Modifiers {
84    pub(crate) fn from_pressed_keys_3(pressed_keys: &HashSet<fidl_fuchsia_input::Key>) -> Self {
85        Self {
86            shift: pressed_keys.contains(&fidl_fuchsia_input::Key::LeftShift)
87                || pressed_keys.contains(&fidl_fuchsia_input::Key::RightShift),
88            alt: pressed_keys.contains(&fidl_fuchsia_input::Key::LeftAlt)
89                || pressed_keys.contains(&fidl_fuchsia_input::Key::RightAlt),
90            control: pressed_keys.contains(&fidl_fuchsia_input::Key::LeftCtrl)
91                || pressed_keys.contains(&fidl_fuchsia_input::Key::RightCtrl),
92            caps_lock: pressed_keys.contains(&fidl_fuchsia_input::Key::CapsLock),
93        }
94    }
95
96    pub(crate) fn is_modifier(key: &fidl_fuchsia_input::Key) -> bool {
97        match key {
98            fidl_fuchsia_input::Key::LeftShift
99            | fidl_fuchsia_input::Key::RightShift
100            | fidl_fuchsia_input::Key::LeftAlt
101            | fidl_fuchsia_input::Key::RightAlt
102            | fidl_fuchsia_input::Key::LeftCtrl
103            | fidl_fuchsia_input::Key::RightCtrl
104            | fidl_fuchsia_input::Key::CapsLock => true,
105            _ => false,
106        }
107    }
108}
109
110/// Mouse-related items
111pub mod mouse {
112    use super::*;
113    use crate::geometry::IntVector;
114
115    /// Phase of a mouse event.
116    #[derive(Debug, PartialEq, Clone)]
117    pub enum Phase {
118        /// A particular button went down.
119        Down(Button),
120        /// A particular button came up.
121        Up(Button),
122        /// The mouse moved, with or without a change in button state.
123        Moved,
124        /// The mouse wheel changed position.
125        Wheel(IntVector),
126    }
127
128    /// A mouse event.
129    #[derive(Debug, PartialEq, Clone)]
130    pub struct Event {
131        /// Pressed buttons.
132        pub buttons: ButtonSet,
133        /// Event phase.
134        pub phase: Phase,
135        /// Location of the mouse cursor during this event.
136        pub location: IntPoint,
137    }
138
139    pub(crate) fn create_event(
140        event_time: u64,
141        device_id: &DeviceId,
142        button_set: &ButtonSet,
143        cursor_position: IntPoint,
144        transform: &Transform2D<f32>,
145        phase: mouse::Phase,
146    ) -> super::Event {
147        let cursor_position = transform.transform_point(cursor_position.to_f32()).to_i32();
148        let mouse_event =
149            mouse::Event { buttons: button_set.clone(), phase, location: cursor_position };
150        super::Event {
151            event_time,
152            device_id: device_id.clone(),
153            event_type: EventType::Mouse(mouse_event),
154        }
155    }
156}
157
158/// Keyboard-related items.
159pub mod keyboard {
160    use super::*;
161
162    /// Phase of a keyboard event.
163    #[derive(Clone, Copy, Debug, PartialEq)]
164    pub enum Phase {
165        /// A key is pressed.
166        Pressed,
167        /// A key is released.
168        Released,
169        /// A key is no longer pressed without being released.
170        Cancelled,
171        /// A key has been held down long enough to start repeating.
172        Repeat,
173    }
174
175    /// A keyboard event.
176    #[derive(Debug, PartialEq, Clone)]
177    pub struct Event {
178        /// Event phase.
179        pub phase: Phase,
180        /// Unicode code point of the key causing the event, if any.
181        pub code_point: Option<u32>,
182        /// USB HID usage of the key causing the event.
183        pub hid_usage: u32,
184        /// Modifier keys being pressed or held in addition to the key
185        /// causing the event.
186        pub modifiers: Modifiers,
187    }
188}
189
190/// Touch-related items.
191pub mod touch {
192    use super::*;
193
194    #[derive(Debug, Eq)]
195    pub(crate) struct RawContact {
196        pub contact_id: u32,
197        pub position: IntPoint,
198        // TODO(https://fxbug.dev/42165549)
199        #[allow(unused)]
200        pub pressure: Option<i64>,
201        pub contact_size: Option<IntSize>,
202    }
203
204    impl PartialEq for RawContact {
205        fn eq(&self, rhs: &Self) -> bool {
206            self.contact_id == rhs.contact_id
207        }
208    }
209
210    impl Hash for RawContact {
211        fn hash<H: Hasher>(&self, state: &mut H) {
212            self.contact_id.hash(state);
213        }
214    }
215
216    /// ID of a touch contact.
217    #[derive(Clone, Copy, Debug, Eq, Ord, PartialOrd, PartialEq, Hash)]
218    pub struct ContactId(pub u32);
219
220    /// Phase of a touch event.
221    #[derive(Debug, PartialEq, Clone)]
222    pub enum Phase {
223        /// A contact began.
224        Down(IntPoint, IntSize),
225        /// A contact moved.
226        Moved(IntPoint, IntSize),
227        /// A contact ended.
228        Up,
229        /// A contact was removed.
230        Remove,
231        /// A contact was cancelled.
232        Cancel,
233    }
234
235    /// A single contact found in a touch event.
236    #[derive(Debug, Clone, PartialEq)]
237    pub struct Contact {
238        /// ID of this contact
239        pub contact_id: ContactId,
240        /// Phase of this contact
241        pub phase: Phase,
242    }
243
244    /// A touch event.
245    #[derive(Debug, PartialEq, Clone)]
246    pub struct Event {
247        /// All the current contact in this event
248        pub contacts: Vec<Contact>,
249        /// Buttons in this touch event, possible if the touch comes
250        /// from a stylus with buttons.
251        pub buttons: HashSet<hid_input_report::TouchButton>,
252    }
253}
254
255/// Pointer event
256///
257/// Carnelian provides a least-common-denominator pointer event that can be created from
258/// either touch events or mouse events.
259pub mod pointer {
260    use super::*;
261
262    /// Pointer phase.
263    #[derive(Debug, PartialEq, Clone)]
264    pub enum Phase {
265        /// A pointer has gone down.
266        Down(IntPoint),
267        /// A pointer has moved.
268        Moved(IntPoint),
269        /// A pointer has come up.
270        Up,
271        /// A pointer has been removed without coming up.
272        Remove,
273        /// A pointer has been cancelled.
274        Cancel,
275    }
276
277    /// Pointer ID.
278    #[derive(Clone, Debug, Eq, Ord, PartialOrd, PartialEq, Hash)]
279    pub enum PointerId {
280        /// ID from a mouse event.
281        Mouse(DeviceId),
282        /// ID from a contact in a touch event.
283        Contact(touch::ContactId),
284    }
285
286    /// Pointer event.
287    #[derive(Debug, PartialEq, Clone)]
288    pub struct Event {
289        /// Pointer event phase.
290        pub phase: Phase,
291        /// Pointer event pointer ID.
292        pub pointer_id: PointerId,
293    }
294
295    impl Event {
296        /// Create a pointer event from a mouse event.
297        pub fn new_from_mouse_event(
298            device_id: &DeviceId,
299            mouse_event: &mouse::Event,
300        ) -> Option<Self> {
301            match &mouse_event.phase {
302                mouse::Phase::Down(button) => {
303                    if button.is_primary() {
304                        Some(pointer::Phase::Down(mouse_event.location))
305                    } else {
306                        None
307                    }
308                }
309                mouse::Phase::Moved => {
310                    if mouse_event.buttons.primary_button_is_down() {
311                        Some(pointer::Phase::Moved(mouse_event.location))
312                    } else {
313                        None
314                    }
315                }
316                mouse::Phase::Up(button) => {
317                    if button.is_primary() {
318                        Some(pointer::Phase::Up)
319                    } else {
320                        None
321                    }
322                }
323                mouse::Phase::Wheel(_) => None,
324            }
325            .and_then(|phase| Some(Self { phase, pointer_id: PointerId::Mouse(device_id.clone()) }))
326        }
327
328        /// Create a pointer event from a single contact in a touch event.
329        pub fn new_from_contact(contact: &touch::Contact) -> Self {
330            let phase = match contact.phase {
331                touch::Phase::Down(location, ..) => pointer::Phase::Down(location),
332                touch::Phase::Moved(location, ..) => pointer::Phase::Moved(location),
333                touch::Phase::Up => pointer::Phase::Up,
334                touch::Phase::Remove => pointer::Phase::Remove,
335                touch::Phase::Cancel => pointer::Phase::Cancel,
336            };
337            Self { phase, pointer_id: PointerId::Contact(contact.contact_id) }
338        }
339    }
340}
341
342/// Events related to "consumer control" buttons, like volume controls.
343///
344/// These events are separated because they are different devices at the driver
345/// level, but it's not clear this is the right abstraction for Carnelian.
346pub mod consumer_control {
347    use super::*;
348
349    /// Phase of a consumer control event.
350    #[derive(Debug, PartialEq, Clone, Copy)]
351    pub enum Phase {
352        /// Button went down.
353        Down,
354        /// Button came up.
355        Up,
356    }
357
358    /// A consumer control event.
359    #[derive(Debug, PartialEq, Clone)]
360    pub struct Event {
361        /// Phase of event.
362        pub phase: Phase,
363        /// USB HID for key being pressed or released.
364        pub button: hid_input_report::ConsumerControlButton,
365    }
366}
367
368/// Unique identifier for an input device.
369#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, PartialOrd, Ord)]
370pub struct DeviceId(pub String);
371
372/// Enum of all supported user-input events.
373#[derive(Debug, PartialEq, Clone)]
374pub enum EventType {
375    /// Mouse event.
376    Mouse(mouse::Event),
377    /// Keyboard event.
378    Keyboard(keyboard::Event),
379    /// Touch event.
380    Touch(touch::Event),
381    /// Consumer control event.
382    ConsumerControl(consumer_control::Event),
383}
384
385/// Over user-input struct.
386#[derive(Debug, PartialEq, Clone)]
387pub struct Event {
388    /// Time of event.
389    pub event_time: u64,
390    /// Id of device producting this event.
391    pub device_id: DeviceId,
392    /// The event.
393    pub event_type: EventType,
394}
395
396async fn listen_to_path(device_path: &Path, internal_sender: &InternalSender) -> Result<(), Error> {
397    let (client, server) = zx::Channel::create();
398    fdio::service_connect(device_path.to_str().expect("bad path"), server)?;
399    let client = fasync::Channel::from_channel(client);
400    let device = hid_input_report::InputDeviceProxy::new(client);
401    let descriptor = device
402        .get_descriptor()
403        .map_err(|err| format_err!("FIDL error on get_descriptor: {:?}", err))
404        .on_timeout(MonotonicInstant::after(MonotonicDuration::from_millis(200)), || {
405            Err(format_err!("FIDL timeout on get_descriptor"))
406        })
407        .await?;
408    let device_id = device_path.file_name().expect("file_name").to_string_lossy().to_string();
409    internal_sender
410        .unbounded_send(MessageInternal::RegisterDevice(
411            DeviceId(device_id.clone()),
412            Box::new(descriptor),
413        ))
414        .expect("unbounded_send");
415    let input_report_sender = internal_sender.clone();
416    let (input_reports_reader_proxy, input_reports_reader_request) = create_proxy();
417    device.get_input_reports_reader(input_reports_reader_request)?;
418    fasync::Task::local(async move {
419        let _device = device;
420        loop {
421            let reports_res = input_reports_reader_proxy.read_input_reports().await;
422            match reports_res {
423                Ok(r) => match r {
424                    Ok(reports) => {
425                        for report in reports {
426                            input_report_sender
427                                .unbounded_send(MessageInternal::InputReport(
428                                    DeviceId(device_id.clone()),
429                                    report,
430                                ))
431                                .expect("unbounded_send");
432                        }
433                    }
434                    Err(err) => {
435                        eprintln!("Error report from read_input_reports: {}: {}", device_id, err);
436                        break;
437                    }
438                },
439                Err(err) => {
440                    eprintln!("Error report from read_input_reports: {}: {}", device_id, err);
441                    break;
442                }
443            }
444        }
445    })
446    .detach();
447    Ok(())
448}
449
450pub(crate) async fn listen_for_user_input(internal_sender: InternalSender) -> Result<(), Error> {
451    let input_devices_directory = "/dev/class/input-report";
452    let watcher_sender = internal_sender.clone();
453    let path = std::path::Path::new(input_devices_directory);
454    let entries = fs::read_dir(path)?;
455    for entry in entries {
456        let entry = entry?;
457        match listen_to_path(&entry.path(), &internal_sender).await {
458            Err(err) => {
459                eprintln!("Error: {}: {}", entry.file_name().to_string_lossy(), err)
460            }
461            _ => (),
462        }
463    }
464    let dir_proxy = fuchsia_fs::directory::open_in_namespace(
465        input_devices_directory,
466        fuchsia_fs::PERM_READABLE,
467    )?;
468    let mut watcher = fuchsia_fs::directory::Watcher::new(&dir_proxy).await?;
469    fasync::Task::local(async move {
470        let input_devices_directory_path = PathBuf::from("/dev/class/input-report");
471        while let Some(msg) = (watcher.try_next()).await.expect("msg") {
472            match msg.event {
473                fuchsia_fs::directory::WatchEvent::ADD_FILE => {
474                    let device_path = input_devices_directory_path.join(msg.filename);
475                    match listen_to_path(&device_path, &watcher_sender).await {
476                        Err(err) => {
477                            eprintln!("Error: {:?}: {}", device_path, err)
478                        }
479                        _ => (),
480                    };
481                }
482                _ => (),
483            }
484        }
485    })
486    .detach();
487
488    Ok(())
489}
490
491pub(crate) mod flatland;
492pub(crate) mod key3;
493pub(crate) mod report;
494
495#[cfg(test)]
496mod tests;