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