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!("uninterested input event: {:?}", input_event.get_event_type()),
86 );
87 vec![input_device::InputEvent::from(input_event)]
88 }
89 }
90 }
91}
92
93impl KeymapHandler {
94 pub fn new(
96 input_handlers_node: &fuchsia_inspect::Node,
97 metrics_logger: metrics::MetricsLogger,
98 ) -> Rc<Self> {
99 let inspect_status = InputHandlerStatus::new(
100 input_handlers_node,
101 "keymap_handler",
102 false,
103 );
104 Rc::new(Self {
105 modifier_state: Default::default(),
106 lock_state: Default::default(),
107 metrics_logger,
108 inspect_status,
109 })
110 }
111
112 fn process_keyboard_event(
114 self: &Rc<Self>,
115 event: keyboard_binding::KeyboardEvent,
116 device_descriptor: input_device::InputDeviceDescriptor,
117 event_time: zx::MonotonicInstant,
118 trace_id: Option<fuchsia_trace::Id>,
119 ) -> input_device::UnhandledInputEvent {
120 if let Some(trace_id) = trace_id {
121 fuchsia_trace::flow_step!("input", "event_in_input_pipeline", trace_id.into());
122 }
123
124 let (key, event_type) = (event.get_key(), event.get_event_type());
125 log::debug!(
126 concat!(
127 "Keymap::process_keyboard_event: key:{:?}, ",
128 "modifier_state:{:?}, lock_state: {:?}, event_type: {:?}"
129 ),
130 key,
131 self.modifier_state.borrow(),
132 self.lock_state.borrow(),
133 event_type
134 );
135
136 self.modifier_state.borrow_mut().update(event_type, key);
137 self.lock_state.borrow_mut().update(event_type, key);
138 let key_meaning = keymaps::select_keymap(&event.get_keymap()).apply(
139 key,
140 &*self.modifier_state.borrow(),
141 &*self.lock_state.borrow(),
142 );
143 input_device::UnhandledInputEvent {
144 device_event: input_device::InputDeviceEvent::Keyboard(
145 event.into_with_key_meaning(key_meaning),
146 ),
147 device_descriptor,
148 event_time,
149 trace_id,
150 }
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use crate::input_handler::InputHandler;
158 use crate::{consumer_controls_binding, testing_utilities};
159 use pretty_assertions::assert_eq;
160 use std::convert::TryFrom as _;
161 use {
162 fidl_fuchsia_input as finput, fidl_fuchsia_ui_input3 as finput3, fuchsia_async as fasync,
163 zx,
164 };
165
166 fn create_unhandled_keyboard_event(
168 key: finput::Key,
169 event_type: finput3::KeyEventType,
170 keymap: Option<String>,
171 ) -> input_device::UnhandledInputEvent {
172 let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
173 keyboard_binding::KeyboardDeviceDescriptor {
174 keys: vec![finput::Key::A, finput::Key::B],
175 ..Default::default()
176 },
177 );
178 let (_, event_time_u64) = testing_utilities::event_times();
179 input_device::UnhandledInputEvent::try_from(
180 testing_utilities::create_keyboard_event_with_time(
181 key,
182 event_type,
183 None,
184 event_time_u64,
185 &device_descriptor,
186 keymap,
187 ),
188 )
189 .unwrap()
190 }
191
192 fn create_unhandled_consumer_controls_event(
194 pressed_buttons: Vec<fidl_fuchsia_input_report::ConsumerControlButton>,
195 event_time: zx::MonotonicInstant,
196 device_descriptor: &input_device::InputDeviceDescriptor,
197 ) -> input_device::UnhandledInputEvent {
198 input_device::UnhandledInputEvent::try_from(
199 testing_utilities::create_consumer_controls_event(
200 pressed_buttons,
201 event_time,
202 device_descriptor,
203 ),
204 )
205 .unwrap()
206 }
207
208 fn get_key_meaning(event: &input_device::InputEvent) -> Option<finput3::KeyMeaning> {
209 match event {
210 input_device::InputEvent {
211 device_event: input_device::InputDeviceEvent::Keyboard(event),
212 ..
213 } => event.get_key_meaning(),
214 _ => None,
215 }
216 }
217
218 #[fasync::run_singlethreaded(test)]
219 async fn test_keymap_application() {
220 #[derive(Debug)]
223 struct TestCase {
224 events: Vec<input_device::UnhandledInputEvent>,
225 expected: Vec<Option<finput3::KeyMeaning>>,
226 }
227 let tests: Vec<TestCase> = vec![
228 TestCase {
229 events: vec![create_unhandled_keyboard_event(
230 finput::Key::A,
231 finput3::KeyEventType::Pressed,
232 Some("US_QWERTY".into()),
233 )],
234 expected: vec![
235 Some(finput3::KeyMeaning::Codepoint(97)), ],
237 },
238 TestCase {
239 events: vec![create_unhandled_consumer_controls_event(
241 vec![],
242 zx::MonotonicInstant::ZERO,
243 &input_device::InputDeviceDescriptor::ConsumerControls(
244 consumer_controls_binding::ConsumerControlsDeviceDescriptor {
245 buttons: vec![],
246 device_id: 0,
247 },
248 ),
249 )],
250 expected: vec![None],
251 },
252 TestCase {
253 events: vec![
254 create_unhandled_keyboard_event(
255 finput::Key::LeftShift,
256 finput3::KeyEventType::Pressed,
257 Some("US_QWERTY".into()),
258 ),
259 create_unhandled_keyboard_event(
260 finput::Key::A,
261 finput3::KeyEventType::Pressed,
262 Some("US_QWERTY".into()),
263 ),
264 ],
265 expected: vec![
266 Some(finput3::KeyMeaning::NonPrintableKey(finput3::NonPrintableKey::Shift)),
267 Some(finput3::KeyMeaning::Codepoint(65)), ],
269 },
270 TestCase {
271 events: vec![
272 create_unhandled_keyboard_event(
273 finput::Key::Tab,
274 finput3::KeyEventType::Pressed,
275 Some("US_QWERTY".into()),
276 ),
277 create_unhandled_keyboard_event(
278 finput::Key::A,
279 finput3::KeyEventType::Pressed,
280 Some("US_QWERTY".into()),
281 ),
282 ],
283 expected: vec![
284 Some(finput3::KeyMeaning::NonPrintableKey(finput3::NonPrintableKey::Tab)),
285 Some(finput3::KeyMeaning::Codepoint(97)), ],
287 },
288 ];
289 let inspector = fuchsia_inspect::Inspector::default();
290 let test_node = inspector.root().create_child("test_node");
291 for test in &tests {
292 let mut actual: Vec<Option<finput3::KeyMeaning>> = vec![];
293 let handler = KeymapHandler::new(&test_node, metrics::MetricsLogger::default());
294 for event in &test.events {
295 let mut result = handler
296 .clone()
297 .handle_unhandled_input_event(event.clone())
298 .await
299 .iter()
300 .map(get_key_meaning)
301 .collect();
302 actual.append(&mut result);
303 }
304 assert_eq!(test.expected, actual);
305 }
306 }
307
308 #[fuchsia::test]
309 async fn keymap_handler_initialized_with_inspect_node() {
310 let inspector = fuchsia_inspect::Inspector::default();
311 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
312 let _handler = KeymapHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
313 diagnostics_assertions::assert_data_tree!(inspector, root: {
314 input_handlers_node: {
315 keymap_handler: {
316 events_received_count: 0u64,
317 events_handled_count: 0u64,
318 last_received_timestamp_ns: 0u64,
319 "fuchsia.inspect.Health": {
320 status: "STARTING_UP",
321 start_timestamp_nanos: diagnostics_assertions::AnyProperty
324 },
325 }
326 }
327 });
328 }
329
330 #[fasync::run_singlethreaded(test)]
331 async fn keymap_handler_inspect_counts_events() {
332 let inspector = fuchsia_inspect::Inspector::default();
333 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
334 let keymap_handler =
335 KeymapHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
336 let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
337 keyboard_binding::KeyboardDeviceDescriptor {
338 keys: vec![finput::Key::A, finput::Key::B],
339 ..Default::default()
340 },
341 );
342 let (_, event_time_u64) = testing_utilities::event_times();
343 let input_events = vec![
344 testing_utilities::create_keyboard_event_with_time(
345 finput::Key::A,
346 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
347 None,
348 event_time_u64,
349 &device_descriptor,
350 None,
351 ),
352 testing_utilities::create_keyboard_event_with_handled(
354 finput::Key::B,
355 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
356 None,
357 event_time_u64,
358 &device_descriptor,
359 None,
360 None,
361 input_device::Handled::Yes,
362 ),
363 testing_utilities::create_keyboard_event_with_time(
364 finput::Key::A,
365 fidl_fuchsia_ui_input3::KeyEventType::Released,
366 None,
367 event_time_u64,
368 &device_descriptor,
369 None,
370 ),
371 testing_utilities::create_fake_input_event(event_time_u64),
373 testing_utilities::create_keyboard_event_with_time(
374 finput::Key::B,
375 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
376 None,
377 event_time_u64,
378 &device_descriptor,
379 None,
380 ),
381 ];
382
383 for input_event in input_events {
384 let _ = keymap_handler.clone().handle_input_event(input_event).await;
385 }
386
387 let last_event_timestamp: u64 = event_time_u64.into_nanos().try_into().unwrap();
388
389 diagnostics_assertions::assert_data_tree!(inspector, root: {
390 input_handlers_node: {
391 keymap_handler: {
392 events_received_count: 3u64,
393 events_handled_count: 0u64,
394 last_received_timestamp_ns: last_event_timestamp,
395 "fuchsia.inspect.Health": {
396 status: "STARTING_UP",
397 start_timestamp_nanos: diagnostics_assertions::AnyProperty
400 },
401 }
402 }
403 });
404 }
405}