blob: d4ddfe2faaced6b0a26104d7ba37652525a26d43 [file] [log] [blame]
// Copyright 2020 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.
use {
anyhow::{Context as _, Error},
fidl::client::QueryResponseFut,
fidl_fuchsia_input as input, fidl_fuchsia_ui_focus as ui_focus,
fidl_fuchsia_ui_input2 as ui_input2, fidl_fuchsia_ui_input3 as ui_input3,
fidl_fuchsia_ui_shortcut as ui_shortcut, fidl_fuchsia_ui_views as ui_views,
fuchsia_component::client::connect_to_service,
fuchsia_scenic as scenic,
futures::StreamExt,
std::sync::Once,
};
static START: Once = Once::new();
/// Creates instances of FIDL fuchsia.ui.shortcut.Shortcut.
pub struct ShortcutBuilder {
shortcut: ui_shortcut::Shortcut,
}
#[allow(dead_code)]
impl ShortcutBuilder {
pub fn new() -> Self {
Self {
shortcut: ui_shortcut::Shortcut {
id: None,
modifiers: None,
key: None,
use_priority: None,
trigger: None,
key3: None,
keys_required: None,
..ui_shortcut::Shortcut::EMPTY
},
}
}
/// Creates a new instance of fuchsia.ui.shortcut.Shortcut.
pub fn build(&self) -> ui_shortcut::Shortcut {
ui_shortcut::Shortcut {
id: self.shortcut.id,
modifiers: self.shortcut.modifiers,
key: self.shortcut.key,
use_priority: self.shortcut.use_priority,
trigger: self.shortcut.trigger,
key3: self.shortcut.key3,
keys_required: self.shortcut.keys_required.clone(),
..ui_shortcut::Shortcut::EMPTY
}
}
pub fn set_id(mut self, id: u32) -> Self {
self.shortcut.id = Some(id);
self
}
pub fn set_modifiers(mut self, modifiers: ui_input2::Modifiers) -> Self {
self.shortcut.modifiers = Some(modifiers);
self
}
pub fn set_key(mut self, key: ui_input2::Key) -> Self {
self.shortcut.key = Some(key);
self
}
pub fn set_use_priority(mut self, use_priority: bool) -> Self {
self.shortcut.use_priority = Some(use_priority);
self
}
pub fn set_trigger(mut self, trigger: ui_shortcut::Trigger) -> Self {
self.shortcut.trigger = Some(trigger);
self
}
pub fn set_key3(mut self, key3: input::Key) -> Self {
self.shortcut.key3 = Some(key3);
self
}
pub fn set_keys_required(mut self, keys_required: Vec<input::Key>) -> Self {
self.shortcut.keys_required = Some(keys_required);
self
}
}
/// Test helper for FIDL fuchsia.ui.shortcut.Registry service.
pub struct RegistryService {
pub view_ref: ui_views::ViewRef,
pub registry: ui_shortcut::RegistryProxy,
pub listener: ui_shortcut::ListenerRequestStream,
}
impl RegistryService {
/// Creates the instance of the test helper and connects to shortcut registry service.
pub async fn new() -> Result<Self, Error> {
START.call_once(|| {
fuchsia_syslog::init_with_tags(&["shortcut"])
.expect("shortcut syslog init should not fail");
});
let registry = connect_to_service::<ui_shortcut::RegistryMarker>()
.context("Failed to connect to Shortcut registry service")?;
let (listener_client_end, listener) =
fidl::endpoints::create_request_stream::<ui_shortcut::ListenerMarker>()?;
// Set listener and view ref.
let view_ref = scenic::ViewRefPair::new()?.view_ref;
registry
.set_view(&mut fuchsia_scenic::duplicate_view_ref(&view_ref)?, listener_client_end)?;
Ok(Self { registry, listener, view_ref })
}
/// Registers a new shortcut with the shortcut registry service.
/// Returns a future that resolves to the FIDL response.
pub async fn register_shortcut(&self, shortcut: ui_shortcut::Shortcut) -> Result<(), Error> {
self.registry.register_shortcut(shortcut).check()?.await.map_err(Into::into)
}
/// Expects next FIDL request from Registry service to be a shortcut activation.
/// `handler` is called to handle the shortcut activation.
/// Return value from handler is routed to the shortcut registry service.
pub async fn handle_shortcut_activation<HandleFunc>(&mut self, mut handler: HandleFunc)
where
HandleFunc: FnMut(u32) -> bool,
{
if let Some(Ok(ui_shortcut::ListenerRequest::OnShortcut { id, responder, .. })) =
self.listener.next().await
{
responder.send(handler(id)).expect("responding from shortcut listener")
} else {
panic!("Error from listener.next() on shortcut activation");
};
}
}
/// Test helper for FIDL fuchsia.ui.shortcut.Manager service.
pub struct ManagerService {
manager: ui_shortcut::ManagerProxy,
}
#[allow(dead_code)]
impl ManagerService {
/// Creates the instance of the test helper and connects to shortcut manager service.
pub async fn new() -> Result<Self, Error> {
START.call_once(|| {
fuchsia_syslog::init_with_tags(&["shortcut"])
.expect("shortcut syslog init should not fail");
});
let manager = connect_to_service::<ui_shortcut::ManagerMarker>()
.context("Failed to connect to Shortcut manager service")?;
Ok(Self { manager })
}
/// Emulates a key press event using input2 interface.
/// Returns a future that resolves to a FIDL response from manager service.
pub fn press_key2(
&self,
key: ui_input2::Key,
modifiers: Option<ui_input2::Modifiers>,
) -> QueryResponseFut<bool> {
// Process key event that triggers a shortcut.
let event = ui_input2::KeyEvent {
key: Some(key),
modifiers: modifiers,
phase: Some(ui_input2::KeyEventPhase::Pressed),
physical_key: None,
semantic_key: None,
..ui_input2::KeyEvent::EMPTY
};
self.manager.handle_key_event(event)
}
/// Emulates a key release event using input2 interface.
/// Returns a future that resolves to a FIDL response from manager service.
pub fn release_key2(
&self,
key: ui_input2::Key,
modifiers: Option<ui_input2::Modifiers>,
) -> QueryResponseFut<bool> {
// Process key event that triggers a shortcut.
let event = ui_input2::KeyEvent {
key: Some(key),
modifiers: modifiers,
phase: Some(ui_input2::KeyEventPhase::Released),
physical_key: None,
semantic_key: None,
..ui_input2::KeyEvent::EMPTY
};
self.manager.handle_key_event(event)
}
/// Emulates a key press event using input3 interface.
/// Returns a future that resolves to a FIDL response from manager service.
pub async fn press_key3(&self, key: input::Key) -> Result<bool, Error> {
// Process key event that triggers a shortcut.
let event = ui_input3::KeyEvent {
timestamp: None,
type_: Some(ui_input3::KeyEventType::Pressed),
key: Some(key),
modifiers: None,
..ui_input3::KeyEvent::EMPTY
};
self.manager.handle_key3_event(event).check()?.await.map_err(Into::into)
}
/// Emulates a key release event using input3 interface.
/// Returns a future that resolves to a FIDL response from manager service.
pub async fn release_key3(&self, key: input::Key) -> Result<bool, Error> {
// Process key event that triggers a shortcut.
let event = ui_input3::KeyEvent {
timestamp: None,
type_: Some(ui_input3::KeyEventType::Released),
key: Some(key),
modifiers: None,
..ui_input3::KeyEvent::EMPTY
};
self.manager.handle_key3_event(event).check()?.await.map_err(Into::into)
}
/// Emulates multiple key press events sequentially using input3 interface.
/// Returns `Ok(true)` if any of the keys were handled.
pub async fn press_multiple_key3(&self, keys: Vec<input::Key>) -> Result<bool, Error> {
let mut was_handled = false;
let mut iter = keys.into_iter().peekable();
while let Some(key) = iter.next() {
let event = ui_input3::KeyEvent {
timestamp: None,
type_: Some(ui_input3::KeyEventType::Pressed),
key: Some(key),
modifiers: None,
..ui_input3::KeyEvent::EMPTY
};
let key_handled = self.manager.handle_key3_event(event).check()?.await?;
if key_handled && iter.peek().is_some() {
panic!("Shortcuts activated, but unused keys remained in the sequence!");
}
was_handled = was_handled || key_handled;
}
Ok(was_handled)
}
/// Emulates multiple key release events sequentially using input3 interface.
/// Returns `Ok(true)` if any of the keys were handled.
pub async fn release_multiple_key3(&self, keys: Vec<input::Key>) -> Result<bool, Error> {
let mut was_handled = false;
for key in keys.into_iter() {
let event = ui_input3::KeyEvent {
timestamp: None,
type_: Some(ui_input3::KeyEventType::Released),
key: Some(key),
modifiers: None,
..ui_input3::KeyEvent::EMPTY
};
let key_handled = self.manager.handle_key3_event(event).check()?.await?;
was_handled = was_handled || key_handled;
}
Ok(was_handled)
}
pub async fn set_focus_chain(&self, focus_chain: Vec<&ui_views::ViewRef>) -> Result<(), Error> {
let focus_chain = ui_focus::FocusChain {
focus_chain: Some(
focus_chain
.into_iter()
.map(scenic::duplicate_view_ref)
.collect::<Result<Vec<_>, _>>()?,
),
..ui_focus::FocusChain::EMPTY
};
self.manager.handle_focus_change(focus_chain).check()?.await.map_err(Into::into)
}
}