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::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![fidl_fuchsia_input::ConsumerControlButton::VolumeUp],
247 device_id: 1,
248 is_injected: false,
249 },
250 ),
251 )],
252 expected: vec![None],
253 },
254 TestCase {
255 events: vec![
256 create_unhandled_keyboard_event(
257 finput::Key::LeftShift,
258 finput3::KeyEventType::Pressed,
259 Some("US_QWERTY".into()),
260 ),
261 create_unhandled_keyboard_event(
262 finput::Key::A,
263 finput3::KeyEventType::Pressed,
264 Some("US_QWERTY".into()),
265 ),
266 ],
267 expected: vec![
268 Some(finput3::KeyMeaning::NonPrintableKey(finput3::NonPrintableKey::Shift)),
269 Some(finput3::KeyMeaning::Codepoint(65)), ],
271 },
272 TestCase {
273 events: vec![
274 create_unhandled_keyboard_event(
275 finput::Key::Tab,
276 finput3::KeyEventType::Pressed,
277 Some("US_QWERTY".into()),
278 ),
279 create_unhandled_keyboard_event(
280 finput::Key::A,
281 finput3::KeyEventType::Pressed,
282 Some("US_QWERTY".into()),
283 ),
284 ],
285 expected: vec![
286 Some(finput3::KeyMeaning::NonPrintableKey(finput3::NonPrintableKey::Tab)),
287 Some(finput3::KeyMeaning::Codepoint(97)), ],
289 },
290 ];
291 let inspector = fuchsia_inspect::Inspector::default();
292 let test_node = inspector.root().create_child("test_node");
293 for test in &tests {
294 let mut actual: Vec<Option<finput3::KeyMeaning>> = vec![];
295 let handler = KeymapHandler::new(&test_node, metrics::MetricsLogger::default());
296 for event in &test.events {
297 let mut result = handler
298 .clone()
299 .handle_unhandled_input_event(event.clone())
300 .await
301 .iter()
302 .map(get_key_meaning)
303 .collect();
304 actual.append(&mut result);
305 }
306 assert_eq!(test.expected, actual);
307 }
308 }
309
310 #[fuchsia::test]
311 async fn keymap_handler_initialized_with_inspect_node() {
312 let inspector = fuchsia_inspect::Inspector::default();
313 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
314 let _handler = KeymapHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
315 diagnostics_assertions::assert_data_tree!(inspector, root: {
316 input_handlers_node: {
317 keymap_handler: {
318 events_received_count: 0u64,
319 events_handled_count: 0u64,
320 last_received_timestamp_ns: 0u64,
321 "fuchsia.inspect.Health": {
322 status: "STARTING_UP",
323 start_timestamp_nanos: diagnostics_assertions::AnyProperty
326 },
327 }
328 }
329 });
330 }
331
332 #[fasync::run_singlethreaded(test)]
333 async fn keymap_handler_inspect_counts_events() {
334 let inspector = fuchsia_inspect::Inspector::default();
335 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
336 let keymap_handler =
337 KeymapHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
338 let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
339 keyboard_binding::KeyboardDeviceDescriptor {
340 keys: vec![finput::Key::A, finput::Key::B],
341 ..Default::default()
342 },
343 );
344 let (_, event_time_u64) = testing_utilities::event_times();
345 let input_events = vec![
346 testing_utilities::create_keyboard_event_with_time(
347 finput::Key::A,
348 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
349 None,
350 event_time_u64,
351 &device_descriptor,
352 None,
353 ),
354 testing_utilities::create_keyboard_event_with_handled(
356 finput::Key::B,
357 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
358 None,
359 event_time_u64,
360 &device_descriptor,
361 None,
362 None,
363 input_device::Handled::Yes,
364 ),
365 testing_utilities::create_keyboard_event_with_time(
366 finput::Key::A,
367 fidl_fuchsia_ui_input3::KeyEventType::Released,
368 None,
369 event_time_u64,
370 &device_descriptor,
371 None,
372 ),
373 testing_utilities::create_fake_input_event(event_time_u64),
375 testing_utilities::create_keyboard_event_with_time(
376 finput::Key::B,
377 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
378 None,
379 event_time_u64,
380 &device_descriptor,
381 None,
382 ),
383 ];
384
385 for input_event in input_events {
386 let _ = keymap_handler.clone().handle_input_event(input_event).await;
387 }
388
389 let last_event_timestamp: u64 = event_time_u64.into_nanos().try_into().unwrap();
390
391 diagnostics_assertions::assert_data_tree!(inspector, root: {
392 input_handlers_node: {
393 keymap_handler: {
394 events_received_count: 3u64,
395 events_handled_count: 0u64,
396 last_received_timestamp_ns: last_event_timestamp,
397 "fuchsia.inspect.Health": {
398 status: "STARTING_UP",
399 start_timestamp_nanos: diagnostics_assertions::AnyProperty
402 },
403 }
404 }
405 });
406 }
407}