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