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