1use crate::input_handler::{InputHandlerStatus, UnhandledInputHandler};
6use crate::{input_device, metrics};
7use anyhow::{Context, Error, Result};
8use async_trait::async_trait;
9use async_utils::hanging_get::client::HangingGetStream;
10use fuchsia_inspect::health::Reporter;
11use futures::{TryFutureExt, TryStreamExt};
12use metrics_registry::*;
13use std::cell::RefCell;
14use std::rc::Rc;
15use {fidl_fuchsia_input as finput, fidl_fuchsia_settings as fsettings, fuchsia_async as fasync};
16
17pub struct TextSettingsHandler {
21 keymap_id: RefCell<Option<finput::KeymapId>>,
25
26 pub inspect_status: InputHandlerStatus,
28
29 metrics_logger: metrics::MetricsLogger,
31}
32
33#[async_trait(?Send)]
34impl UnhandledInputHandler for TextSettingsHandler {
35 async fn handle_unhandled_input_event(
36 self: Rc<Self>,
37 unhandled_input_event: input_device::UnhandledInputEvent,
38 ) -> Vec<input_device::InputEvent> {
39 fuchsia_trace::duration!("input", "text_settings_handler");
40 match unhandled_input_event {
41 input_device::UnhandledInputEvent {
42 device_event: input_device::InputDeviceEvent::Keyboard(mut event),
43 device_descriptor,
44 event_time,
45 trace_id,
46 } => {
47 fuchsia_trace::duration!("input", "text_settings_handler[processing]");
48 if let Some(trace_id) = trace_id {
49 fuchsia_trace::flow_step!(
50 c"input",
51 c"event_in_input_pipeline",
52 trace_id.into()
53 );
54 }
55
56 self.inspect_status.count_received_event(&event_time);
57 let keymap_id = self.get_keymap_name();
58 log::debug!(
59 "text_settings_handler::Instance::handle_unhandled_input_event: keymap_id = {:?}",
60 &keymap_id
61 );
62 event = event.into_with_keymap(keymap_id);
63 vec![input_device::InputEvent {
64 device_event: input_device::InputDeviceEvent::Keyboard(event),
65 device_descriptor,
66 event_time,
67 handled: input_device::Handled::No,
68 trace_id,
69 }]
70 }
71 _ => {
73 log::warn!("Unhandled input event: {:?}", unhandled_input_event.get_event_type());
75 vec![input_device::InputEvent::from(unhandled_input_event)]
76 }
77 }
78 }
79
80 fn set_handler_healthy(self: std::rc::Rc<Self>) {
81 self.inspect_status.health_node.borrow_mut().set_ok();
82 }
83
84 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
85 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
86 }
87
88 fn get_name(&self) -> &'static str {
89 "TextSettingsHandler"
90 }
91
92 fn interest(&self) -> Vec<input_device::InputEventType> {
93 vec![input_device::InputEventType::Keyboard]
94 }
95}
96
97impl TextSettingsHandler {
98 pub fn new(
103 initial_keymap: Option<finput::KeymapId>,
104 input_handlers_node: &fuchsia_inspect::Node,
105 metrics_logger: metrics::MetricsLogger,
106 ) -> Rc<Self> {
107 let inspect_status = InputHandlerStatus::new(
108 input_handlers_node,
109 "text_settings_handler",
110 false,
111 );
112 Rc::new(Self { keymap_id: RefCell::new(initial_keymap), inspect_status, metrics_logger })
113 }
114
115 pub async fn process_keymap_configuration_from(
117 self: &Rc<Self>,
118 proxy: fsettings::KeyboardProxy,
119 ) -> Result<(), Error> {
120 let mut stream = HangingGetStream::new(proxy, fsettings::KeyboardProxy::watch);
121 loop {
122 match stream
123 .try_next()
124 .await
125 .context("while waiting on fuchsia.settings.Keyboard/Watch")?
126 {
127 Some(fsettings::KeyboardSettings { keymap, .. }) => {
128 self.set_keymap_id(keymap);
129 log::info!("keymap ID set to: {:?}", self.get_keymap_id());
130 }
131 e => {
132 self.metrics_logger.log_error(
133 InputPipelineErrorMetricDimensionEvent::TextSettingsHandlerExit,
134 std::format!("exiting - unexpected response: {:?}", e),
135 );
136 break;
137 }
138 }
139 }
140 Ok(())
141 }
142
143 pub fn serve(self: Rc<Self>, proxy: fsettings::KeyboardProxy) {
145 let metrics_logger_clone = self.metrics_logger.clone();
146 fasync::Task::local(
147 async move { self.process_keymap_configuration_from(proxy).await }
148 .unwrap_or_else(move |e: anyhow::Error| {
153 metrics_logger_clone.log_warn(
154 InputPipelineErrorMetricDimensionEvent::TextSettingsHandlerCantRun,
155 std::format!("can't run: {:?}", e),
156 );
157 }),
158 )
159 .detach();
160 }
161
162 fn set_keymap_id(self: &Rc<Self>, keymap_id: Option<finput::KeymapId>) {
163 *(self.keymap_id.borrow_mut()) = keymap_id;
164 }
165
166 pub fn get_keymap_id(&self) -> Option<finput::KeymapId> {
168 self.keymap_id.borrow().clone()
169 }
170
171 fn get_keymap_name(&self) -> Option<String> {
172 match *self.keymap_id.borrow() {
174 Some(id) => match id {
175 finput::KeymapId::FrAzerty => Some("FR_AZERTY".to_owned()),
176 finput::KeymapId::UsDvorak => Some("US_DVORAK".to_owned()),
177 finput::KeymapId::UsColemak => Some("US_COLEMAK".to_owned()),
178 finput::KeymapId::UsQwerty | finput::KeymapIdUnknown!() => {
179 Some("US_QWERTY".to_owned())
180 }
181 },
182 None => Some("US_QWERTY".to_owned()),
183 }
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 use crate::input_handler::InputHandler;
192 use crate::{keyboard_binding, testing_utilities};
193 use fuchsia_async as fasync;
194 use pretty_assertions::assert_eq;
195 use std::convert::TryFrom as _;
196
197 fn input_event_from(
198 keyboard_event: keyboard_binding::KeyboardEvent,
199 ) -> input_device::InputEvent {
200 testing_utilities::create_input_event(
201 keyboard_event,
202 &input_device::InputDeviceDescriptor::Fake,
203 zx::MonotonicInstant::from_nanos(42),
204 input_device::Handled::No,
205 )
206 }
207
208 fn key_event_with_settings(keymap: Option<String>) -> input_device::InputEvent {
209 let keyboard_event = keyboard_binding::KeyboardEvent::new(
210 fidl_fuchsia_input::Key::A,
211 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
212 )
213 .into_with_keymap(keymap);
214 input_event_from(keyboard_event)
215 }
216
217 fn key_event(keymap: Option<String>) -> input_device::InputEvent {
218 let keyboard_event = keyboard_binding::KeyboardEvent::new(
219 fidl_fuchsia_input::Key::A,
220 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
221 )
222 .into_with_keymap(keymap);
223 input_event_from(keyboard_event)
224 }
225
226 fn unhandled_key_event() -> input_device::UnhandledInputEvent {
227 input_device::UnhandledInputEvent::try_from(key_event(None)).unwrap()
228 }
229
230 #[fasync::run_singlethreaded(test)]
231 async fn keymap_id_setting() {
232 #[derive(Debug)]
233 struct Test {
234 keymap_id: Option<finput::KeymapId>,
235 expected: Option<String>,
236 }
237 let tests = vec![
238 Test { keymap_id: None, expected: Some("US_QWERTY".to_owned()) },
239 Test {
240 keymap_id: Some(finput::KeymapId::UsQwerty),
241 expected: Some("US_QWERTY".to_owned()),
242 },
243 Test {
244 keymap_id: Some(finput::KeymapId::FrAzerty),
245 expected: Some("FR_AZERTY".to_owned()),
246 },
247 Test {
248 keymap_id: Some(finput::KeymapId::UsDvorak),
249 expected: Some("US_DVORAK".to_owned()),
250 },
251 Test {
252 keymap_id: Some(finput::KeymapId::UsColemak),
253 expected: Some("US_COLEMAK".to_owned()),
254 },
255 ];
256 let inspector = fuchsia_inspect::Inspector::default();
257 let test_node = inspector.root().create_child("test_node");
258 for test in tests {
259 let handler = TextSettingsHandler::new(
260 test.keymap_id.clone(),
261 &test_node,
262 metrics::MetricsLogger::default(),
263 );
264 let expected = key_event(test.expected.clone());
265 let result = handler.handle_unhandled_input_event(unhandled_key_event()).await;
266 assert_eq!(vec![expected], result, "for: {:?}", &test);
267 }
268 }
269
270 fn serve_into(
271 mut server_end: fsettings::KeyboardRequestStream,
272 keymap: Option<finput::KeymapId>,
273 ) {
274 fasync::Task::local(async move {
275 if let Ok(Some(fsettings::KeyboardRequest::Watch { responder, .. })) =
276 server_end.try_next().await
277 {
278 let settings = fsettings::KeyboardSettings { keymap, ..Default::default() };
279 responder.send(&settings).expect("response sent");
280 }
281 })
282 .detach();
283 }
284
285 #[fasync::run_singlethreaded(test)]
286 async fn config_call_processing() {
287 let inspector = fuchsia_inspect::Inspector::default();
288 let test_node = inspector.root().create_child("test_node");
289 let handler = TextSettingsHandler::new(None, &test_node, metrics::MetricsLogger::default());
290
291 let (proxy, stream) =
292 fidl::endpoints::create_proxy_and_stream::<fsettings::KeyboardMarker>();
293
294 serve_into(stream, Some(finput::KeymapId::FrAzerty));
296
297 handler.clone().serve(proxy);
300
301 let deadline =
308 fuchsia_async::MonotonicInstant::after(zx::MonotonicDuration::from_seconds(5));
309 loop {
310 let result = handler.clone().handle_unhandled_input_event(unhandled_key_event()).await;
311 let expected = key_event_with_settings(Some("FR_AZERTY".to_owned()));
312 if vec![expected] == result {
313 break;
314 }
315 fuchsia_async::Timer::new(fuchsia_async::MonotonicInstant::after(
316 zx::MonotonicDuration::from_millis(10),
317 ))
318 .await;
319 let now = fuchsia_async::MonotonicInstant::now();
320 assert!(now < deadline, "the settings did not get applied, was: {:?}", &result);
321 }
322 }
323
324 #[fuchsia::test]
325 async fn text_settings_handler_initialized_with_inspect_node() {
326 let inspector = fuchsia_inspect::Inspector::default();
327 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
328 let _handler =
329 TextSettingsHandler::new(None, &fake_handlers_node, metrics::MetricsLogger::default());
330 diagnostics_assertions::assert_data_tree!(inspector, root: {
331 input_handlers_node: {
332 text_settings_handler: {
333 events_received_count: 0u64,
334 events_handled_count: 0u64,
335 last_received_timestamp_ns: 0u64,
336 "fuchsia.inspect.Health": {
337 status: "STARTING_UP",
338 start_timestamp_nanos: diagnostics_assertions::AnyProperty
341 },
342 }
343 }
344 });
345 }
346
347 #[fasync::run_singlethreaded(test)]
348 async fn text_settings_handler_inspect_counts_events() {
349 let inspector = fuchsia_inspect::Inspector::default();
350 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
351 let text_settings_handler =
352 TextSettingsHandler::new(None, &fake_handlers_node, metrics::MetricsLogger::default());
353 let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
354 keyboard_binding::KeyboardDeviceDescriptor {
355 keys: vec![finput::Key::A, finput::Key::B],
356 ..Default::default()
357 },
358 );
359 let (_, event_time_u64) = testing_utilities::event_times();
360 let input_events = vec![
361 testing_utilities::create_keyboard_event_with_time(
362 finput::Key::A,
363 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
364 None,
365 event_time_u64,
366 &device_descriptor,
367 None,
368 ),
369 testing_utilities::create_keyboard_event_with_handled(
371 finput::Key::B,
372 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
373 None,
374 event_time_u64,
375 &device_descriptor,
376 None,
377 None,
378 input_device::Handled::Yes,
379 ),
380 testing_utilities::create_keyboard_event_with_time(
381 finput::Key::A,
382 fidl_fuchsia_ui_input3::KeyEventType::Released,
383 None,
384 event_time_u64,
385 &device_descriptor,
386 None,
387 ),
388 testing_utilities::create_fake_input_event(event_time_u64),
390 testing_utilities::create_keyboard_event_with_time(
391 finput::Key::B,
392 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
393 None,
394 event_time_u64,
395 &device_descriptor,
396 None,
397 ),
398 ];
399
400 for input_event in input_events {
401 let _ = text_settings_handler.clone().handle_input_event(input_event).await;
402 }
403
404 let last_event_timestamp: u64 = event_time_u64.into_nanos().try_into().unwrap();
405
406 diagnostics_assertions::assert_data_tree!(inspector, root: {
407 input_handlers_node: {
408 text_settings_handler: {
409 events_received_count: 3u64,
410 events_handled_count: 0u64,
411 last_received_timestamp_ns: last_event_timestamp,
412 "fuchsia.inspect.Health": {
413 status: "STARTING_UP",
414 start_timestamp_nanos: diagnostics_assertions::AnyProperty
417 },
418 }
419 }
420 });
421 }
422}