input_pipeline/
focus_listener.rs1use crate::{Incoming, metrics};
6use fuchsia_async::{self as fasync, TimeoutExt};
7use zx;
8
9const DEFAULT_TEXT_MANAGER_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_seconds(15);
10use anyhow::{Context, Error};
11use fidl_fuchsia_ui_focus as focus;
12use fidl_fuchsia_ui_keyboard_focus as kbd_focus;
13use focus_chain_provider::FocusChainProviderPublisher;
14use futures::StreamExt;
15use metrics_registry::*;
16
17pub struct FocusListener {
19 text_manager: kbd_focus::ControllerProxy,
21
22 focus_chain_listener: focus::FocusChainListenerRequestStream,
24
25 focus_chain_publisher: FocusChainProviderPublisher,
27
28 metrics_logger: metrics::MetricsLogger,
30}
31
32impl FocusListener {
33 pub fn new(
60 incoming: &Incoming,
61 focus_chain_publisher: FocusChainProviderPublisher,
62 metrics_logger: metrics::MetricsLogger,
63 ) -> Result<Self, Error> {
64 let text_manager = incoming.connect_protocol::<kbd_focus::ControllerProxy>()?;
65
66 let (focus_chain_listener_client_end, focus_chain_listener) =
67 fidl::endpoints::create_request_stream::<focus::FocusChainListenerMarker>();
68
69 let focus_chain_listener_registry: focus::FocusChainListenerRegistryProxy =
70 incoming.connect_protocol::<focus::FocusChainListenerRegistryProxy>()?;
71 focus_chain_listener_registry
72 .register(focus_chain_listener_client_end)
73 .context("Failed to register focus chain listener.")?;
74
75 Ok(Self::new_listener(
76 text_manager,
77 focus_chain_listener,
78 focus_chain_publisher,
79 metrics_logger,
80 ))
81 }
82
83 fn new_listener(
94 text_manager: kbd_focus::ControllerProxy,
95 focus_chain_listener: focus::FocusChainListenerRequestStream,
96 focus_chain_publisher: FocusChainProviderPublisher,
97 metrics_logger: metrics::MetricsLogger,
98 ) -> Self {
99 Self { text_manager, focus_chain_listener, focus_chain_publisher, metrics_logger }
100 }
101
102 pub async fn dispatch_focus_changes(&mut self) -> Result<(), Error> {
104 while let Some(focus_change) = self.focus_chain_listener.next().await {
105 fuchsia_trace::duration!("input", "dispatch_focus_changes");
106 match focus_change {
107 Ok(focus::FocusChainListenerRequest::OnFocusChange {
108 focus_chain,
109 responder,
110 ..
111 }) => {
112 fuchsia_trace::duration!("input", "dispatch_focus_changes[processing]");
113 self.focus_chain_publisher
115 .set_state_and_notify_if_changed(&focus_chain)
116 .context("while notifying FocusChainProviderPublisher")?;
117
118 if let Some(ref focus_chain) = focus_chain.focus_chain {
120 if let Some(ref view_ref) = focus_chain.last() {
121 let view_ref_dup = fuchsia_scenic::duplicate_view_ref(&view_ref)?;
122 let notify_result = self
123 .text_manager
124 .notify(view_ref_dup)
125 .on_timeout(
126 fasync::MonotonicInstant::after(DEFAULT_TEXT_MANAGER_TIMEOUT),
127 || {
128 Err(fidl::Error::ClientChannelClosed {
129 status: zx::Status::TIMED_OUT,
130 protocol_name: "fuchsia.ui.keyboard.focus.Controller",
131 epitaph: None,
132 })
133 },
134 )
135 .await;
136 if let Err(e) = notify_result {
137 log::warn!(
138 "Failed to notify text_manager of focus change: {:?}",
139 e
140 );
141 }
142 }
143 };
144
145 responder.send().context("while sending focus chain listener response")?;
146 }
147 Err(e) => self.metrics_logger.log_error(
148 InputPipelineErrorMetricDimensionEvent::FocusChainListenerRequestError,
149 std::format!("FocusChainListenerRequest has error: {}.", e),
150 ),
151 }
152 }
153 log::warn!("Stopped dispatching focus changes.");
154 Ok(())
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use fidl_fuchsia_ui_focus_ext::FocusChainExt;
162 use fidl_fuchsia_ui_views as fidl_ui_views;
163 use fidl_fuchsia_ui_views_ext::ViewRefExt;
164 use fuchsia_scenic as scenic;
165 use futures::join;
166 use pretty_assertions::assert_eq;
167
168 async fn expect_focus_ctl_focus_change(
176 mut request_stream: kbd_focus::ControllerRequestStream,
177 ) -> fidl_ui_views::ViewRef {
178 match request_stream.next().await {
179 Some(Ok(kbd_focus::ControllerRequest::Notify { view_ref, responder, .. })) => {
180 let _ = responder.send();
181 view_ref
182 }
183 _ => panic!("Error expecting text_manager focus change."),
184 }
185 }
186
187 async fn expect_focus_koid_chain(
188 focus_chain_provider_proxy: &focus::FocusChainProviderProxy,
189 ) -> focus::FocusKoidChain {
190 focus_chain_provider_proxy
191 .watch_focus_koid_chain(&focus::FocusChainProviderWatchFocusKoidChainRequest::default())
192 .await
193 .expect("watch_focus_koid_chain")
194 }
195
196 #[fuchsia_async::run_until_stalled(test)]
198 async fn dispatch_focus() -> Result<(), Error> {
199 let (focus_proxy, focus_request_stream) =
200 fidl::endpoints::create_proxy_and_stream::<kbd_focus::ControllerMarker>();
201
202 let (focus_chain_listener_client_end, focus_chain_listener) =
203 fidl::endpoints::create_proxy_and_stream::<focus::FocusChainListenerMarker>();
204
205 let (focus_chain_watcher, focus_chain_provider_stream) =
206 fidl::endpoints::create_proxy_and_stream::<focus::FocusChainProviderMarker>();
207 let (focus_chain_provider_publisher, focus_chain_provider_stream_handler) =
208 focus_chain_provider::make_publisher_and_stream_handler();
209 let _provider_task =
210 focus_chain_provider_stream_handler.handle_request_stream(focus_chain_provider_stream);
211
212 let mut listener = FocusListener::new_listener(
213 focus_proxy,
214 focus_chain_listener,
215 focus_chain_provider_publisher,
216 metrics::MetricsLogger::default(),
217 );
218
219 let _listener_task = fuchsia_async::Task::local(async move {
220 let _ = listener.dispatch_focus_changes().await;
221 });
222
223 let got_focus_koid_chain = expect_focus_koid_chain(&focus_chain_watcher).await;
228 assert_eq!(got_focus_koid_chain, focus::FocusKoidChain::default());
229
230 let view_ref = scenic::ViewRefPair::new()?.view_ref;
231 let view_ref_dup = fuchsia_scenic::duplicate_view_ref(&view_ref)?;
232 let focus_chain =
233 focus::FocusChain { focus_chain: Some(vec![view_ref]), ..Default::default() };
234
235 let (_, view_ref, got_focus_koid_chain) = join!(
236 focus_chain_listener_client_end.on_focus_change(focus_chain.duplicate().unwrap()),
237 expect_focus_ctl_focus_change(focus_request_stream),
238 expect_focus_koid_chain(&focus_chain_watcher),
239 );
240
241 assert_eq!(view_ref.get_koid().unwrap(), view_ref_dup.get_koid().unwrap(),);
242 assert!(focus_chain.equivalent(&got_focus_koid_chain).unwrap());
243
244 Ok(())
245 }
246}