1use crate::input_handler::{Handler, InputHandlerStatus, UnhandledInputHandler};
10use crate::{input_device, keyboard_binding, metrics};
11use async_trait::async_trait;
12use fuchsia_inspect::health::Reporter;
13use metrics_registry::InputPipelineErrorMetricDimensionEvent;
14use std::cell::RefCell;
15use std::rc::Rc;
16
17#[derive(Debug)]
23pub struct KeymapHandler {
24 modifier_state: RefCell<keymaps::ModifierState>,
26
27 lock_state: RefCell<keymaps::LockStateKeys>,
29
30 metrics_logger: metrics::MetricsLogger,
32
33 pub inspect_status: InputHandlerStatus,
35}
36
37impl Handler for KeymapHandler {
38 fn set_handler_healthy(self: std::rc::Rc<Self>) {
39 self.inspect_status.health_node.borrow_mut().set_ok();
40 }
41
42 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
43 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
44 }
45
46 fn get_name(&self) -> &'static str {
47 "KeymapHandler"
48 }
49
50 fn interest(&self) -> Vec<input_device::InputEventType> {
51 vec![input_device::InputEventType::Keyboard]
52 }
53}
54
55#[async_trait(?Send)]
58impl UnhandledInputHandler for KeymapHandler {
59 async fn handle_unhandled_input_event(
60 self: Rc<Self>,
61 input_event: input_device::UnhandledInputEvent,
62 ) -> Vec<input_device::InputEvent> {
63 fuchsia_trace::duration!("input", "keymap_handler");
64 match input_event {
65 input_device::UnhandledInputEvent {
67 device_event: input_device::InputDeviceEvent::Keyboard(event),
68 device_descriptor,
69 event_time,
70 trace_id,
71 } => {
72 fuchsia_trace::duration!("input", "keymap_handler[processing]");
73 self.inspect_status.count_received_event(&event_time);
74 vec![input_device::InputEvent::from(self.process_keyboard_event(
75 event,
76 device_descriptor,
77 event_time,
78 trace_id,
79 ))]
80 }
81 _ => {
83 self.metrics_logger.log_error(
84 InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
85 std::format!(
86 "{} uninterested input event: {:?}",
87 self.get_name(),
88 input_event.get_event_type()
89 ),
90 );
91 vec![input_device::InputEvent::from(input_event)]
92 }
93 }
94 }
95}
96
97impl KeymapHandler {
98 pub fn new(
100 input_handlers_node: &fuchsia_inspect::Node,
101 metrics_logger: metrics::MetricsLogger,
102 ) -> Rc<Self> {
103 let inspect_status = InputHandlerStatus::new(
104 input_handlers_node,
105 "keymap_handler",
106 false,
107 );
108 Rc::new(Self {
109 modifier_state: Default::default(),
110 lock_state: Default::default(),
111 metrics_logger,
112 inspect_status,
113 })
114 }
115
116 fn process_keyboard_event(
118 self: &Rc<Self>,
119 event: keyboard_binding::KeyboardEvent,
120 device_descriptor: input_device::InputDeviceDescriptor,
121 event_time: zx::MonotonicInstant,
122 trace_id: Option<fuchsia_trace::Id>,
123 ) -> input_device::UnhandledInputEvent {
124 if let Some(trace_id) = trace_id {
125 fuchsia_trace::flow_step!("input", "event_in_input_pipeline", trace_id.into());
126 }
127
128 let (key, event_type) = (event.get_key(), event.get_event_type());
129 log::debug!(
130 concat!(
131 "Keymap::process_keyboard_event: key:{:?}, ",
132 "modifier_state:{:?}, lock_state: {:?}, event_type: {:?}"
133 ),
134 key,
135 self.modifier_state.borrow(),
136 self.lock_state.borrow(),
137 event_type
138 );
139
140 self.modifier_state.borrow_mut().update(event_type, key);
141 self.lock_state.borrow_mut().update(event_type, key);
142 let key_meaning =
143 keymaps::apply(key, &*self.modifier_state.borrow(), &*self.lock_state.borrow());
144 input_device::UnhandledInputEvent {
145 device_event: input_device::InputDeviceEvent::Keyboard(
146 event.into_with_key_meaning(key_meaning),
147 ),
148 device_descriptor,
149 event_time,
150 trace_id,
151 }
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use crate::input_handler::InputHandler;
159 use crate::{consumer_controls_binding, testing_utilities};
160 use fidl_fuchsia_input as finput;
161 use fidl_fuchsia_ui_input3 as finput3;
162 use fuchsia_async as fasync;
163 use pretty_assertions::assert_eq;
164 use std::convert::TryFrom as _;
165 use zx;
166
167 fn create_unhandled_keyboard_event(
169 key: finput::Key,
170 event_type: finput3::KeyEventType,
171 keymap: Option<String>,
172 ) -> input_device::UnhandledInputEvent {
173 let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
174 keyboard_binding::KeyboardDeviceDescriptor {
175 keys: vec![finput::Key::A, finput::Key::B],
176 ..Default::default()
177 },
178 );
179 let (_, event_time_u64) = testing_utilities::event_times();
180 input_device::UnhandledInputEvent::try_from(
181 testing_utilities::create_keyboard_event_with_time(
182 key,
183 event_type,
184 None,
185 event_time_u64,
186 &device_descriptor,
187 keymap,
188 ),
189 )
190 .unwrap()
191 }
192
193 fn create_unhandled_consumer_controls_event(
195 pressed_buttons: Vec<fidl_fuchsia_input_report::ConsumerControlButton>,
196 event_time: zx::MonotonicInstant,
197 device_descriptor: &input_device::InputDeviceDescriptor,
198 ) -> input_device::UnhandledInputEvent {
199 input_device::UnhandledInputEvent::try_from(
200 testing_utilities::create_consumer_controls_event(
201 pressed_buttons,
202 event_time,
203 device_descriptor,
204 ),
205 )
206 .unwrap()
207 }
208
209 fn get_key_meaning(event: &input_device::InputEvent) -> Option<finput3::KeyMeaning> {
210 match event {
211 input_device::InputEvent {
212 device_event: input_device::InputDeviceEvent::Keyboard(event),
213 ..
214 } => event.get_key_meaning(),
215 _ => None,
216 }
217 }
218
219 #[fasync::run_singlethreaded(test)]
220 async fn test_keymap_application() {
221 #[derive(Debug)]
224 struct TestCase {
225 events: Vec<input_device::UnhandledInputEvent>,
226 expected: Vec<Option<finput3::KeyMeaning>>,
227 }
228 let tests: Vec<TestCase> = vec![
229 TestCase {
230 events: vec![create_unhandled_keyboard_event(
231 finput::Key::A,
232 finput3::KeyEventType::Pressed,
233 Some("US_QWERTY".into()),
234 )],
235 expected: vec![
236 Some(finput3::KeyMeaning::Codepoint(97)), ],
238 },
239 TestCase {
240 events: vec![create_unhandled_consumer_controls_event(
242 vec![],
243 zx::MonotonicInstant::ZERO,
244 &input_device::InputDeviceDescriptor::ConsumerControls(
245 consumer_controls_binding::ConsumerControlsDeviceDescriptor {
246 buttons: vec![],
247 device_id: 0,
248 },
249 ),
250 )],
251 expected: vec![None],
252 },
253 TestCase {
254 events: vec![
255 create_unhandled_keyboard_event(
256 finput::Key::LeftShift,
257 finput3::KeyEventType::Pressed,
258 Some("US_QWERTY".into()),
259 ),
260 create_unhandled_keyboard_event(
261 finput::Key::A,
262 finput3::KeyEventType::Pressed,
263 Some("US_QWERTY".into()),
264 ),
265 ],
266 expected: vec![
267 Some(finput3::KeyMeaning::NonPrintableKey(finput3::NonPrintableKey::Shift)),
268 Some(finput3::KeyMeaning::Codepoint(65)), ],
270 },
271 TestCase {
272 events: vec![
273 create_unhandled_keyboard_event(
274 finput::Key::Tab,
275 finput3::KeyEventType::Pressed,
276 Some("US_QWERTY".into()),
277 ),
278 create_unhandled_keyboard_event(
279 finput::Key::A,
280 finput3::KeyEventType::Pressed,
281 Some("US_QWERTY".into()),
282 ),
283 ],
284 expected: vec![
285 Some(finput3::KeyMeaning::NonPrintableKey(finput3::NonPrintableKey::Tab)),
286 Some(finput3::KeyMeaning::Codepoint(97)), ],
288 },
289 ];
290 let inspector = fuchsia_inspect::Inspector::default();
291 let test_node = inspector.root().create_child("test_node");
292 for test in &tests {
293 let mut actual: Vec<Option<finput3::KeyMeaning>> = vec![];
294 let handler = KeymapHandler::new(&test_node, metrics::MetricsLogger::default());
295 for event in &test.events {
296 let mut result = handler
297 .clone()
298 .handle_unhandled_input_event(event.clone())
299 .await
300 .iter()
301 .map(get_key_meaning)
302 .collect();
303 actual.append(&mut result);
304 }
305 assert_eq!(test.expected, actual);
306 }
307 }
308
309 #[fuchsia::test]
310 async fn keymap_handler_initialized_with_inspect_node() {
311 let inspector = fuchsia_inspect::Inspector::default();
312 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
313 let _handler = KeymapHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
314 diagnostics_assertions::assert_data_tree!(inspector, root: {
315 input_handlers_node: {
316 keymap_handler: {
317 events_received_count: 0u64,
318 events_handled_count: 0u64,
319 last_received_timestamp_ns: 0u64,
320 "fuchsia.inspect.Health": {
321 status: "STARTING_UP",
322 start_timestamp_nanos: diagnostics_assertions::AnyProperty
325 },
326 }
327 }
328 });
329 }
330
331 #[fasync::run_singlethreaded(test)]
332 async fn keymap_handler_inspect_counts_events() {
333 let inspector = fuchsia_inspect::Inspector::default();
334 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
335 let keymap_handler =
336 KeymapHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
337 let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
338 keyboard_binding::KeyboardDeviceDescriptor {
339 keys: vec![finput::Key::A, finput::Key::B],
340 ..Default::default()
341 },
342 );
343 let (_, event_time_u64) = testing_utilities::event_times();
344 let input_events = vec![
345 testing_utilities::create_keyboard_event_with_time(
346 finput::Key::A,
347 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
348 None,
349 event_time_u64,
350 &device_descriptor,
351 None,
352 ),
353 testing_utilities::create_keyboard_event_with_handled(
355 finput::Key::B,
356 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
357 None,
358 event_time_u64,
359 &device_descriptor,
360 None,
361 None,
362 input_device::Handled::Yes,
363 ),
364 testing_utilities::create_keyboard_event_with_time(
365 finput::Key::A,
366 fidl_fuchsia_ui_input3::KeyEventType::Released,
367 None,
368 event_time_u64,
369 &device_descriptor,
370 None,
371 ),
372 testing_utilities::create_fake_input_event(event_time_u64),
374 testing_utilities::create_keyboard_event_with_time(
375 finput::Key::B,
376 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
377 None,
378 event_time_u64,
379 &device_descriptor,
380 None,
381 ),
382 ];
383
384 for input_event in input_events {
385 let _ = keymap_handler.clone().handle_input_event(input_event).await;
386 }
387
388 let last_event_timestamp: u64 = event_time_u64.into_nanos().try_into().unwrap();
389
390 diagnostics_assertions::assert_data_tree!(inspector, root: {
391 input_handlers_node: {
392 keymap_handler: {
393 events_received_count: 3u64,
394 events_handled_count: 0u64,
395 last_received_timestamp_ns: last_event_timestamp,
396 "fuchsia.inspect.Health": {
397 status: "STARTING_UP",
398 start_timestamp_nanos: diagnostics_assertions::AnyProperty
401 },
402 }
403 }
404 });
405 }
406}