blob: 20a2bcaf6a438f1046b2362772b57ca80d973767 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#[cfg(feature = "debug_console")]
use crate::ConsoleMessages;
use crate::input::{self};
use std::collections::VecDeque;
use anyhow::Error;
use carnelian::render::Context;
use carnelian::{Message, Size, ViewAssistant, ViewAssistantContext, ViewAssistantPtr};
use fuchsia_zircon::Event;
use mockall::predicate::*;
use mockall::*;
pub enum ProxyMessages {
#[allow(unused)]
NewViewAssistant(Option<ViewAssistantPtr>),
#[allow(unused)]
PopViewAssistant,
}
pub struct ProxyViewAssistant {
#[cfg(feature = "debug_console")]
console_view_assistant: ViewAssistantPtr,
view_assistant_stack: VecDeque<ViewAssistantPtr>,
first_call_after_switch: bool,
#[cfg(feature = "debug_console")]
console_active: bool,
}
impl ProxyViewAssistant {
pub fn new(
#[cfg(feature = "debug_console")] console_view_assistant: ViewAssistantPtr,
view_assistant_ptr: ViewAssistantPtr,
) -> Result<ProxyViewAssistant, Error> {
let mut view_assistant_stack = VecDeque::new();
view_assistant_stack.push_front(view_assistant_ptr);
Ok(ProxyViewAssistant {
#[cfg(feature = "debug_console")]
console_view_assistant,
view_assistant_stack,
first_call_after_switch: true,
#[cfg(feature = "debug_console")]
console_active: false,
})
}
/// Returns true if event should toggle display of Console (and consider the event consumed).
#[cfg(feature = "debug_console")]
fn should_console_toggle(&mut self, event: &input::Event) -> bool {
let mut toggle_requested = false;
match &event.event_type {
input::EventType::Touch(touch_event) => {
for contact in &touch_event.contacts {
let pointer_event = &input::pointer::Event::new_from_contact(contact);
match pointer_event.phase {
input::pointer::Phase::Down(location) => {
if location.x < 100 && location.y < 100 {
toggle_requested = true;
break;
}
}
_ => {}
};
}
}
_ => {}
}
toggle_requested
}
}
#[automock]
impl ViewAssistant for ProxyViewAssistant {
fn setup(&mut self, context: &ViewAssistantContext) -> Result<(), Error> {
self.view_assistant_stack.front_mut().unwrap().setup(context)
}
fn resize(&mut self, new_size: &Size) -> Result<(), Error> {
self.view_assistant_stack.front_mut().unwrap().resize(new_size)
}
fn render(
&mut self,
render_context: &mut Context,
buffer_ready_event: Event,
view_context: &ViewAssistantContext,
) -> Result<(), Error> {
#[cfg(feature = "debug_console")]
if self.console_active {
return self.console_view_assistant.as_mut().render(
render_context,
buffer_ready_event,
view_context,
);
}
self.view_assistant_stack.front_mut().unwrap().render(
render_context,
buffer_ready_event,
view_context,
)
}
fn handle_input_event(
&mut self,
context: &mut ViewAssistantContext,
event: &input::Event,
) -> Result<(), Error> {
#[cfg(feature = "debug_console")]
if self.should_console_toggle(event) {
self.console_active = !self.console_active;
return Ok(()); // Consume the console-toggling event.
}
#[cfg(feature = "debug_console")]
if self.console_active {
return Ok(()); // Consume the event when the console is active.
}
if self.first_call_after_switch {
self.first_call_after_switch = false;
self.handle_focus_event(context, true)?;
}
self.view_assistant_stack.front_mut().unwrap().handle_input_event(context, event)
}
fn handle_mouse_event(
&mut self,
context: &mut ViewAssistantContext,
event: &input::Event,
mouse_event: &input::mouse::Event,
) -> Result<(), Error> {
self.view_assistant_stack.front_mut().unwrap().handle_mouse_event(
context,
event,
mouse_event,
)
}
fn handle_touch_event(
&mut self,
context: &mut ViewAssistantContext,
event: &input::Event,
touch_event: &input::touch::Event,
) -> Result<(), Error> {
self.view_assistant_stack.front_mut().unwrap().handle_touch_event(
context,
event,
touch_event,
)
}
fn handle_pointer_event(
&mut self,
context: &mut ViewAssistantContext,
event: &input::Event,
pointer_event: &input::pointer::Event,
) -> Result<(), Error> {
if self.first_call_after_switch {
self.first_call_after_switch = false;
self.handle_focus_event(context, true)?;
}
self.view_assistant_stack.front_mut().unwrap().handle_pointer_event(
context,
event,
pointer_event,
)
}
fn handle_keyboard_event(
&mut self,
context: &mut ViewAssistantContext,
event: &input::Event,
keyboard_event: &input::keyboard::Event,
) -> Result<(), Error> {
self.view_assistant_stack.front_mut().unwrap().handle_keyboard_event(
context,
event,
keyboard_event,
)
}
fn handle_consumer_control_event(
&mut self,
context: &mut ViewAssistantContext,
event: &input::Event,
consumer_control_event: &input::consumer_control::Event,
) -> Result<(), Error> {
self.view_assistant_stack.front_mut().unwrap().handle_consumer_control_event(
context,
event,
consumer_control_event,
)
}
fn handle_focus_event(
&mut self,
context: &mut ViewAssistantContext,
focused: bool,
) -> Result<(), Error> {
self.view_assistant_stack.front_mut().unwrap().handle_focus_event(context, focused)
}
fn handle_message(&mut self, message: Message) {
if message.is::<ProxyMessages>() {
let proxy_message = message.downcast::<ProxyMessages>().unwrap();
match *proxy_message {
ProxyMessages::NewViewAssistant(mut view_assistant_ptr) => {
let view_assistant_ptr =
std::mem::replace(&mut view_assistant_ptr, None).unwrap();
self.first_call_after_switch = true;
self.view_assistant_stack.push_front(view_assistant_ptr);
}
ProxyMessages::PopViewAssistant => {
if self.view_assistant_stack.len() > 1 {
self.first_call_after_switch = true;
self.view_assistant_stack.pop_front();
}
}
}
return;
}
if cfg!(feature = "debug_console") {
#[cfg(feature = "debug_console")]
if message.is::<ConsoleMessages>() {
self.console_view_assistant.as_mut().handle_message(message);
return;
}
}
self.view_assistant_stack.front_mut().unwrap().handle_message(message);
}
fn uses_pointer_events(&self) -> bool {
self.view_assistant_stack.front().unwrap().uses_pointer_events()
}
fn ownership_changed(&mut self, _owned: bool) -> Result<(), Error> {
self.view_assistant_stack.front_mut().unwrap().ownership_changed(_owned)
}
fn get_render_offset(&mut self) -> Option<i64> {
self.view_assistant_stack.front_mut().unwrap().get_render_offset()
}
}
#[cfg(test)]
mod tests {
use super::*;
use carnelian::make_message;
enum MockMessageType {
MockMessage,
}
fn get_input_event() -> input::Event {
let mouse_event = input::mouse::Event {
buttons: Default::default(),
phase: input::mouse::Phase::Moved,
location: Default::default(),
};
input::Event {
event_time: 0,
device_id: Default::default(),
event_type: input::EventType::Mouse(mouse_event),
}
}
#[test]
fn test_proxy_view_assistant_switching() -> std::result::Result<(), anyhow::Error> {
#[cfg(feature = "debug_console")]
let mock0 = MockProxyViewAssistant::new();
let mut mock1 = MockProxyViewAssistant::new();
mock1.expect_handle_message().times(2).return_const(());
let mut mock2 = MockProxyViewAssistant::new();
mock2.expect_handle_message().times(1).return_const(());
let mut proxy = ProxyViewAssistant::new(
#[cfg(feature = "debug_console")]
Box::new(mock0),
Box::new(mock1),
)
.unwrap();
assert_eq!(proxy.view_assistant_stack.len(), 1);
proxy.handle_message(make_message(MockMessageType::MockMessage));
proxy.handle_message(make_message(ProxyMessages::NewViewAssistant(Some(Box::new(mock2)))));
assert_eq!(proxy.view_assistant_stack.len(), 2);
proxy.handle_message(make_message(MockMessageType::MockMessage));
proxy.handle_message(make_message(ProxyMessages::PopViewAssistant));
assert_eq!(proxy.view_assistant_stack.len(), 1);
proxy.handle_message(make_message(MockMessageType::MockMessage));
Ok(())
}
#[test]
fn test_proxy_view_assistant_pop_first_entry() -> std::result::Result<(), anyhow::Error> {
#[cfg(feature = "debug_console")]
let mock0 = MockProxyViewAssistant::new();
let mut mock1 = MockProxyViewAssistant::new();
mock1.expect_handle_message().times(0).return_const(());
let mut proxy = ProxyViewAssistant::new(
#[cfg(feature = "debug_console")]
Box::new(mock0),
Box::new(mock1),
)
.unwrap();
assert_eq!(proxy.view_assistant_stack.len(), 1);
proxy.handle_message(make_message(ProxyMessages::PopViewAssistant));
assert_eq!(proxy.view_assistant_stack.len(), 1);
Ok(())
}
#[test]
fn test_proxy_view_assistant_first_call_pointer_event() -> std::result::Result<(), anyhow::Error>
{
let context = &mut ViewAssistantContext::new_for_testing();
#[cfg(feature = "debug_console")]
let mock0 = MockProxyViewAssistant::new();
let mut mock1 = MockProxyViewAssistant::new();
mock1.expect_handle_pointer_event().times(2).returning(|_, _, _| Ok(()));
mock1.expect_handle_focus_event().times(1).returning(|_, _| Ok(()));
let mut proxy = ProxyViewAssistant::new(
#[cfg(feature = "debug_console")]
Box::new(mock0),
Box::new(mock1),
)
.unwrap();
let event = get_input_event();
let pointer_id =
input::pointer::PointerId::Mouse(input::DeviceId("Mouse Event".to_owned()));
let pointer_event =
input::pointer::Event { phase: input::pointer::Phase::Up, pointer_id: pointer_id };
assert_eq!(proxy.first_call_after_switch, true);
proxy.handle_pointer_event(context, &event, &pointer_event)?;
assert_eq!(proxy.first_call_after_switch, false);
proxy.handle_pointer_event(context, &event, &pointer_event)?;
assert_eq!(proxy.first_call_after_switch, false);
Ok(())
}
#[test]
fn test_proxy_view_assistant_first_call_input_event() -> std::result::Result<(), anyhow::Error>
{
let context = &mut ViewAssistantContext::new_for_testing();
#[cfg(feature = "debug_console")]
let mock0 = MockProxyViewAssistant::new();
let mut mock1 = MockProxyViewAssistant::new();
mock1.expect_handle_input_event().times(2).returning(|_, _| Ok(()));
mock1.expect_handle_focus_event().times(1).returning(|_, _| Ok(()));
let mut proxy = ProxyViewAssistant::new(
#[cfg(feature = "debug_console")]
Box::new(mock0),
Box::new(mock1),
)
.unwrap();
let event = get_input_event();
assert_eq!(proxy.first_call_after_switch, true);
proxy.handle_input_event(context, &event)?;
assert_eq!(proxy.first_call_after_switch, false);
proxy.handle_input_event(context, &event)?;
assert_eq!(proxy.first_call_after_switch, false);
Ok(())
}
#[test]
fn test_proxy_view_assistant_first_call_input_pointer_event(
) -> std::result::Result<(), anyhow::Error> {
let context = &mut ViewAssistantContext::new_for_testing();
#[cfg(feature = "debug_console")]
let mock0 = MockProxyViewAssistant::new();
let mut mock1 = MockProxyViewAssistant::new();
mock1.expect_handle_pointer_event().times(1).returning(|_, _, _| Ok(()));
mock1.expect_handle_input_event().times(1).returning(|_, _| Ok(()));
mock1.expect_handle_focus_event().times(1).returning(|_, _| Ok(()));
let mut proxy = ProxyViewAssistant::new(
#[cfg(feature = "debug_console")]
Box::new(mock0),
Box::new(mock1),
)
.unwrap();
let event = get_input_event();
let pointer_id =
input::pointer::PointerId::Mouse(input::DeviceId("Mouse Event".to_owned()));
let pointer_event =
input::pointer::Event { phase: input::pointer::Phase::Up, pointer_id: pointer_id };
assert_eq!(proxy.first_call_after_switch, true);
proxy.handle_pointer_event(context, &event, &pointer_event)?;
assert_eq!(proxy.first_call_after_switch, false);
proxy.handle_input_event(context, &event)?;
assert_eq!(proxy.first_call_after_switch, false);
Ok(())
}
}