blob: 3e593c808baa1bcba13c3c72b34638d2962ab69d [file] [log] [blame]
// Copyright 2019 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 {
crate::mouse_pointer_hack::*,
crate::pointer_hack_server::PointerHackServer,
crate::touch_pointer_hack::*,
anyhow::{Context, Error},
fidl_fuchsia_input_injection::InputDeviceRegistryRequestStream,
fidl_fuchsia_ui_shortcut as ui_shortcut, fuchsia_async as fasync,
fuchsia_component::client::connect_to_service,
fuchsia_syslog::fx_log_warn,
futures::StreamExt,
input::{
ime_handler::ImeHandler,
input_device,
input_handler::InputHandler,
input_pipeline::{self, InputPipeline},
mouse_handler::MouseHandler,
shortcut_handler::ShortcutHandler,
touch_handler::TouchHandler,
Position, Size,
},
scene_management::{self, FlatSceneManager, SceneManager, ScreenCoordinates},
};
/// Begins handling input events. The returned future will complete when
/// input events are no longer being handled.
///
/// # Parameters
/// - `scene_manager`: The scene manager used by the session.
/// - `pointer_hack_server`: The pointer hack server, used to fetch listeners for pointer
/// hack input handlers.
pub async fn handle_input(
scene_manager: FlatSceneManager,
pointer_hack_server: &PointerHackServer,
input_device_registry_request_stream_receiver: futures::channel::mpsc::UnboundedReceiver<
InputDeviceRegistryRequestStream,
>,
) -> Result<(), Error> {
let input_pipeline = InputPipeline::new(
vec![
input_device::InputDeviceType::Mouse,
input_device::InputDeviceType::Touch,
input_device::InputDeviceType::Keyboard,
],
input_handlers(scene_manager, pointer_hack_server).await,
)
.await
.context("Failed to create InputPipeline.")?;
let input_device_registry_fut = handle_input_device_registry_request_streams(
input_device_registry_request_stream_receiver,
input_pipeline.input_device_types.clone(),
input_pipeline.input_event_sender.clone(),
input_pipeline.input_device_bindings.clone(),
);
let input_pipeline_fut = input_pipeline.handle_input_events();
futures::join!(input_device_registry_fut, input_pipeline_fut);
Ok(())
}
async fn input_handlers(
scene_manager: FlatSceneManager,
pointer_hack_server: &PointerHackServer,
) -> Vec<Box<dyn InputHandler>> {
let mut handlers: Vec<Box<dyn InputHandler>> = vec![];
// Touch and mouse hack handlers are inserted first.
add_touch_hack(&scene_manager, &pointer_hack_server, &mut handlers).await;
add_mouse_hack(&scene_manager, &pointer_hack_server, &mut handlers).await;
// Shortcut needs to go before IME.
add_shortcut_handler(&mut handlers).await;
add_ime(&scene_manager, &mut handlers).await;
add_touch_handler(&scene_manager, &mut handlers).await;
add_mouse_handler(scene_manager, &mut handlers).await;
handlers
}
async fn add_shortcut_handler(handlers: &mut Vec<Box<dyn InputHandler>>) {
if let Ok(manager) = connect_to_service::<ui_shortcut::ManagerMarker>() {
if let Ok(shortcut_handler) = ShortcutHandler::new(manager) {
handlers.push(Box::new(shortcut_handler));
}
}
}
async fn add_ime(scene_manager: &FlatSceneManager, handlers: &mut Vec<Box<dyn InputHandler>>) {
if let Ok(ime_handler) =
ImeHandler::new(scene_manager.session.clone(), scene_manager.compositor_id).await
{
handlers.push(Box::new(ime_handler));
}
}
async fn add_touch_handler(
scene_manager: &FlatSceneManager,
handlers: &mut Vec<Box<dyn InputHandler>>,
) {
let (width_pixels, height_pixels) = scene_manager.display_size.pixels();
if let Ok(touch_handler) = TouchHandler::new(
scene_manager.session.clone(),
scene_manager.compositor_id,
Size { width: width_pixels, height: height_pixels },
)
.await
{
handlers.push(Box::new(touch_handler));
}
}
async fn add_mouse_handler(
mut scene_manager: FlatSceneManager,
handlers: &mut Vec<Box<dyn InputHandler>>,
) {
let (sender, mut receiver) = futures::channel::mpsc::channel(0);
let (width_pixels, height_pixels) = scene_manager.display_size.pixels();
let mouse_handler = MouseHandler::new(
Position { x: width_pixels, y: height_pixels },
Some(sender),
scene_manager.session.clone(),
scene_manager.compositor_id,
);
handlers.push(Box::new(mouse_handler));
fasync::Task::spawn(async move {
while let Some(Position { x, y }) = receiver.next().await {
let screen_coordinates =
ScreenCoordinates::from_pixels(x, y, scene_manager.display_metrics);
scene_manager.set_cursor_location(screen_coordinates);
}
})
.detach();
}
async fn add_mouse_hack(
scene_manager: &FlatSceneManager,
pointer_hack_server: &PointerHackServer,
handlers: &mut Vec<Box<dyn InputHandler>>,
) {
let mouse_hack = MousePointerHack::new(
scene_manager.display_size.size(),
1.0 / scene_manager.display_metrics.pixels_per_pip(),
pointer_hack_server.pointer_listeners.clone(),
);
handlers.push(Box::new(mouse_hack));
}
async fn add_touch_hack(
scene_manager: &FlatSceneManager,
pointer_hack_server: &PointerHackServer,
handlers: &mut Vec<Box<dyn InputHandler>>,
) {
let touch_hack = TouchPointerHack::new(
scene_manager.display_size.size(),
1.0 / scene_manager.display_metrics.pixels_per_pip(),
pointer_hack_server.pointer_listeners.clone(),
);
handlers.push(Box::new(touch_hack));
}
async fn handle_input_device_registry_request_streams(
stream_receiver: futures::channel::mpsc::UnboundedReceiver<InputDeviceRegistryRequestStream>,
input_device_types: Vec<input_device::InputDeviceType>,
input_event_sender: futures::channel::mpsc::Sender<input_device::InputEvent>,
input_device_bindings: input_pipeline::InputDeviceBindingHashMap,
) {
// It's unlikely that multiple clients will concurrently connect to the InputDeviceRegistry.
// However, if multiple clients do connect concurrently, we don't want said clients
// depending on the serialization that would be provided by `for_each()`.
stream_receiver
.for_each_concurrent(None, |stream| async {
match InputPipeline::handle_input_device_registry_request_stream(
stream,
&input_device_types,
&input_event_sender,
&input_device_bindings,
)
.await
{
Ok(()) => (),
Err(e) => {
fx_log_warn!(
"failure while serving InputDeviceRegistry: {}; \
will continue serving other clients",
e
);
}
}
})
.await;
}