input_pipeline/
modifier_handler.rs

1// Copyright 2021 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::input_device::{Handled, InputDeviceEvent, InputEvent, UnhandledInputEvent};
6use crate::input_handler::{InputHandlerStatus, UnhandledInputHandler};
7use async_trait::async_trait;
8use fidl_fuchsia_ui_input3::{KeyMeaning, Modifiers, NonPrintableKey};
9use fuchsia_inspect::health::Reporter;
10use keymaps::{LockStateKeys, ModifierState};
11use std::cell::RefCell;
12use std::rc::Rc;
13
14/// Tracks modifier state and decorates passing events with the modifiers.
15///
16/// This handler should be installed as early as possible in the input pipeline,
17/// to ensure that all later stages have the modifiers and lock states available.
18/// This holds even for non-keyboard handlers, to allow handling `Ctrl+Click`
19/// events, for example.
20///
21/// One possible exception to this rule would be a hardware key rewriting handler for
22/// limited keyboards.
23#[derive(Debug)]
24pub struct ModifierHandler {
25    /// The tracked state of the modifiers.
26    modifier_state: RefCell<ModifierState>,
27
28    /// The tracked lock state.
29    lock_state: RefCell<LockStateKeys>,
30
31    /// The inventory of this handler's Inspect status.
32    pub inspect_status: InputHandlerStatus,
33}
34
35#[async_trait(?Send)]
36impl UnhandledInputHandler for ModifierHandler {
37    async fn handle_unhandled_input_event(
38        self: Rc<Self>,
39        unhandled_input_event: UnhandledInputEvent,
40    ) -> Vec<InputEvent> {
41        match unhandled_input_event.clone() {
42            UnhandledInputEvent {
43                device_event: InputDeviceEvent::Keyboard(mut event),
44                device_descriptor,
45                event_time,
46                trace_id,
47            } => {
48                fuchsia_trace::duration!(c"input", c"modifier_handler");
49                if let Some(trace_id) = trace_id {
50                    fuchsia_trace::flow_step!(
51                        c"input",
52                        c"event_in_input_pipeline",
53                        trace_id.into()
54                    );
55                }
56
57                self.inspect_status.count_received_event(InputEvent::from(unhandled_input_event));
58                self.modifier_state.borrow_mut().update(event.get_event_type(), event.get_key());
59                self.lock_state.borrow_mut().update(event.get_event_type(), event.get_key());
60                event = event
61                    .into_with_lock_state(Some(self.lock_state.borrow().get_state()))
62                    .into_with_modifiers(Some(self.modifier_state.borrow().get_state()));
63                log::debug!("modifiers and lock state applied: {:?}", &event);
64                vec![InputEvent {
65                    device_event: InputDeviceEvent::Keyboard(event),
66                    device_descriptor,
67                    event_time,
68                    handled: Handled::No,
69                    trace_id,
70                }]
71            }
72            // Pass other events through.
73            _ => vec![InputEvent::from(unhandled_input_event)],
74        }
75    }
76
77    fn set_handler_healthy(self: std::rc::Rc<Self>) {
78        self.inspect_status.health_node.borrow_mut().set_ok();
79    }
80
81    fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
82        self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
83    }
84}
85
86impl ModifierHandler {
87    /// Creates a new [ModifierHandler].
88    pub fn new(input_handlers_node: &fuchsia_inspect::Node) -> Rc<Self> {
89        let inspect_status = InputHandlerStatus::new(
90            input_handlers_node,
91            "modifier_handler",
92            /* generates_events */ false,
93        );
94        Rc::new(Self {
95            modifier_state: RefCell::new(ModifierState::new()),
96            lock_state: RefCell::new(LockStateKeys::new()),
97            inspect_status,
98        })
99    }
100}
101
102/// Tracks the state of the modifiers that are tied to the key meaning (as opposed to hardware
103/// keys).
104#[derive(Debug)]
105pub struct ModifierMeaningHandler {
106    /// The tracked state of the modifiers.
107    modifier_state: RefCell<ModifierState>,
108
109    /// The inventory of this handler's Inspect status.
110    pub inspect_status: InputHandlerStatus,
111}
112
113impl ModifierMeaningHandler {
114    /// Creates a new [ModifierMeaningHandler].
115    pub fn new(input_handlers_node: &fuchsia_inspect::Node) -> Rc<Self> {
116        let inspect_status = InputHandlerStatus::new(
117            input_handlers_node,
118            "modifier_meaning_handler",
119            /* generates_events */ false,
120        );
121        Rc::new(Self { modifier_state: RefCell::new(ModifierState::new()), inspect_status })
122    }
123}
124
125#[async_trait(?Send)]
126impl UnhandledInputHandler for ModifierMeaningHandler {
127    async fn handle_unhandled_input_event(
128        self: Rc<Self>,
129        unhandled_input_event: UnhandledInputEvent,
130    ) -> Vec<InputEvent> {
131        match unhandled_input_event.clone() {
132            UnhandledInputEvent {
133                device_event: InputDeviceEvent::Keyboard(mut event),
134                device_descriptor,
135                event_time,
136                trace_id,
137            } if event.get_key_meaning()
138                == Some(KeyMeaning::NonPrintableKey(NonPrintableKey::AltGraph)) =>
139            {
140                self.inspect_status.count_received_event(InputEvent::from(unhandled_input_event));
141                // The "obvious" rewrite of this if and the match guard above is
142                // unstable, so doing it this way.
143                if let Some(key_meaning) = event.get_key_meaning() {
144                    self.modifier_state
145                        .borrow_mut()
146                        .update_with_key_meaning(event.get_event_type(), key_meaning);
147                    let new_modifier = event.get_modifiers().unwrap_or(Modifiers::empty())
148                        | self.modifier_state.borrow().get_state();
149                    event = event.into_with_modifiers(Some(new_modifier));
150                    log::debug!("additinal modifiers and lock state applied: {:?}", &event);
151                }
152                vec![InputEvent {
153                    device_event: InputDeviceEvent::Keyboard(event),
154                    device_descriptor,
155                    event_time,
156                    handled: Handled::No,
157                    trace_id,
158                }]
159            }
160            // Pass other events through.
161            _ => vec![InputEvent::from(unhandled_input_event)],
162        }
163    }
164
165    fn set_handler_healthy(self: std::rc::Rc<Self>) {
166        self.inspect_status.health_node.borrow_mut().set_ok();
167    }
168
169    fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
170        self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177    use crate::input_device::InputDeviceDescriptor;
178    use crate::input_handler::InputHandler;
179    use crate::keyboard_binding::{self, KeyboardEvent};
180    use crate::testing_utilities;
181    use fidl_fuchsia_input::Key;
182    use fidl_fuchsia_ui_input3::{KeyEventType, LockState};
183    use fuchsia_async as fasync;
184    use pretty_assertions::assert_eq;
185
186    fn get_unhandled_input_event(event: KeyboardEvent) -> UnhandledInputEvent {
187        UnhandledInputEvent {
188            device_event: InputDeviceEvent::Keyboard(event),
189            event_time: zx::MonotonicInstant::from_nanos(42),
190            device_descriptor: InputDeviceDescriptor::Fake,
191            trace_id: None,
192        }
193    }
194
195    #[fasync::run_singlethreaded(test)]
196    async fn test_decoration() {
197        let inspector = fuchsia_inspect::Inspector::default();
198        let test_node = inspector.root().create_child("test_node");
199        let handler = ModifierHandler::new(&test_node);
200        let input_event =
201            get_unhandled_input_event(KeyboardEvent::new(Key::CapsLock, KeyEventType::Pressed));
202        let result = handler.handle_unhandled_input_event(input_event.clone()).await;
203
204        // This handler decorates, but does not handle the key. Hence,
205        // the key remains unhandled.
206        let expected = InputEvent::from(get_unhandled_input_event(
207            KeyboardEvent::new(Key::CapsLock, KeyEventType::Pressed)
208                .into_with_modifiers(Some(Modifiers::CAPS_LOCK))
209                .into_with_lock_state(Some(LockState::CAPS_LOCK)),
210        ));
211        assert_eq!(vec![expected], result);
212    }
213
214    #[fasync::run_singlethreaded(test)]
215    async fn test_key_meaning_decoration() {
216        let inspector = fuchsia_inspect::Inspector::default();
217        let test_node = inspector.root().create_child("test_node");
218        let handler = ModifierMeaningHandler::new(&test_node);
219        {
220            let input_event = get_unhandled_input_event(
221                KeyboardEvent::new(Key::RightAlt, KeyEventType::Pressed)
222                    .into_with_key_meaning(Some(KeyMeaning::NonPrintableKey(
223                        NonPrintableKey::AltGraph,
224                    )))
225                    .into_with_modifiers(Some(Modifiers::CAPS_LOCK)),
226            );
227            let result = handler.clone().handle_unhandled_input_event(input_event.clone()).await;
228            let expected = InputEvent::from(get_unhandled_input_event(
229                KeyboardEvent::new(Key::RightAlt, KeyEventType::Pressed)
230                    .into_with_key_meaning(Some(KeyMeaning::NonPrintableKey(
231                        NonPrintableKey::AltGraph,
232                    )))
233                    .into_with_modifiers(Some(Modifiers::ALT_GRAPH | Modifiers::CAPS_LOCK)),
234            ));
235            assert_eq!(vec![expected], result);
236        }
237        {
238            let input_event = get_unhandled_input_event(
239                KeyboardEvent::new(Key::RightAlt, KeyEventType::Released)
240                    .into_with_key_meaning(Some(KeyMeaning::NonPrintableKey(
241                        NonPrintableKey::AltGraph,
242                    )))
243                    .into_with_modifiers(Some(Modifiers::CAPS_LOCK)),
244            );
245            let handler = handler.clone();
246            let result = handler.handle_unhandled_input_event(input_event.clone()).await;
247            let expected = InputEvent::from(get_unhandled_input_event(
248                KeyboardEvent::new(Key::RightAlt, KeyEventType::Released)
249                    .into_with_key_meaning(Some(KeyMeaning::NonPrintableKey(
250                        NonPrintableKey::AltGraph,
251                    )))
252                    .into_with_modifiers(Some(Modifiers::CAPS_LOCK)),
253            ));
254            assert_eq!(vec![expected], result);
255        }
256    }
257
258    // CapsLock  """"""\______/""""""""""\_______/"""
259    // Modifiers ------CCCCCCCC----------CCCCCCCCC---
260    // LockState ------CCCCCCCCCCCCCCCCCCCCCCCCCCC---
261    //
262    // C == CapsLock
263    #[fasync::run_singlethreaded(test)]
264    async fn test_modifier_press_lock_release() {
265        let input_events = vec![
266            get_unhandled_input_event(KeyboardEvent::new(Key::CapsLock, KeyEventType::Pressed)),
267            get_unhandled_input_event(KeyboardEvent::new(Key::CapsLock, KeyEventType::Released)),
268            get_unhandled_input_event(KeyboardEvent::new(Key::CapsLock, KeyEventType::Pressed)),
269            get_unhandled_input_event(KeyboardEvent::new(Key::CapsLock, KeyEventType::Released)),
270        ];
271
272        let inspector = fuchsia_inspect::Inspector::default();
273        let test_node = inspector.root().create_child("test_node");
274        let handler = ModifierHandler::new(&test_node);
275        let clone_handler = move || handler.clone();
276        let result = futures::future::join_all(
277            input_events
278                .into_iter()
279                .map(|e| async { clone_handler().handle_unhandled_input_event(e).await }),
280        )
281        .await
282        .into_iter()
283        .flatten()
284        .collect::<Vec<InputEvent>>();
285
286        let expected = IntoIterator::into_iter([
287            get_unhandled_input_event(
288                KeyboardEvent::new(Key::CapsLock, KeyEventType::Pressed)
289                    .into_with_modifiers(Some(Modifiers::CAPS_LOCK))
290                    .into_with_lock_state(Some(LockState::CAPS_LOCK)),
291            ),
292            get_unhandled_input_event(
293                KeyboardEvent::new(Key::CapsLock, KeyEventType::Released)
294                    .into_with_modifiers(Some(Modifiers::from_bits_allow_unknown(0)))
295                    .into_with_lock_state(Some(LockState::CAPS_LOCK)),
296            ),
297            get_unhandled_input_event(
298                KeyboardEvent::new(Key::CapsLock, KeyEventType::Pressed)
299                    .into_with_modifiers(Some(Modifiers::CAPS_LOCK))
300                    .into_with_lock_state(Some(LockState::CAPS_LOCK)),
301            ),
302            get_unhandled_input_event(
303                KeyboardEvent::new(Key::CapsLock, KeyEventType::Released)
304                    .into_with_modifiers(Some(Modifiers::from_bits_allow_unknown(0)))
305                    .into_with_lock_state(Some(LockState::from_bits_allow_unknown(0))),
306            ),
307        ])
308        .map(InputEvent::from)
309        .collect::<Vec<_>>();
310
311        assert_eq!(expected, result);
312    }
313
314    // CapsLock  """"""\______/"""""""""""""""""""
315    // A         """""""""""""""""""\________/""""
316    // Modifiers ------CCCCCCCC-------------------
317    // LockState ------CCCCCCCCCCCCCCCCCCCCCCCCCCC
318    //
319    // C == CapsLock
320    #[fasync::run_singlethreaded(test)]
321    async fn repeated_modifier_key() {
322        let input_events = vec![
323            get_unhandled_input_event(KeyboardEvent::new(Key::CapsLock, KeyEventType::Pressed)),
324            get_unhandled_input_event(KeyboardEvent::new(Key::CapsLock, KeyEventType::Released)),
325            get_unhandled_input_event(KeyboardEvent::new(Key::A, KeyEventType::Pressed)),
326            get_unhandled_input_event(KeyboardEvent::new(Key::A, KeyEventType::Released)),
327        ];
328
329        let inspector = fuchsia_inspect::Inspector::default();
330        let test_node = inspector.root().create_child("test_node");
331        let handler = ModifierHandler::new(&test_node);
332        let clone_handler = move || handler.clone();
333        let result = futures::future::join_all(
334            input_events
335                .into_iter()
336                .map(|e| async { clone_handler().handle_unhandled_input_event(e).await }),
337        )
338        .await
339        .into_iter()
340        .flatten()
341        .collect::<Vec<InputEvent>>();
342
343        let expected = IntoIterator::into_iter([
344            get_unhandled_input_event(
345                KeyboardEvent::new(Key::CapsLock, KeyEventType::Pressed)
346                    .into_with_modifiers(Some(Modifiers::CAPS_LOCK))
347                    .into_with_lock_state(Some(LockState::CAPS_LOCK)),
348            ),
349            get_unhandled_input_event(
350                KeyboardEvent::new(Key::CapsLock, KeyEventType::Released)
351                    .into_with_modifiers(Some(Modifiers::from_bits_allow_unknown(0)))
352                    .into_with_lock_state(Some(LockState::CAPS_LOCK)),
353            ),
354            get_unhandled_input_event(
355                KeyboardEvent::new(Key::A, KeyEventType::Pressed)
356                    .into_with_modifiers(Some(Modifiers::from_bits_allow_unknown(0)))
357                    .into_with_lock_state(Some(LockState::CAPS_LOCK)),
358            ),
359            get_unhandled_input_event(
360                KeyboardEvent::new(Key::A, KeyEventType::Released)
361                    .into_with_modifiers(Some(Modifiers::from_bits_allow_unknown(0)))
362                    .into_with_lock_state(Some(LockState::CAPS_LOCK)),
363            ),
364        ])
365        .map(InputEvent::from)
366        .collect::<Vec<_>>();
367        assert_eq!(expected, result);
368    }
369
370    #[fuchsia::test]
371    fn modifier_handlers_initialized_with_inspect_node() {
372        let inspector = fuchsia_inspect::Inspector::default();
373        let fake_handlers_node = inspector.root().create_child("input_handlers_node");
374        let _modifier_handler = ModifierHandler::new(&fake_handlers_node);
375        let _modifier_meaning_handler = ModifierMeaningHandler::new(&fake_handlers_node);
376        diagnostics_assertions::assert_data_tree!(inspector, root: {
377            input_handlers_node: {
378                modifier_handler: {
379                    events_received_count: 0u64,
380                    events_handled_count: 0u64,
381                    last_received_timestamp_ns: 0u64,
382                    "fuchsia.inspect.Health": {
383                        status: "STARTING_UP",
384                        // Timestamp value is unpredictable and not relevant in this context,
385                        // so we only assert that the property is present.
386                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
387                    },
388                },
389                modifier_meaning_handler: {
390                    events_received_count: 0u64,
391                    events_handled_count: 0u64,
392                    last_received_timestamp_ns: 0u64,
393                    "fuchsia.inspect.Health": {
394                        status: "STARTING_UP",
395                        // Timestamp value is unpredictable and not relevant in this context,
396                        // so we only assert that the property is present.
397                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
398                    },
399                }
400            }
401        });
402    }
403
404    #[fasync::run_singlethreaded(test)]
405    async fn modifier_handler_inspect_counts_events() {
406        let inspector = fuchsia_inspect::Inspector::default();
407        let fake_handlers_node = inspector.root().create_child("input_handlers_node");
408        let modifier_handler = ModifierHandler::new(&fake_handlers_node);
409        let modifier_meaning_handler = ModifierMeaningHandler::new(&fake_handlers_node);
410        let device_descriptor =
411            InputDeviceDescriptor::Keyboard(keyboard_binding::KeyboardDeviceDescriptor {
412                keys: vec![Key::A, Key::B, Key::RightAlt],
413                ..Default::default()
414            });
415        let (_, event_time_u64) = testing_utilities::event_times();
416        let input_events = vec![
417            testing_utilities::create_keyboard_event_with_time(
418                Key::A,
419                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
420                None,
421                event_time_u64,
422                &device_descriptor,
423                /* keymap= */ None,
424            ),
425            // Should not count received events that have already been handled.
426            testing_utilities::create_keyboard_event_with_handled(
427                Key::B,
428                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
429                None,
430                event_time_u64,
431                &device_descriptor,
432                /* keymap= */ None,
433                /* key_meaning= */ None,
434                Handled::Yes,
435            ),
436            testing_utilities::create_keyboard_event_with_time(
437                Key::A,
438                fidl_fuchsia_ui_input3::KeyEventType::Released,
439                None,
440                event_time_u64,
441                &device_descriptor,
442                /* keymap= */ None,
443            ),
444            // Should not count non-keyboard input events.
445            testing_utilities::create_fake_input_event(event_time_u64),
446            // Only event that should be counted by ModifierMeaningHandler.
447            testing_utilities::create_keyboard_event_with_key_meaning(
448                Key::RightAlt,
449                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
450                None,
451                event_time_u64,
452                &device_descriptor,
453                /* keymap= */ None,
454                /* key_meaning= */
455                Some(KeyMeaning::NonPrintableKey(NonPrintableKey::AltGraph)),
456            ),
457        ];
458
459        for input_event in input_events {
460            let _ = modifier_handler.clone().handle_input_event(input_event.clone()).await;
461            let _ = modifier_meaning_handler.clone().handle_input_event(input_event).await;
462        }
463
464        let last_event_timestamp: u64 = event_time_u64.into_nanos().try_into().unwrap();
465
466        diagnostics_assertions::assert_data_tree!(inspector, root: {
467            input_handlers_node: {
468                modifier_handler: {
469                    events_received_count: 3u64,
470                    events_handled_count: 0u64,
471                    last_received_timestamp_ns: last_event_timestamp,
472                    "fuchsia.inspect.Health": {
473                        status: "STARTING_UP",
474                        // Timestamp value is unpredictable and not relevant in this context,
475                        // so we only assert that the property is present.
476                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
477                    },
478                },
479                modifier_meaning_handler: {
480                    events_received_count: 1u64,
481                    events_handled_count: 0u64,
482                    last_received_timestamp_ns: last_event_timestamp,
483                    "fuchsia.inspect.Health": {
484                        status: "STARTING_UP",
485                        // Timestamp value is unpredictable and not relevant in this context,
486                        // so we only assert that the property is present.
487                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
488                    },
489                }
490            }
491        });
492    }
493}