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 = keymaps::select_keymap(&event.get_keymap()).apply(
143 key,
144 &*self.modifier_state.borrow(),
145 &*self.lock_state.borrow(),
146 );
147 input_device::UnhandledInputEvent {
148 device_event: input_device::InputDeviceEvent::Keyboard(
149 event.into_with_key_meaning(key_meaning),
150 ),
151 device_descriptor,
152 event_time,
153 trace_id,
154 }
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use crate::input_handler::InputHandler;
162 use crate::{consumer_controls_binding, testing_utilities};
163 use fidl_fuchsia_input as finput;
164 use fidl_fuchsia_ui_input3 as finput3;
165 use fuchsia_async as fasync;
166 use pretty_assertions::assert_eq;
167 use std::convert::TryFrom as _;
168 use zx;
169
170 fn create_unhandled_keyboard_event(
172 key: finput::Key,
173 event_type: finput3::KeyEventType,
174 keymap: Option<String>,
175 ) -> input_device::UnhandledInputEvent {
176 let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
177 keyboard_binding::KeyboardDeviceDescriptor {
178 keys: vec![finput::Key::A, finput::Key::B],
179 ..Default::default()
180 },
181 );
182 let (_, event_time_u64) = testing_utilities::event_times();
183 input_device::UnhandledInputEvent::try_from(
184 testing_utilities::create_keyboard_event_with_time(
185 key,
186 event_type,
187 None,
188 event_time_u64,
189 &device_descriptor,
190 keymap,
191 ),
192 )
193 .unwrap()
194 }
195
196 fn create_unhandled_consumer_controls_event(
198 pressed_buttons: Vec<fidl_fuchsia_input_report::ConsumerControlButton>,
199 event_time: zx::MonotonicInstant,
200 device_descriptor: &input_device::InputDeviceDescriptor,
201 ) -> input_device::UnhandledInputEvent {
202 input_device::UnhandledInputEvent::try_from(
203 testing_utilities::create_consumer_controls_event(
204 pressed_buttons,
205 event_time,
206 device_descriptor,
207 ),
208 )
209 .unwrap()
210 }
211
212 fn get_key_meaning(event: &input_device::InputEvent) -> Option<finput3::KeyMeaning> {
213 match event {
214 input_device::InputEvent {
215 device_event: input_device::InputDeviceEvent::Keyboard(event),
216 ..
217 } => event.get_key_meaning(),
218 _ => None,
219 }
220 }
221
222 #[fasync::run_singlethreaded(test)]
223 async fn test_keymap_application() {
224 #[derive(Debug)]
227 struct TestCase {
228 events: Vec<input_device::UnhandledInputEvent>,
229 expected: Vec<Option<finput3::KeyMeaning>>,
230 }
231 let tests: Vec<TestCase> = vec![
232 TestCase {
233 events: vec![create_unhandled_keyboard_event(
234 finput::Key::A,
235 finput3::KeyEventType::Pressed,
236 Some("US_QWERTY".into()),
237 )],
238 expected: vec![
239 Some(finput3::KeyMeaning::Codepoint(97)), ],
241 },
242 TestCase {
243 events: vec![create_unhandled_consumer_controls_event(
245 vec![],
246 zx::MonotonicInstant::ZERO,
247 &input_device::InputDeviceDescriptor::ConsumerControls(
248 consumer_controls_binding::ConsumerControlsDeviceDescriptor {
249 buttons: vec![],
250 device_id: 0,
251 },
252 ),
253 )],
254 expected: vec![None],
255 },
256 TestCase {
257 events: vec![
258 create_unhandled_keyboard_event(
259 finput::Key::LeftShift,
260 finput3::KeyEventType::Pressed,
261 Some("US_QWERTY".into()),
262 ),
263 create_unhandled_keyboard_event(
264 finput::Key::A,
265 finput3::KeyEventType::Pressed,
266 Some("US_QWERTY".into()),
267 ),
268 ],
269 expected: vec![
270 Some(finput3::KeyMeaning::NonPrintableKey(finput3::NonPrintableKey::Shift)),
271 Some(finput3::KeyMeaning::Codepoint(65)), ],
273 },
274 TestCase {
275 events: vec![
276 create_unhandled_keyboard_event(
277 finput::Key::Tab,
278 finput3::KeyEventType::Pressed,
279 Some("US_QWERTY".into()),
280 ),
281 create_unhandled_keyboard_event(
282 finput::Key::A,
283 finput3::KeyEventType::Pressed,
284 Some("US_QWERTY".into()),
285 ),
286 ],
287 expected: vec![
288 Some(finput3::KeyMeaning::NonPrintableKey(finput3::NonPrintableKey::Tab)),
289 Some(finput3::KeyMeaning::Codepoint(97)), ],
291 },
292 ];
293 let inspector = fuchsia_inspect::Inspector::default();
294 let test_node = inspector.root().create_child("test_node");
295 for test in &tests {
296 let mut actual: Vec<Option<finput3::KeyMeaning>> = vec![];
297 let handler = KeymapHandler::new(&test_node, metrics::MetricsLogger::default());
298 for event in &test.events {
299 let mut result = handler
300 .clone()
301 .handle_unhandled_input_event(event.clone())
302 .await
303 .iter()
304 .map(get_key_meaning)
305 .collect();
306 actual.append(&mut result);
307 }
308 assert_eq!(test.expected, actual);
309 }
310 }
311
312 #[fuchsia::test]
313 async fn keymap_handler_initialized_with_inspect_node() {
314 let inspector = fuchsia_inspect::Inspector::default();
315 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
316 let _handler = KeymapHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
317 diagnostics_assertions::assert_data_tree!(inspector, root: {
318 input_handlers_node: {
319 keymap_handler: {
320 events_received_count: 0u64,
321 events_handled_count: 0u64,
322 last_received_timestamp_ns: 0u64,
323 "fuchsia.inspect.Health": {
324 status: "STARTING_UP",
325 start_timestamp_nanos: diagnostics_assertions::AnyProperty
328 },
329 }
330 }
331 });
332 }
333
334 #[fasync::run_singlethreaded(test)]
335 async fn keymap_handler_inspect_counts_events() {
336 let inspector = fuchsia_inspect::Inspector::default();
337 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
338 let keymap_handler =
339 KeymapHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
340 let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
341 keyboard_binding::KeyboardDeviceDescriptor {
342 keys: vec![finput::Key::A, finput::Key::B],
343 ..Default::default()
344 },
345 );
346 let (_, event_time_u64) = testing_utilities::event_times();
347 let input_events = vec![
348 testing_utilities::create_keyboard_event_with_time(
349 finput::Key::A,
350 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
351 None,
352 event_time_u64,
353 &device_descriptor,
354 None,
355 ),
356 testing_utilities::create_keyboard_event_with_handled(
358 finput::Key::B,
359 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
360 None,
361 event_time_u64,
362 &device_descriptor,
363 None,
364 None,
365 input_device::Handled::Yes,
366 ),
367 testing_utilities::create_keyboard_event_with_time(
368 finput::Key::A,
369 fidl_fuchsia_ui_input3::KeyEventType::Released,
370 None,
371 event_time_u64,
372 &device_descriptor,
373 None,
374 ),
375 testing_utilities::create_fake_input_event(event_time_u64),
377 testing_utilities::create_keyboard_event_with_time(
378 finput::Key::B,
379 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
380 None,
381 event_time_u64,
382 &device_descriptor,
383 None,
384 ),
385 ];
386
387 for input_event in input_events {
388 let _ = keymap_handler.clone().handle_input_event(input_event).await;
389 }
390
391 let last_event_timestamp: u64 = event_time_u64.into_nanos().try_into().unwrap();
392
393 diagnostics_assertions::assert_data_tree!(inspector, root: {
394 input_handlers_node: {
395 keymap_handler: {
396 events_received_count: 3u64,
397 events_handled_count: 0u64,
398 last_received_timestamp_ns: last_event_timestamp,
399 "fuchsia.inspect.Health": {
400 status: "STARTING_UP",
401 start_timestamp_nanos: diagnostics_assertions::AnyProperty
404 },
405 }
406 }
407 });
408 }
409}