Skip to main content

carnelian/
view.rs

1// Copyright 2018 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::app::MessageInternal;
6use crate::app::strategies::framebuffer::DisplayId;
7use crate::geometry::{IntPoint, Size};
8use crate::input::{self, UserInputMessage};
9use crate::message::Message;
10use crate::render::Context;
11use crate::scene::facets::FacetId;
12use crate::scene::scene::Scene;
13use crate::view::strategies::base::ViewStrategyPtr;
14use crate::{IdFromRaw, MessageTarget};
15use anyhow::{Error, ensure};
16use euclid::size2;
17use fuchsia_framebuffer::ImageId;
18use fuchsia_trace::instant;
19use futures::channel::mpsc::{UnboundedSender, unbounded};
20use std::fmt::{Display, Formatter};
21use zx::{Event, MonotonicInstant};
22
23pub(crate) mod strategies;
24
25#[derive(Debug, Clone)]
26pub struct DisplayInfo {
27    pub id: DisplayId,
28    pub horizontal_size_mm: u32,
29    pub vertical_size_mm: u32,
30    pub using_fallback_size: bool,
31    pub max_layer_count: u32,
32}
33
34impl From<&fidl_fuchsia_hardware_display::Info> for DisplayInfo {
35    fn from(info: &fidl_fuchsia_hardware_display::Info) -> Self {
36        Self {
37            id: info.id.into(),
38            horizontal_size_mm: info.horizontal_size_mm,
39            vertical_size_mm: info.vertical_size_mm,
40            using_fallback_size: info.using_fallback_size,
41            max_layer_count: info.max_layer_count,
42        }
43    }
44}
45
46/// parameter struct passed to setup and update trait methods.
47pub struct ViewAssistantContext {
48    /// A unique key representing this view.
49    pub key: ViewKey,
50    /// The actual number of pixels in the view.
51    pub size: Size,
52    /// A factor representing pixel density. Use to
53    /// calculate sizes for things like fonts.
54    pub metrics: Size,
55    /// For render, the time the rendering will be presented. Currently
56    /// not implemented correctly.
57    pub presentation_time: MonotonicInstant,
58    /// When running in frame buffer mode, the number of buffers in
59    /// the buffer collection
60    pub buffer_count: Option<usize>,
61    /// The ID of the Image being rendered in a buffer in
62    /// preparation for being displayed. Used to keep track
63    /// of what content needs to be rendered for a particular image
64    /// in double or triple buffering configurations.
65    pub image_id: ImageId,
66    /// The index of the buffer in a buffer collection that is
67    /// being used as the contents of the image specified in
68    /// `image_id`.
69    pub image_index: u32,
70    /// Position of the mouse cursor when running directly on the
71    /// display coordinator.
72    pub mouse_cursor_position: Option<IntPoint>,
73    /// information about the hosting display when running directly on the
74    /// display coordinator.
75    pub display_info: Option<DisplayInfo>,
76
77    app_sender: UnboundedSender<MessageInternal>,
78}
79
80impl ViewAssistantContext {
81    /// Returns an empty ViewAssistantContext to enable testing with mocks
82    pub fn new_for_testing() -> Self {
83        let (unbounded_sender, _) = unbounded::<MessageInternal>();
84        Self {
85            key: Default::default(),
86            size: Default::default(),
87            metrics: Default::default(),
88            presentation_time: Default::default(),
89            buffer_count: Default::default(),
90            image_id: Default::default(),
91            image_index: Default::default(),
92            mouse_cursor_position: Default::default(),
93            display_info: Default::default(),
94            app_sender: unbounded_sender,
95        }
96    }
97
98    /// Queue up a message for delivery
99    pub fn queue_message(&mut self, message: Message) {
100        self.app_sender
101            .unbounded_send(MessageInternal::TargetedMessage(
102                MessageTarget::View(self.key),
103                message,
104            ))
105            .expect("ViewAssistantContext::queue_message - unbounded_send");
106    }
107
108    /// Request that a render occur for this view at the next
109    /// appropriate time to render.
110    pub fn request_render(&self) {
111        self.app_sender
112            .unbounded_send(MessageInternal::RequestRender(self.key))
113            .expect("unbounded_send");
114    }
115}
116
117/// Trait that allows Carnelian developers to customize the behavior of views.
118pub trait ViewAssistant {
119    /// This method is called once when a view is created.
120    #[allow(unused_variables)]
121    fn setup(&mut self, context: &ViewAssistantContext) -> Result<(), Error> {
122        Ok(())
123    }
124
125    #[allow(unused_variables)]
126    /// Implement this method to to handle when a view is resized.
127    fn resize(&mut self, new_size: &Size) -> Result<(), Error> {
128        Ok(())
129    }
130
131    #[allow(unused_variables)]
132    /// Implement this method to return a mutable reference to the scene that
133    /// represents the view.
134    fn get_scene(&mut self, size: Size) -> Option<&mut Scene> {
135        None
136    }
137
138    #[allow(unused_variables)]
139    /// Implement this method to return a mutable reference to the scene that
140    /// represents the view. Implement this one if you'll need the various
141    /// contexts to build a scene.
142    fn get_scene_with_contexts(
143        &mut self,
144        render_context: &mut Context,
145        view_context: &ViewAssistantContext,
146    ) -> Option<&mut Scene> {
147        self.get_scene(view_context.size)
148    }
149
150    /// This method is called when a view needs to
151    /// be rendered.
152    fn render(
153        &mut self,
154        render_context: &mut Context,
155        buffer_ready_event: Event,
156        view_context: &ViewAssistantContext,
157    ) -> Result<(), Error> {
158        if let Some(scene) = self.get_scene_with_contexts(render_context, view_context) {
159            scene.layout(view_context.size);
160            scene.render(render_context, buffer_ready_event, view_context)?;
161            if scene.is_animated() {
162                view_context.request_render();
163            }
164            Ok(())
165        } else {
166            anyhow::bail!("Assistant has ViewMode::Render but doesn't implement render or scene.")
167        }
168    }
169
170    /// This method is called when input events come to this view. The default implementation
171    /// calls specific methods for the type of event, so usually one does not need to implement
172    /// this method. Since the default methods for touch and mouse handle the pointer abstraction,
173    /// make sure to call them in an implementation of this method if you wish to use that
174    /// abstraction.
175    fn handle_input_event(
176        &mut self,
177        context: &mut ViewAssistantContext,
178        event: &input::Event,
179    ) -> Result<(), Error> {
180        match &event.event_type {
181            input::EventType::Mouse(mouse_event) => {
182                self.handle_mouse_event(context, event, mouse_event)
183            }
184            input::EventType::Touch(touch_event) => {
185                self.handle_touch_event(context, event, touch_event)
186            }
187            input::EventType::Keyboard(keyboard_event) => {
188                self.handle_keyboard_event(context, event, keyboard_event)
189            }
190            input::EventType::ConsumerControl(consumer_control_event) => {
191                self.handle_consumer_control_event(context, event, consumer_control_event)
192            }
193        }
194    }
195
196    /// This method is called when mouse events come to this view.
197    /// ```no_run
198    /// # use anyhow::Error;
199    /// # use carnelian::{
200    /// #    input::{self},IntPoint,
201    /// #    ViewAssistant, ViewAssistantContext,
202    /// # };
203    /// # struct SampleViewAssistant { mouse_start: IntPoint};
204    /// impl ViewAssistant for SampleViewAssistant {
205    ///     fn handle_mouse_event(
206    ///         &mut self,
207    ///         context: &mut ViewAssistantContext,
208    ///         event: &input::Event,
209    ///         mouse_event: &input::mouse::Event,
210    ///     ) -> Result<(), Error> {
211    ///         match &mouse_event.phase {
212    ///             input::mouse::Phase::Down(button) => {
213    ///                 if button.is_primary() {
214    ///                     self.mouse_start = mouse_event.location
215    ///                 }
216    ///             }
217    ///             input::mouse::Phase::Moved => {}
218    ///             input::mouse::Phase::Up(button) => {
219    ///                 if button.is_primary() {
220    ///                     println!("mouse moved {}", mouse_event.location - self.mouse_start);
221    ///                 }
222    ///             }
223    ///             _ => (),
224    ///         }
225    ///         Ok(())
226    ///     }
227    /// }
228    /// ```
229    fn handle_mouse_event(
230        &mut self,
231        context: &mut ViewAssistantContext,
232        event: &input::Event,
233        mouse_event: &input::mouse::Event,
234    ) -> Result<(), Error> {
235        if self.uses_pointer_events() {
236            if let Some(mouse_event) =
237                input::pointer::Event::new_from_mouse_event(&event.device_id, mouse_event)
238            {
239                self.handle_pointer_event(context, event, &mouse_event)
240            } else {
241                Ok(())
242            }
243        } else {
244            Ok(())
245        }
246    }
247
248    /// This method is called when touch events come to this view.
249    /// ```no_run
250    /// # use anyhow::Error;
251    /// # use carnelian::{
252    /// #    input::{self},IntPoint,
253    /// #    ViewAssistant, ViewAssistantContext,
254    /// # };
255    /// # struct SampleViewAssistant {};
256    /// impl ViewAssistant for SampleViewAssistant {
257    ///     fn handle_touch_event(
258    ///         &mut self,
259    ///         context: &mut ViewAssistantContext,
260    ///         event: &input::Event,
261    ///         touch_event: &input::touch::Event,
262    ///     ) -> Result<(), Error> {
263    ///         for contact in &touch_event.contacts {
264    ///             // Use contact.contact_id as a key to handle
265    ///             // each contact individually
266    ///         }
267    ///         Ok(())
268    ///     }
269    /// }
270    /// ```
271    fn handle_touch_event(
272        &mut self,
273        context: &mut ViewAssistantContext,
274        event: &input::Event,
275        touch_event: &input::touch::Event,
276    ) -> Result<(), Error> {
277        if self.uses_pointer_events() {
278            for contact in &touch_event.contacts {
279                self.handle_pointer_event(
280                    context,
281                    event,
282                    &input::pointer::Event::new_from_contact(contact),
283                )?;
284            }
285            Ok(())
286        } else {
287            Ok(())
288        }
289    }
290
291    /// This method is called when the view desires pointer events and a compatible
292    /// mouse or touch event comes to this view.
293    /// ```no_run
294    /// # use anyhow::Error;
295    /// # use carnelian::{
296    /// #    input::{self},IntPoint,
297    /// #    ViewAssistant, ViewAssistantContext,
298    /// # };
299    /// #    #[derive(Default)]
300    /// #    struct SampleViewAssistant {
301    /// #        mouse_start: IntPoint,
302    /// #        pointer_start: IntPoint,
303    /// #        current_pointer_location: IntPoint,
304    /// #    }
305    /// impl ViewAssistant for SampleViewAssistant {
306    ///     fn handle_pointer_event(
307    ///         &mut self,
308    ///         context: &mut ViewAssistantContext,
309    ///         event: &input::Event,
310    ///         pointer_event: &input::pointer::Event,
311    ///     ) -> Result<(), Error> {
312    ///         match pointer_event.phase {
313    ///             input::pointer::Phase::Down(pointer_location) => {
314    ///                 self.pointer_start = pointer_location
315    ///             }
316    ///             input::pointer::Phase::Moved(pointer_location) => {
317    ///                 self.current_pointer_location = pointer_location;
318    ///             }
319    ///             input::pointer::Phase::Up => {
320    ///                 println!(
321    ///                     "pointer moved {}",
322    ///                     self.current_pointer_location - self.pointer_start
323    ///                 );
324    ///             }
325    ///             _ => (),
326    ///         }
327    ///         Ok(())
328    ///     }
329    /// }
330    /// ```
331    #[allow(unused_variables)]
332    fn handle_pointer_event(
333        &mut self,
334        context: &mut ViewAssistantContext,
335        event: &input::Event,
336        pointer_event: &input::pointer::Event,
337    ) -> Result<(), Error> {
338        Ok(())
339    }
340
341    /// This method is called when keyboard events come to this view.
342    #[allow(unused_variables)]
343    fn handle_keyboard_event(
344        &mut self,
345        context: &mut ViewAssistantContext,
346        event: &input::Event,
347        keyboard_event: &input::keyboard::Event,
348    ) -> Result<(), Error> {
349        Ok(())
350    }
351
352    /// This method is called when consumer control events come to this view.
353    #[allow(unused_variables)]
354    fn handle_consumer_control_event(
355        &mut self,
356        context: &mut ViewAssistantContext,
357        event: &input::Event,
358        consumer_control_event: &input::consumer_control::Event,
359    ) -> Result<(), Error> {
360        Ok(())
361    }
362
363    /// This method is called when focus events come from Scenic to this view. It will be
364    /// called once when a Carnelian app is running directly on the frame buffer, as such
365    /// views are always focused. See the button sample for an one way to respond to focus.
366    #[allow(unused_variables)]
367    fn handle_focus_event(
368        &mut self,
369        context: &mut ViewAssistantContext,
370        focused: bool,
371    ) -> Result<(), Error> {
372        Ok(())
373    }
374
375    /// This method is called when `App::send_message` is called with the associated
376    /// view controller's `ViewKey` and the view controller does not handle the message.
377    /// ```no_run
378    /// # use anyhow::Error;
379    /// # use carnelian::{
380    /// #     input::{self},
381    /// #     IntPoint, Message, ViewAssistant, ViewAssistantContext,
382    /// # };
383    /// # #[derive(Default)]
384    /// # struct SampleViewAssistant {}
385    /// use zx::MonotonicInstant;
386    /// pub enum SampleMessages {
387    ///     Pressed(MonotonicInstant),
388    /// }
389    /// impl ViewAssistant for SampleViewAssistant {
390    ///     fn handle_message(&mut self, message: Message) {
391    ///         if let Some(sample_message) = message.downcast_ref::<SampleMessages>() {
392    ///             match sample_message {
393    ///                 SampleMessages::Pressed(value) => {
394    ///                     println!("value = {:#?}", value);
395    ///                 }
396    ///             }
397    ///         }
398    ///     }
399    /// }
400    /// ```
401    #[allow(unused_variables)]
402    fn handle_message(&mut self, message: Message) {}
403
404    /// Whether this view wants touch and mouse events abstracted as
405    /// [`input::pointer::Event`](./input/pointer/struct.Event.html). Defaults to true.
406    fn uses_pointer_events(&self) -> bool {
407        true
408    }
409
410    /// This method is called when running directly on the display and the ownership
411    /// of the display changes.
412    fn ownership_changed(&mut self, _owned: bool) -> Result<(), Error> {
413        Ok(())
414    }
415
416    /// This method is called after setup to get an offset to use when calculating
417    /// render time. It is only called once.
418    fn get_render_offset(&mut self) -> Option<i64> {
419        None
420    }
421}
422
423/// Reference to a view assistant. _This type is likely to change in the future so
424/// using this type alias might make for easier forward migration._
425pub type ViewAssistantPtr = Box<dyn ViewAssistant>;
426
427/// Key identifying a view.
428#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
429pub struct ViewKey(pub u64);
430
431impl IdFromRaw for ViewKey {
432    fn from_raw(id: u64) -> ViewKey {
433        ViewKey(id)
434    }
435}
436
437impl Display for ViewKey {
438    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
439        write!(f, "ViewKey({})", self.0)
440    }
441}
442
443#[derive(Debug)]
444pub(crate) struct ViewDetails {
445    key: ViewKey,
446    metrics: Size,
447    physical_size: Size,
448    logical_size: Size,
449}
450
451/// This struct takes care of all the boilerplate needed for implementing a Fuchsia
452/// view, forwarding the interesting implementation points to a struct implementing
453/// the `ViewAssistant` trait.
454pub(crate) struct ViewController {
455    key: ViewKey,
456    assistant: ViewAssistantPtr,
457    metrics: Size,
458    physical_size: Size,
459    logical_size: Size,
460    render_requested: bool,
461    strategy: ViewStrategyPtr,
462    app_sender: UnboundedSender<MessageInternal>,
463}
464
465impl ViewController {
466    pub async fn new_with_strategy(
467        key: ViewKey,
468        view_assistant: ViewAssistantPtr,
469        strategy: ViewStrategyPtr,
470        app_sender: UnboundedSender<MessageInternal>,
471    ) -> Result<ViewController, Error> {
472        let metrics = strategy.initial_metrics();
473        let physical_size = strategy.initial_physical_size();
474        let logical_size = strategy.initial_logical_size();
475
476        let mut view_controller = ViewController {
477            key,
478            metrics,
479            physical_size,
480            logical_size,
481            render_requested: true,
482            assistant: view_assistant,
483            strategy,
484            app_sender,
485        };
486
487        view_controller
488            .strategy
489            .setup(&view_controller.make_view_details(), &mut view_controller.assistant);
490
491        Ok(view_controller)
492    }
493
494    fn make_view_details(&self) -> ViewDetails {
495        ViewDetails {
496            key: self.key,
497            metrics: self.metrics,
498            physical_size: self.physical_size,
499            logical_size: self.logical_size,
500        }
501    }
502
503    /// Informs Scenic of any changes made to this View's |Session|.
504    pub fn present(&mut self) {
505        self.strategy.present(&self.make_view_details());
506    }
507
508    pub fn send_update_message(&mut self) {
509        self.app_sender.unbounded_send(MessageInternal::Render(self.key)).expect("unbounded_send");
510    }
511
512    pub fn ownership_changed(&mut self, owned: bool) {
513        self.strategy.ownership_changed(owned);
514        self.assistant
515            .ownership_changed(owned)
516            .unwrap_or_else(|e| println!("ownership_changed error: {}", e));
517    }
518
519    pub fn drop_display_resources(&mut self) {
520        self.strategy.drop_display_resources();
521    }
522
523    pub fn request_render(&mut self) {
524        self.render_requested = true;
525        self.strategy.render_requested();
526    }
527
528    pub async fn render(&mut self) {
529        if self.render_requested {
530            // Recompute our physical size based on the provided logical size and screen metrics.
531            self.physical_size = size2(
532                self.logical_size.width * self.metrics.width,
533                self.logical_size.height * self.metrics.height,
534            );
535
536            if self.strategy.render(&self.make_view_details(), &mut self.assistant).await {
537                self.render_requested = false;
538            }
539            self.present();
540        }
541    }
542
543    pub fn present_done(&mut self, info: fidl_fuchsia_scenic_scheduling::FramePresentedInfo) {
544        self.strategy.present_done(&self.make_view_details(), &mut self.assistant, info);
545    }
546
547    pub fn handle_metrics_changed(&mut self, metrics: Size) {
548        if self.metrics != metrics {
549            instant!(
550                c"gfx",
551                c"ViewController::metrics_changed",
552                fuchsia_trace::Scope::Process,
553                "old_device_pixel_ratio_x" => self.metrics.width as f64,
554                "old_device_pixel_ratio_y" => self.metrics.height as f64,
555                "new_device_pixel_ratio_x" => metrics.width as f64,
556                "new_device_pixel_ratio_y" => metrics.height as f64
557            );
558            self.metrics = metrics;
559            self.render_requested = true;
560            self.send_update_message();
561        }
562    }
563
564    pub fn handle_size_changed(&mut self, new_size: Size) {
565        if self.logical_size != new_size {
566            instant!(
567                c"gfx",
568                c"ViewController::size_changed",
569                fuchsia_trace::Scope::Process,
570                "old_logical_width" => self.logical_size.width as f64,
571                "old_logical_height" => self.logical_size.height as f64,
572                "new_logical_width" => new_size.width as f64,
573                "new_logical_height" => new_size.height as f64
574            );
575            self.logical_size = new_size;
576            self.assistant
577                .resize(&new_size)
578                .unwrap_or_else(|e| println!("handle_size_changed error: {}", e));
579            self.render_requested = true;
580            self.send_update_message();
581        }
582    }
583
584    pub fn focus(&mut self, focus: bool) {
585        self.strategy.handle_focus(&self.make_view_details(), &mut self.assistant, focus);
586    }
587
588    fn handle_input_events_internal(
589        &mut self,
590        view_details: &ViewDetails,
591        events: Vec<input::Event>,
592    ) -> Result<(), Error> {
593        let mut view_assistant_context = self.strategy.create_view_assistant_context(&view_details);
594        for event in events {
595            self.strategy.inspect_event(view_details, &event);
596            self.assistant.handle_input_event(&mut view_assistant_context, &event)?;
597        }
598        Ok(())
599    }
600
601    pub fn handle_user_input_message(
602        &mut self,
603        user_input_message: UserInputMessage,
604    ) -> Result<(), Error> {
605        let view_details = self.make_view_details();
606        let events = self.strategy.convert_user_input_message(&view_details, user_input_message)?;
607        self.handle_input_events_internal(&view_details, events)?;
608        Ok(())
609    }
610
611    /// Handle input events that have been converted to Carnelian's format
612    pub fn handle_input_events(&mut self, events: Vec<input::Event>) -> Result<(), Error> {
613        let view_details = self.make_view_details();
614        self.handle_input_events_internal(&view_details, events)?;
615        Ok(())
616    }
617
618    /// This method sends an arbitrary message to this view to be
619    /// forwarded to the view assistant.
620    pub fn send_message(&mut self, msg: Message) {
621        self.assistant.handle_message(msg);
622    }
623
624    /// This method sends an arbitrary message to this view to be
625    /// forwarded to the view assistant.
626    pub fn send_facet_message(&mut self, facet_id: FacetId, msg: Message) -> Result<(), Error> {
627        let scene = self.assistant.get_scene(self.physical_size);
628        ensure!(scene.is_some(), "send_facet_message called on view not providing a scene");
629        let scene = scene.unwrap();
630        scene.send_message(&facet_id, msg);
631        Ok(())
632    }
633
634    pub fn image_freed(&mut self, image_id: u64, collection_id: u32) {
635        self.strategy.image_freed(image_id, collection_id);
636    }
637
638    pub fn handle_on_next_frame_begin(
639        &mut self,
640        info: &fidl_fuchsia_ui_composition::OnNextFrameBeginValues,
641    ) {
642        self.strategy.handle_on_next_frame_begin(info);
643    }
644
645    pub async fn handle_display_coordinator_listener_request(
646        &mut self,
647        event: fidl_fuchsia_hardware_display::CoordinatorListenerRequest,
648    ) {
649        self.strategy.handle_display_coordinator_listener_request(event).await;
650    }
651
652    pub fn is_hosted_on_display(&self, display_id: DisplayId) -> bool {
653        self.strategy.is_hosted_on_display(display_id)
654    }
655
656    pub fn close(&mut self) {
657        self.strategy.close();
658    }
659}