blob: 5a20fe5cce02ca02e324ab6cc7f372a2e8963fa4 [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 {
async_trait::async_trait,
fidl_fuchsia_ui_input as fidl_ui_input,
fidl_fuchsia_ui_policy::PointerCaptureListenerHackProxy,
futures::lock::Mutex,
input::input_device,
input::input_handler::InputHandler,
input::mouse,
input::{Position, Size},
std::collections::HashSet,
std::sync::Arc,
};
/// A [`MousePointerHack`] tracks the mouse position and sends it to observers.
///
/// Ermine cannot receive pointer events from Scenic for subviews (i.e., Flutter
/// applications it embeds), so this handler is used to send all pointer events directly
/// to Ermine.
///
/// Once Ermine has a way to observe these events directly from Scenic, this handler
/// can be removed.
pub struct MousePointerHack {
/// The current cursor location.
current_position: Position,
/// The maximum cursor position, used to bound events sent to clients.
screen_size: Size,
/// The scale factor to be applied to cursor events before sending to listeners.
event_scale: f32,
/// The listeners which will receive pointer events.
listeners: Arc<Mutex<Vec<PointerCaptureListenerHackProxy>>>,
}
#[async_trait]
impl InputHandler for MousePointerHack {
async fn handle_input_event(
&mut self,
input_event: input_device::InputEvent,
) -> Vec<input_device::InputEvent> {
match &input_event {
input_device::InputEvent {
device_event: input_device::InputDeviceEvent::Mouse(mouse_event),
device_descriptor: input_device::InputDeviceDescriptor::Mouse(mouse_descriptor),
event_time,
} => {
self.update_cursor_position(&mouse_event).await;
self.send_event_to_listeners(
mouse_event.phase,
&mouse_event.buttons,
*event_time,
&mouse_descriptor,
)
.await;
}
_ => {}
}
vec![input_event]
}
}
impl MousePointerHack {
/// Creates a new [`MousePointerHack `] that sends pointer events to Ermine.
///
/// # Parameters
/// - `max_cursor_position`: The maximum position to cap mouse movement at.
/// - `event_scale`: The scale to apply to pointer events before sending them to listeners.
/// - `listeners`: The listeners that will get notified of mouse events.
pub fn new(
screen_size: Size,
event_scale: f32,
listeners: Arc<Mutex<Vec<PointerCaptureListenerHackProxy>>>,
) -> MousePointerHack {
MousePointerHack {
current_position: Position { x: 0.0, y: 0.0 },
screen_size,
event_scale,
listeners,
}
}
/// Updates the current cursor position according to the received mouse event.
///
/// # Parameters
/// - `mouse_event`: The mouse event to use to update the cursor location.
async fn update_cursor_position(&mut self, mouse_event: &mouse::MouseEvent) {
if mouse_event.movement == Position::zero() {
return;
}
self.current_position += mouse_event.movement;
Position::clamp_size(&mut self.current_position, Size::zero(), self.screen_size)
}
/// Sends a pointer event with the given phase and buttons to listeners.
///
/// # Parameters
/// - `phase`: The phase of the buttons associated with the mouse event.
/// - `buttons`: The buttons associated with the event.
/// - `event_time`: The time the event was first reported.
/// - `device_descriptor`: The descriptor for the device that generated the event.
async fn send_event_to_listeners(
&self,
phase: fidl_ui_input::PointerEventPhase,
buttons: &HashSet<mouse::MouseButton>,
event_time: input_device::EventTime,
device_descriptor: &mouse::MouseDeviceDescriptor,
) {
let buttons = mouse::get_u32_from_buttons(buttons);
let scaled_position = self.current_position * self.event_scale;
let mut pointer = fidl_ui_input::PointerEvent {
event_time: event_time,
device_id: device_descriptor.device_id,
pointer_id: 0,
type_: fidl_ui_input::PointerEventType::Mouse,
phase,
// Since Ermine listens for these events it needs to receive them in Ermine's view's
// coordinate space. Normally, Scenic would perform this scaling when traversing
// scale nodes in the Scene graph, but here the scale needs to be done manually.
x: scaled_position.x,
y: scaled_position.y,
radius_major: 0.0,
radius_minor: 0.0,
buttons,
};
let listeners = self.listeners.lock().await;
for listener in &mut listeners.iter() {
let _ = listener.on_pointer_event(&mut pointer);
}
}
}