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