ui_puppet_lib/
view.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::presentation_loop;
6use async_utils::event::Event as AsyncEvent;
7use async_utils::hanging_get::client::HangingGetStream;
8use euclid::{Point2D, Transform2D};
9use fidl::endpoints::{create_proxy, create_request_stream, ServerEnd};
10use fidl_fuchsia_ui_input3::{self as ui_input3, KeyEvent};
11use fidl_fuchsia_ui_pointer::{
12    self as ui_pointer, MouseEvent, TouchEvent, TouchInteractionId, TouchInteractionStatus,
13    TouchResponse,
14};
15use futures::channel::{mpsc, oneshot};
16use futures::StreamExt;
17use log::info;
18use once_cell::unsync::OnceCell;
19use std::cell::RefCell;
20use std::collections::HashMap;
21use std::rc::Rc;
22use std::slice::Iter;
23use {
24    fidl_fuchsia_math as fmath, fidl_fuchsia_ui_composition as ui_comp,
25    fidl_fuchsia_ui_test_conformance as ui_conformance, fidl_fuchsia_ui_test_input as test_input,
26    fidl_fuchsia_ui_views as ui_views, fuchsia_async as fasync, fuchsia_scenic as scenic,
27};
28
29pub type FlatlandPtr = Rc<ui_comp::FlatlandProxy>;
30
31/// Helper function to request to present a set of changes to flatland.
32async fn request_present(presentation_sender: &presentation_loop::PresentationSender) {
33    let (sender, receiver) = oneshot::channel::<()>();
34    presentation_sender.unbounded_send(sender).expect("failed to request present");
35    _ = receiver.await;
36}
37
38// An interaction is a add-change-remove sequence for a single "finger" on a particular
39// device.  Until the interaction status has been settled (i.e. the entire interaction is
40// either denied or granted), events are buffered.  When the interaction is granted, the
41// buffered events are sent to the app via `touch_input_listener`, and subsequent events
42// are immediately sent via `touch_input_listener`.  Conversely, when the interaction is
43// denied, buffered events and all subsequent events are dropped.
44struct TouchInteraction {
45    // Only contains InternalMessage::TouchEvents.
46    pending_events: Vec<test_input::TouchInputListenerReportTouchInputRequest>,
47    status: Option<ui_pointer::TouchInteractionStatus>,
48}
49
50/// Identifiers required to manipulate embedded view state.
51struct EmbeddedViewIds {
52    /// Flatland `TransformId` for the embedding viewport.
53    transform_id: ui_comp::TransformId,
54
55    /// Flatland `ContentId` for the embedding viewport.
56    content_id: ui_comp::ContentId,
57}
58
59/// Encapsulates capabilities and resources associated with a puppet's view.
60pub(super) struct View {
61    /// Flatland connection scoped to our view.
62    flatland: FlatlandPtr,
63
64    /// Task to poll continuously for view events, and respond as necessary.
65    view_event_listener: OnceCell<fasync::Task<()>>,
66
67    /// Used to present changes to flatland.
68    presentation_sender: presentation_loop::PresentationSender,
69
70    /// Used to generate flatland transform and content IDs.
71    id_generator: scenic::flatland::IdGenerator,
72
73    /// Flatland `TransformId` that corresponds to our view's root transform.
74    root_transform_id: ui_comp::TransformId,
75
76    /// View dimensions, in its own logical coordinate space.
77    logical_size: fmath::SizeU,
78
79    /// DPR used to convert between logical and physical coordinates.
80    device_pixel_ratio: f32,
81
82    /// Indicates whether our view is connected to the display.
83    connected_to_display: bool,
84
85    /// Task to poll continuously for touch events, and respond as necessary.
86    touch_watcher_task: OnceCell<fasync::Task<()>>,
87
88    /// ViewParameters of touch event, need store this value because not all touch
89    /// events include this information.
90    view_parameters: Option<ui_pointer::ViewParameters>,
91
92    /// Store pending touch interactions.
93    touch_interactions: HashMap<TouchInteractionId, TouchInteraction>,
94
95    /// Proxy to forward touch events to test.
96    touch_input_listener: Option<test_input::TouchInputListenerProxy>,
97
98    /// Task to poll continuously for mouse events.
99    mouse_watched_task: OnceCell<fasync::Task<()>>,
100
101    /// Proxy to forward mouse events to test.
102    mouse_input_listener: Option<test_input::MouseInputListenerProxy>,
103
104    /// Task to poll continuously for keyboard events.
105    keyboard_watched_task: OnceCell<fasync::Task<()>>,
106
107    /// Proxy to forward keyboard events to test.
108    keyboard_input_listener: Option<test_input::KeyboardInputListenerProxy>,
109
110    /// Holds a map from user-defined ID to embedded view IDs.
111    embedded_views: HashMap<u64, EmbeddedViewIds>,
112}
113
114impl View {
115    pub async fn new(
116        flatland: ui_comp::FlatlandProxy,
117        keyboard_client: ui_input3::KeyboardProxy,
118        view_creation_token: ui_views::ViewCreationToken,
119        touch_input_listener: Option<test_input::TouchInputListenerProxy>,
120        mouse_input_listener: Option<test_input::MouseInputListenerProxy>,
121        keyboard_input_listener: Option<test_input::KeyboardInputListenerProxy>,
122        device_pixel_ratio: f32,
123        view_focuser: Option<ServerEnd<ui_views::FocuserMarker>>,
124    ) -> (Rc<RefCell<Self>>, ui_views::ViewRef) {
125        let flatland = Rc::new(flatland);
126        let (presentation_sender, presentation_receiver) = mpsc::unbounded();
127        presentation_loop::start_flatland_presentation_loop(
128            presentation_receiver,
129            Rc::downgrade(&flatland),
130        );
131
132        let mut id_generator = scenic::flatland::IdGenerator::new();
133
134        // Create view parameters.
135        let (parent_viewport_watcher, parent_viewport_watcher_request) =
136            create_proxy::<ui_comp::ParentViewportWatcherMarker>();
137        let (touch_source, touch_source_request) = create_proxy::<ui_pointer::TouchSourceMarker>();
138        let (mouse_source, mouse_source_request) = create_proxy::<ui_pointer::MouseSourceMarker>();
139        let view_bound_protocols = ui_comp::ViewBoundProtocols {
140            touch_source: Some(touch_source_request),
141            mouse_source: Some(mouse_source_request),
142            view_focuser,
143            ..Default::default()
144        };
145        let view_ref_pair = scenic::ViewRefPair::new().expect("failed to create view ref pair");
146        let view_ref = scenic::duplicate_view_ref(&view_ref_pair.view_ref)
147            .expect("failed to duplicate view ref");
148        let view_ref_clone =
149            scenic::duplicate_view_ref(&view_ref).expect("failed to duplicate view ref");
150        let view_identity = ui_views::ViewIdentityOnCreation::from(view_ref_pair);
151
152        // Create root transform ID.
153        let root_transform_id = Self::create_transform(flatland.clone(), &mut id_generator);
154
155        // Create the view and present.
156        flatland
157            .create_view2(
158                view_creation_token,
159                view_identity,
160                view_bound_protocols,
161                parent_viewport_watcher_request,
162            )
163            .expect("failed to create view");
164        flatland.set_root_transform(&root_transform_id).expect("failed to set root transform");
165        info!("[b/328261162] Wait for request_present()");
166        request_present(&presentation_sender).await;
167
168        let this = Rc::new(RefCell::new(Self {
169            flatland,
170            view_event_listener: OnceCell::new(),
171            presentation_sender,
172            id_generator,
173            root_transform_id,
174            logical_size: fmath::SizeU { width: 0, height: 0 },
175            device_pixel_ratio,
176            connected_to_display: false,
177            touch_watcher_task: OnceCell::new(),
178            view_parameters: None,
179            touch_interactions: HashMap::new(),
180            touch_input_listener,
181            mouse_watched_task: OnceCell::new(),
182            mouse_input_listener,
183            keyboard_watched_task: OnceCell::new(),
184            keyboard_input_listener,
185            embedded_views: HashMap::new(),
186        }));
187
188        let view_initialized = AsyncEvent::new();
189        let view_events_task = fasync::Task::local(Self::listen_for_view_events(
190            this.clone(),
191            parent_viewport_watcher,
192            view_initialized.clone(),
193        ));
194        this.borrow_mut()
195            .view_event_listener
196            .set(view_events_task)
197            .expect("set event listener task more than once");
198
199        // Start the touch watcher task.
200        let touch_task =
201            fasync::Task::local(Self::listen_for_touch_events(this.clone(), touch_source));
202        this.borrow_mut()
203            .touch_watcher_task
204            .set(touch_task)
205            .expect("set touch watcher task more than once");
206
207        let mouse_task =
208            fasync::Task::local(Self::listen_for_mouse_events(this.clone(), mouse_source));
209        this.borrow_mut()
210            .mouse_watched_task
211            .set(mouse_task)
212            .expect("set mouse watcher task more than once");
213
214        let keyboard_ready = AsyncEvent::new();
215        let keyboard_task = fasync::Task::local(Self::listen_for_key_events(
216            this.clone(),
217            keyboard_client,
218            view_ref,
219            keyboard_ready.clone(),
220        ));
221        this.borrow_mut()
222            .keyboard_watched_task
223            .set(keyboard_task)
224            .expect("set keyboard watcher task more than once");
225
226        info!("[b/328261162] Wait for view to be initialized.");
227        _ = view_initialized.wait().await;
228
229        info!("[b/328261162] Wait for keyboard listener ready.");
230        _ = keyboard_ready.wait().await;
231
232        (this, view_ref_clone)
233    }
234
235    /// Returns true if the parent viewport is connected to the display AND we've received non-zero
236    /// layout info.
237    fn is_initialized(&self) -> bool {
238        info!(
239            "connected to display = {} logical size = ({}, {})",
240            self.connected_to_display, self.logical_size.width, self.logical_size.height
241        );
242        self.connected_to_display && self.logical_size.width > 0 && self.logical_size.height > 0
243    }
244
245    /// Polls continuously for events reported to the view (parent viewport updates,
246    /// touch/mouse/keyboard input, etc.).
247    async fn listen_for_view_events(
248        this: Rc<RefCell<Self>>,
249        parent_viewport_watcher: ui_comp::ParentViewportWatcherProxy,
250        view_initialized: AsyncEvent,
251    ) {
252        let mut view_initialized = Some(view_initialized);
253
254        let mut layout_info_stream = HangingGetStream::new(
255            parent_viewport_watcher.clone(),
256            ui_comp::ParentViewportWatcherProxy::get_layout,
257        );
258        let mut status_stream = HangingGetStream::new(
259            parent_viewport_watcher,
260            ui_comp::ParentViewportWatcherProxy::get_status,
261        );
262
263        loop {
264            futures::select! {
265                parent_status = status_stream.select_next_some() => {
266                    match parent_status {
267                        Ok(status) => {
268                            info!("received parent status update");
269                            this.borrow_mut().update_parent_status(status);
270                        }
271                        // Currently, CTF tests share the same puppet factory, so when one test exits,
272                        // all other tests will get a channel closed error.
273                        Err(fidl::Error::ClientChannelClosed{..}) => {
274                            break;
275                        }
276                        Err(e) => {
277                            panic!("get_status got unexpected error {:?}", e);
278                        }
279                    }
280                }
281                layout_info = layout_info_stream.select_next_some() => {
282                    match layout_info {
283                        Ok(layout_info) => {
284                            this.borrow_mut().update_view_parameters(layout_info.logical_size, layout_info.device_pixel_ratio);
285                        }
286                        // Currently, CTF tests share the same puppet factory, so when one test exits,
287                        // all other tests will get a channel closed error.
288                        Err(fidl::Error::ClientChannelClosed{..}) => {
289                            break;
290                        }
291                        Err(e) => {
292                            panic!("get_layout got unexpected error {:?}", e);
293                        }
294                    }
295                }
296            }
297
298            // If the view has become initialized, ping the `view_is_initialized` channel.
299            if view_initialized.is_some() && this.borrow().is_initialized() {
300                view_initialized.take().expect("failed to take view initialized sender").signal();
301            }
302        }
303    }
304
305    /// Creates a viewport according to the given `properties`.
306    pub async fn embed_remote_view(
307        &mut self,
308        id: u64,
309        properties: ui_conformance::EmbeddedViewProperties,
310    ) -> ui_views::ViewCreationToken {
311        let view_bounds = properties.bounds.expect("missing embedded view bounds");
312
313        // Create the viewport transform.
314        let transform_id = Self::create_transform(self.flatland.clone(), &mut self.id_generator);
315
316        // Create the content id.
317        let content_id = self.id_generator.next_content_id();
318
319        // Create the view/viewport token pair.
320        let token_pair = scenic::flatland::ViewCreationTokenPair::new()
321            .expect("failed to create view creation token pair");
322
323        // Create the embedding viewport.
324        let (_, child_view_watcher_request) = create_proxy::<ui_comp::ChildViewWatcherMarker>();
325        self.flatland
326            .create_viewport(
327                &content_id,
328                token_pair.viewport_creation_token,
329                &ui_comp::ViewportProperties {
330                    logical_size: view_bounds.size,
331                    ..Default::default()
332                },
333                child_view_watcher_request,
334            )
335            .expect("failed to create child viewport");
336
337        // Attach the embedding viewport to its transform.
338        self.flatland
339            .set_content(&transform_id, &content_id)
340            .expect("failed to set viewport content");
341
342        // Position the embedded view.
343        if let Some(origin) = view_bounds.origin {
344            self.flatland
345                .set_translation(&transform_id, &origin)
346                .expect("failed to position embedded view");
347        }
348
349        // Attach the child view to the view's root transform.
350        self.flatland
351            .add_child(&self.root_transform_id, &transform_id)
352            .expect("failed to attach embedded view to root transform");
353
354        // Present changes.
355        request_present(&self.presentation_sender).await;
356
357        self.embedded_views.insert(id, EmbeddedViewIds { transform_id, content_id });
358
359        token_pair.view_creation_token
360    }
361
362    pub async fn set_embedded_view_properties(
363        &mut self,
364        id: u64,
365        properties: ui_conformance::EmbeddedViewProperties,
366    ) {
367        let view_bounds = properties.bounds.expect("missing embedded view bounds");
368
369        // Get embedded view content + transform IDs.
370        let embedded_view =
371            self.embedded_views.get_mut(&id).expect("no embedded view with specified id");
372
373        // Set viewport properties and translation.
374        self.flatland
375            .set_viewport_properties(
376                &embedded_view.content_id,
377                &ui_comp::ViewportProperties {
378                    logical_size: view_bounds.size,
379                    ..Default::default()
380                },
381            )
382            .expect("failed to set viewport properties");
383        if let Some(origin) = view_bounds.origin {
384            self.flatland
385                .set_translation(&embedded_view.transform_id, &origin)
386                .expect("failed to position embedded view");
387        }
388
389        // Present changes.
390        request_present(&self.presentation_sender).await;
391    }
392
393    /// Creates a flatland transform and returns its `TransformId`.
394    fn create_transform(
395        flatland: FlatlandPtr,
396        id_generator: &mut scenic::flatland::IdGenerator,
397    ) -> ui_comp::TransformId {
398        let flatland_transform_id = id_generator.next_transform_id();
399
400        flatland.create_transform(&flatland_transform_id).expect("failed to create transform");
401
402        flatland_transform_id
403    }
404
405    /// Helper method to update our book keeping on our view's spatial parameters.
406    fn update_view_parameters(
407        &mut self,
408        logical_size: Option<fmath::SizeU>,
409        device_pixel_ratio: Option<fmath::VecF>,
410    ) {
411        if let Some(size) = logical_size {
412            self.logical_size = size;
413        }
414
415        if let Some(dpr) = device_pixel_ratio {
416            assert!(dpr.x == dpr.y);
417            self.device_pixel_ratio = dpr.x;
418        }
419    }
420
421    /// Helper method to update our book keeping on the parent viewport's status.
422    fn update_parent_status(&mut self, parent_status: ui_comp::ParentViewportStatus) {
423        self.connected_to_display = match parent_status {
424            ui_comp::ParentViewportStatus::ConnectedToDisplay => true,
425            ui_comp::ParentViewportStatus::DisconnectedFromDisplay => false,
426        };
427    }
428
429    // If no `Interaction` exists for the specified `id`, insert a newly-instantiated one.
430    fn ensure_interaction_exists(&mut self, id: &TouchInteractionId) {
431        if !self.touch_interactions.contains_key(id) {
432            self.touch_interactions
433                .insert(id.clone(), TouchInteraction { pending_events: vec![], status: None });
434        }
435    }
436
437    fn get_touch_report(
438        &self,
439        touch_event: &ui_pointer::TouchEvent,
440    ) -> test_input::TouchInputListenerReportTouchInputRequest {
441        let pointer_sample =
442            touch_event.pointer_sample.as_ref().expect("touch event missing pointer_sample");
443        let position_in_viewport = pointer_sample
444            .position_in_viewport
445            .expect("pointer sample missing position_in_viewport");
446        let local_position =
447            self.get_local_position(Point2D::new(position_in_viewport[0], position_in_viewport[1]));
448        let interact = pointer_sample.interaction.expect("interaction is missing");
449        let pointer_id = interact.pointer_id;
450        let device_id = interact.device_id;
451
452        let local_x: f64 = local_position.x.try_into().expect("failed to convert to f64");
453        let local_y: f64 = local_position.y.try_into().expect("failed to convert to f64");
454        let view_bounds = self.view_parameters.expect("missing view parameters").view;
455        let view_min_x: f64 = view_bounds.min[0].try_into().expect("failed to convert to f64");
456        let view_min_y: f64 = view_bounds.min[1].try_into().expect("failed to convert to f64");
457        let view_max_x: f64 = view_bounds.max[0].try_into().expect("failed to convert to f64");
458        let view_max_y: f64 = view_bounds.max[1].try_into().expect("failed to convert to f64");
459
460        info!("view min ({:?}, {:?})", view_min_x, view_min_y);
461        info!("view max ({:?}, {:?})", view_max_x, view_max_y);
462        info!("tap received at ({:?}, {:?})", local_x, local_y);
463
464        test_input::TouchInputListenerReportTouchInputRequest {
465            local_x: Some(local_x),
466            local_y: Some(local_y),
467            phase: pointer_sample.phase,
468            pointer_id: Some(pointer_id),
469            time_received: touch_event.timestamp,
470            device_pixel_ratio: Some(self.device_pixel_ratio as f64),
471            device_id: Some(device_id),
472            ..Default::default()
473        }
474    }
475
476    fn get_local_position(&self, position_in_viewpoint: Point2D<f32, f32>) -> Point2D<f32, f32> {
477        let viewport_to_view_transform =
478            self.view_parameters.expect("missing view parameters").viewport_to_view_transform;
479        Transform2D::new(
480            /* 1, 1 */ viewport_to_view_transform[0],
481            /* 1, 2 */ viewport_to_view_transform[3],
482            /* 2, 1 */ viewport_to_view_transform[1],
483            /* 2, 2 */ viewport_to_view_transform[4],
484            /* 3, 1 */ viewport_to_view_transform[6],
485            /* 3, 2 */ viewport_to_view_transform[7],
486        )
487        .transform_point(position_in_viewpoint)
488    }
489
490    fn process_touch_events(
491        &mut self,
492        events: Vec<ui_pointer::TouchEvent>,
493    ) -> Vec<ui_pointer::TouchResponse> {
494        // Generate the responses which will be sent with the next call to
495        // `fuchsia.ui.pointer.TouchSource.Watch()`.
496        let pending_responses = Self::generate_touch_event_responses(events.iter());
497
498        for e in events.iter() {
499            if let Some(view_parameters) = e.view_parameters {
500                self.view_parameters = Some(view_parameters);
501            }
502
503            // Handle `pointer_sample` field, if it exists.
504            if let Some(ui_pointer::TouchPointerSample { interaction: Some(id), .. }) =
505                &e.pointer_sample
506            {
507                self.ensure_interaction_exists(&id);
508                let interaction_status =
509                    self.touch_interactions.get(id).expect("interaction does not exist").status;
510                match interaction_status {
511                    None => {
512                        // Queue pending report unil interaction is resolved.
513                        let touch_report = self.get_touch_report(&e);
514                        self.touch_interactions
515                            .get_mut(&id)
516                            .unwrap()
517                            .pending_events
518                            .push(touch_report);
519                    }
520                    Some(TouchInteractionStatus::Granted) => {
521                        match &self.touch_input_listener {
522                            Some(listener) => {
523                                // Samples received after the interaction is granted are
524                                // immediately sent to the listener
525                                let touch_report = self.get_touch_report(&e);
526                                listener
527                                    .report_touch_input(&touch_report)
528                                    .expect("failed to send touch input report");
529                            }
530                            None => {
531                                info!("no touch event listener.");
532                            }
533                        }
534                    }
535                    Some(TouchInteractionStatus::Denied) => {
536                        // Drop the event/msg, and remove the interaction from the map:
537                        // we're guaranteed not to receive any further events for this
538                        // interaction.
539                        self.touch_interactions.remove(&id);
540                    }
541                }
542            }
543
544            // Handle `interaction_result` field, if it exists.
545            if let Some(ui_pointer::TouchInteractionResult { interaction: id, status }) =
546                &e.interaction_result
547            {
548                self.ensure_interaction_exists(&id);
549                let interaction = self.touch_interactions.get_mut(&id).unwrap();
550                if let Some(existing_status) = &interaction.status {
551                    // The status of an interaction can only change from None to Some().
552                    assert_eq!(status, existing_status);
553                } else {
554                    // Status was previously None.
555                    interaction.status = Some(status.clone());
556                }
557
558                match status {
559                    ui_pointer::TouchInteractionStatus::Granted => {
560                        // Report buffered events to touch listener
561                        let mut pending_events = vec![];
562                        std::mem::swap(
563                            &mut pending_events,
564                            &mut self.touch_interactions.get_mut(&id).unwrap().pending_events,
565                        );
566                        for pending_event in pending_events {
567                            match &self.touch_input_listener {
568                                Some(listener) => {
569                                    listener
570                                        .report_touch_input(&pending_event)
571                                        .expect("failed to send touch input report");
572                                }
573                                None => {
574                                    info!("no touch event listener.");
575                                }
576                            }
577                        }
578                    }
579                    ui_pointer::TouchInteractionStatus::Denied => {
580                        // Drop any buffered events and remove the interaction from the
581                        // map: we're guaranteed not to receive any further events for
582                        // this interaction.
583                        self.touch_interactions.remove(&id);
584                    }
585                }
586            }
587        }
588
589        pending_responses
590    }
591
592    /// Generate a vector of responses to the input `TouchEvents`, as required by
593    /// `fuchsia.ui.pointer.TouchSource.Watch()`.
594    fn generate_touch_event_responses(events: Iter<'_, TouchEvent>) -> Vec<TouchResponse> {
595        events
596            .map(|evt| {
597                if let Some(_) = &evt.pointer_sample {
598                    return TouchResponse {
599                        response_type: Some(ui_pointer::TouchResponseType::Yes),
600                        trace_flow_id: evt.trace_flow_id,
601                        ..Default::default()
602                    };
603                }
604                TouchResponse::default()
605            })
606            .collect()
607    }
608
609    async fn listen_for_touch_events(
610        this: Rc<RefCell<Self>>,
611        touch_source: ui_pointer::TouchSourceProxy,
612    ) {
613        let mut pending_responses: Vec<TouchResponse> = vec![];
614
615        loop {
616            let events = touch_source.watch(&pending_responses);
617
618            match events.await {
619                Ok(events) => {
620                    pending_responses = this.borrow_mut().process_touch_events(events);
621                }
622                _ => {
623                    info!("TouchSource connection closed");
624                    return;
625                }
626            }
627        }
628    }
629
630    fn get_mouse_report(
631        &self,
632        mouse_event: &ui_pointer::MouseEvent,
633    ) -> test_input::MouseInputListenerReportMouseInputRequest {
634        let pointer_sample =
635            mouse_event.pointer_sample.as_ref().expect("mouse event missing pointer_sample");
636        let position_in_viewport = pointer_sample
637            .position_in_viewport
638            .expect("pointer sample missing position_in_viewport");
639        let local_position =
640            self.get_local_position(Point2D::new(position_in_viewport[0], position_in_viewport[1]));
641        let local_x: f64 = local_position.x.try_into().expect("failed to convert to f64");
642        let local_y: f64 = local_position.y.try_into().expect("failed to convert to f64");
643        let device_id = pointer_sample.device_id.expect("pointer sample missing device id");
644
645        let buttons: Option<Vec<test_input::MouseButton>> = match &pointer_sample.pressed_buttons {
646            None => None,
647            Some(buttons) => Some(
648                buttons
649                    .into_iter()
650                    .map(|button| {
651                        test_input::MouseButton::from_primitive_allow_unknown(*button as u32)
652                    })
653                    .collect(),
654            ),
655        };
656
657        test_input::MouseInputListenerReportMouseInputRequest {
658            local_x: Some(local_x),
659            local_y: Some(local_y),
660            time_received: mouse_event.timestamp,
661            buttons,
662            device_pixel_ratio: Some(self.device_pixel_ratio as f64),
663            wheel_x_physical_pixel: pointer_sample.scroll_h_physical_pixel,
664            wheel_y_physical_pixel: pointer_sample.scroll_v_physical_pixel,
665            device_id: Some(device_id),
666            ..Default::default()
667        }
668    }
669
670    fn process_mouse_events(&mut self, events: Vec<MouseEvent>) {
671        for mouse_event in events {
672            match &mouse_event {
673                &MouseEvent {
674                    stream_info:
675                        Some(ui_pointer::MouseEventStreamInfo {
676                            status: ui_pointer::MouseViewStatus::Exited,
677                            ..
678                        }),
679                    ..
680                } => return,
681                &MouseEvent {
682                    stream_info:
683                        Some(ui_pointer::MouseEventStreamInfo {
684                            status: ui_pointer::MouseViewStatus::Entered,
685                            ..
686                        }),
687                    ..
688                } => {
689                    let view_parameters =
690                        mouse_event.view_parameters.expect("entered event missing view_parameters");
691                    self.view_parameters = Some(view_parameters);
692                    continue;
693                }
694                _ => {}
695            }
696            let event = self.get_mouse_report(&mouse_event);
697            match &self.mouse_input_listener {
698                Some(listener) => {
699                    listener.report_mouse_input(&event).expect("failed to send mouse input report");
700                }
701                None => {
702                    info!("no mouse event listener");
703                }
704            }
705        }
706    }
707
708    async fn listen_for_mouse_events(
709        this: Rc<RefCell<Self>>,
710        mouse_source: ui_pointer::MouseSourceProxy,
711    ) {
712        loop {
713            let events = mouse_source.watch();
714            match events.await {
715                Ok(events) => {
716                    this.borrow_mut().process_mouse_events(events);
717                }
718                _ => {
719                    info!("MouseSource connection closed");
720                    return;
721                }
722            }
723        }
724    }
725
726    fn get_key_report(
727        &self,
728        key_event: KeyEvent,
729    ) -> Option<test_input::KeyboardInputListenerReportTextInputRequest> {
730        if key_event.type_ != Some(ui_input3::KeyEventType::Pressed) {
731            return None;
732        }
733        match key_event.key_meaning.unwrap() {
734            ui_input3::KeyMeaning::Codepoint(code) => {
735                let s = char::from_u32(code).expect("key event is not a valid char").to_string();
736                info!("Key received {:?}", s);
737                Some(test_input::KeyboardInputListenerReportTextInputRequest {
738                    text: Some(s),
739                    device_id: key_event.device_id,
740                    ..Default::default()
741                })
742            }
743            ui_input3::KeyMeaning::NonPrintableKey(key) => {
744                info!("NonPrintableKey received {:?}", key);
745                Some(test_input::KeyboardInputListenerReportTextInputRequest {
746                    non_printable: Some(key),
747                    device_id: key_event.device_id,
748                    ..Default::default()
749                })
750            }
751        }
752    }
753
754    fn process_key_event(&mut self, event: KeyEvent) {
755        let report = self.get_key_report(event);
756        match &self.keyboard_input_listener {
757            Some(listener) => match report {
758                Some(event) => {
759                    listener
760                        .report_text_input(&event)
761                        .expect("failed to send keyboard input report");
762                }
763                None => {}
764            },
765            None => {
766                info!("no keyboard event listener");
767            }
768        }
769    }
770
771    async fn listen_for_key_events(
772        this: Rc<RefCell<Self>>,
773        keyboard: ui_input3::KeyboardProxy,
774        view_ref: ui_views::ViewRef,
775        keyboard_ready: AsyncEvent,
776    ) {
777        let (keyboard_client, mut keyboard_stream) =
778            create_request_stream::<ui_input3::KeyboardListenerMarker>();
779
780        keyboard
781            .add_listener(view_ref, keyboard_client)
782            .await
783            .expect("failed to add keyboard listener");
784
785        keyboard_ready.signal();
786
787        loop {
788            let listener_request = keyboard_stream.next().await;
789            match listener_request {
790                Some(Ok(ui_input3::KeyboardListenerRequest::OnKeyEvent {
791                    event,
792                    responder,
793                    ..
794                })) => {
795                    responder.send(ui_input3::KeyEventStatus::Handled).expect("send");
796                    this.borrow_mut().process_key_event(event);
797                }
798                _ => {
799                    info!("keyboard connection closed");
800                    return;
801                }
802            }
803        }
804    }
805}