blob: 525444c7cabc9e45878d5fba7e49ada3500eb63a [file] [log] [blame]
// Copyright 2022 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::{
input_device,
input_handler::{InputHandlerStatus, UnhandledInputHandler},
metrics, mouse_binding,
utils::Position,
},
anyhow::{format_err, Error},
async_trait::async_trait,
derivative::Derivative,
fuchsia_inspect::health::Reporter,
metrics_registry::*,
std::rc::Rc,
};
// TODO(https://fxbug.dev/42172817) Add trackpad support
#[derive(Derivative)]
#[derivative(Debug, PartialEq)]
pub struct PointerDisplayScaleHandler {
/// The amount by which motion will be scaled up. E.g., a `scale_factor`
/// of 2 means that all motion will be multiplied by 2.
scale_factor: f32,
/// The inventory of this handler's Inspect status.
pub inspect_status: InputHandlerStatus,
/// The metrics logger.
#[derivative(Debug = "ignore", PartialEq = "ignore")]
metrics_logger: metrics::MetricsLogger,
}
#[async_trait(?Send)]
impl UnhandledInputHandler for PointerDisplayScaleHandler {
async fn handle_unhandled_input_event(
self: Rc<Self>,
unhandled_input_event: input_device::UnhandledInputEvent,
) -> Vec<input_device::InputEvent> {
match unhandled_input_event.clone() {
input_device::UnhandledInputEvent {
device_event:
input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent {
location:
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: raw_mm,
}),
wheel_delta_v,
wheel_delta_h,
// Only the `Move` phase carries non-zero motion.
phase: phase @ mouse_binding::MousePhase::Move,
affected_buttons,
pressed_buttons,
is_precision_scroll,
}),
device_descriptor: device_descriptor @ input_device::InputDeviceDescriptor::Mouse(_),
event_time,
trace_id: _,
} => {
self.inspect_status
.count_received_event(input_device::InputEvent::from(unhandled_input_event));
let scaled_mm = self.scale_motion(raw_mm);
let input_event = input_device::InputEvent {
device_event: input_device::InputDeviceEvent::Mouse(
mouse_binding::MouseEvent {
location: mouse_binding::MouseLocation::Relative(
mouse_binding::RelativeLocation { millimeters: scaled_mm },
),
wheel_delta_v,
wheel_delta_h,
phase,
affected_buttons,
pressed_buttons,
is_precision_scroll,
},
),
device_descriptor,
event_time,
handled: input_device::Handled::No,
trace_id: None,
};
vec![input_event]
}
input_device::UnhandledInputEvent {
device_event:
input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent {
location,
wheel_delta_v,
wheel_delta_h,
phase: phase @ mouse_binding::MousePhase::Wheel,
affected_buttons,
pressed_buttons,
is_precision_scroll,
}),
device_descriptor: device_descriptor @ input_device::InputDeviceDescriptor::Mouse(_),
event_time,
trace_id: _,
} => {
self.inspect_status
.count_received_event(input_device::InputEvent::from(unhandled_input_event));
let scaled_wheel_delta_v = self.scale_wheel_delta(wheel_delta_v);
let scaled_wheel_delta_h = self.scale_wheel_delta(wheel_delta_h);
let input_event = input_device::InputEvent {
device_event: input_device::InputDeviceEvent::Mouse(
mouse_binding::MouseEvent {
location,
wheel_delta_v: scaled_wheel_delta_v,
wheel_delta_h: scaled_wheel_delta_h,
phase,
affected_buttons,
pressed_buttons,
is_precision_scroll,
},
),
device_descriptor,
event_time,
handled: input_device::Handled::No,
trace_id: None,
};
vec![input_event]
}
_ => vec![input_device::InputEvent::from(unhandled_input_event)],
}
}
fn set_handler_healthy(self: std::rc::Rc<Self>) {
self.inspect_status.health_node.borrow_mut().set_ok();
}
fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
}
}
impl PointerDisplayScaleHandler {
/// Creates a new [`PointerMotionDisplayScaleHandler`].
///
/// Returns
/// * `Ok(Rc<Self>)` if `scale_factor` is finite and >= 1.0, and
/// * `Err(Error)` otherwise.
pub fn new(
scale_factor: f32,
input_handlers_node: &fuchsia_inspect::Node,
metrics_logger: metrics::MetricsLogger,
) -> Result<Rc<Self>, Error> {
tracing::debug!("scale_factor={}", scale_factor);
use std::num::FpCategory;
let inspect_status = InputHandlerStatus::new(
input_handlers_node,
"pointer_display_scale_handler",
/* generates_events */ false,
);
match scale_factor.classify() {
FpCategory::Nan | FpCategory::Infinite | FpCategory::Zero | FpCategory::Subnormal => {
Err(format_err!(
"scale_factor {} is not a `Normal` floating-point value",
scale_factor
))
}
FpCategory::Normal => {
if scale_factor < 0.0 {
Err(format_err!("Inverting motion is not supported"))
} else if scale_factor < 1.0 {
Err(format_err!("Down-scaling motion is not supported"))
} else {
Ok(Rc::new(Self { scale_factor, inspect_status, metrics_logger }))
}
}
}
}
/// Scales `motion`, using the configuration in `self`.
fn scale_motion(self: &Rc<Self>, motion: Position) -> Position {
motion * self.scale_factor
}
/// Scales `wheel_delta`, using the configuration in `self`.
fn scale_wheel_delta(
self: &Rc<Self>,
wheel_delta: Option<mouse_binding::WheelDelta>,
) -> Option<mouse_binding::WheelDelta> {
match wheel_delta {
None => None,
Some(delta) => Some(mouse_binding::WheelDelta {
raw_data: delta.raw_data,
physical_pixel: match delta.physical_pixel {
None => {
// this should never reach as pointer_sensor_scale_handler should
// fill this field.
self.metrics_logger.log_error(
InputPipelineErrorMetricDimensionEvent::PointerDisplayScaleNoPhysicalPixel,
"physical_pixel is none",
);
None
}
Some(pixel) => Some(self.scale_factor * pixel),
},
}),
}
}
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::input_handler::InputHandler,
crate::testing_utilities,
assert_matches::assert_matches,
fuchsia_async as fasync, fuchsia_zircon as zx,
maplit::hashset,
std::{cell::Cell, collections::HashSet, ops::Add},
test_case::test_case,
};
const COUNTS_PER_MM: f32 = 12.0;
const DEVICE_DESCRIPTOR: input_device::InputDeviceDescriptor =
input_device::InputDeviceDescriptor::Mouse(mouse_binding::MouseDeviceDescriptor {
device_id: 0,
absolute_x_range: None,
absolute_y_range: None,
wheel_v_range: None,
wheel_h_range: None,
buttons: None,
counts_per_mm: COUNTS_PER_MM as u32,
});
std::thread_local! {static NEXT_EVENT_TIME: Cell<i64> = Cell::new(0)}
fn make_unhandled_input_event(
mouse_event: mouse_binding::MouseEvent,
) -> input_device::UnhandledInputEvent {
let event_time = NEXT_EVENT_TIME.with(|t| {
let old = t.get();
t.set(old + 1);
old
});
input_device::UnhandledInputEvent {
device_event: input_device::InputDeviceEvent::Mouse(mouse_event),
device_descriptor: DEVICE_DESCRIPTOR.clone(),
event_time: zx::Time::from_nanos(event_time),
trace_id: None,
}
}
#[test_case(f32::NAN => matches Err(_); "yields err for NaN scale")]
#[test_case(f32::INFINITY => matches Err(_); "yields err for pos infinite scale")]
#[test_case(f32::NEG_INFINITY => matches Err(_); "yields err for neg infinite scale")]
#[test_case( -1.0 => matches Err(_); "yields err for neg scale")]
#[test_case( 0.0 => matches Err(_); "yields err for pos zero scale")]
#[test_case( -0.0 => matches Err(_); "yields err for neg zero scale")]
#[test_case( 0.5 => matches Err(_); "yields err for downscale")]
#[test_case( 1.0 => matches Ok(_); "yields handler for unit scale")]
#[test_case( 1.5 => matches Ok(_); "yields handler for upscale")]
fn new(scale_factor: f32) -> Result<Rc<PointerDisplayScaleHandler>, Error> {
let inspector = fuchsia_inspect::Inspector::default();
let test_node = inspector.root().create_child("test_node");
PointerDisplayScaleHandler::new(scale_factor, &test_node, metrics::MetricsLogger::default())
}
#[fuchsia::test(allow_stalls = false)]
async fn applies_scale_mm() {
let inspector = fuchsia_inspect::Inspector::default();
let test_node = inspector.root().create_child("test_node");
let handler =
PointerDisplayScaleHandler::new(2.0, &test_node, metrics::MetricsLogger::default())
.expect("failed to make handler");
let input_event = make_unhandled_input_event(mouse_binding::MouseEvent {
location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position { x: 1.5, y: 4.5 },
}),
wheel_delta_v: None,
wheel_delta_h: None,
phase: mouse_binding::MousePhase::Move,
affected_buttons: hashset! {},
pressed_buttons: hashset! {},
is_precision_scroll: None,
});
assert_matches!(
handler.clone().handle_unhandled_input_event(input_event).await.as_slice(),
[input_device::InputEvent {
device_event:
input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent {
location:
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {millimeters: Position { x, y }}),
..
}),
..
}] if *x == 3.0 && *y == 9.0
);
}
#[test_case(
mouse_binding::MouseEvent {
location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position {
x: 1.5 / COUNTS_PER_MM,
y: 4.5 / COUNTS_PER_MM },
}),
wheel_delta_v: None,
wheel_delta_h: None,
phase: mouse_binding::MousePhase::Move,
affected_buttons: hashset! {},
pressed_buttons: hashset! {},
is_precision_scroll: None,
}; "move event")]
#[test_case(
mouse_binding::MouseEvent {
location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position::zero(),
}),
wheel_delta_v: Some(mouse_binding::WheelDelta {
raw_data: mouse_binding::RawWheelDelta::Ticks(1),
physical_pixel: Some(1.0),
}),
wheel_delta_h: None,
phase: mouse_binding::MousePhase::Wheel,
affected_buttons: hashset! {},
pressed_buttons: hashset! {},
is_precision_scroll: None,
}; "wheel event")]
#[fuchsia::test(allow_stalls = false)]
async fn does_not_consume(event: mouse_binding::MouseEvent) {
let inspector = fuchsia_inspect::Inspector::default();
let test_node = inspector.root().create_child("test_node");
let handler =
PointerDisplayScaleHandler::new(2.0, &test_node, metrics::MetricsLogger::default())
.expect("failed to make handler");
let input_event = make_unhandled_input_event(event);
assert_matches!(
handler.clone().handle_unhandled_input_event(input_event).await.as_slice(),
[input_device::InputEvent { handled: input_device::Handled::No, .. }]
);
}
#[test_case(hashset! { }; "empty buttons")]
#[test_case(hashset! { 1}; "one button")]
#[test_case(hashset! {1, 2, 3}; "multiple buttons")]
#[fuchsia::test(allow_stalls = false)]
async fn preserves_buttons_move_event(input_buttons: HashSet<u8>) {
let inspector = fuchsia_inspect::Inspector::default();
let test_node = inspector.root().create_child("test_node");
let handler =
PointerDisplayScaleHandler::new(2.0, &test_node, metrics::MetricsLogger::default())
.expect("failed to make handler");
let input_event = make_unhandled_input_event(mouse_binding::MouseEvent {
location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position { x: 1.5 / COUNTS_PER_MM, y: 4.5 / COUNTS_PER_MM },
}),
wheel_delta_v: None,
wheel_delta_h: None,
phase: mouse_binding::MousePhase::Move,
affected_buttons: input_buttons.clone(),
pressed_buttons: input_buttons.clone(),
is_precision_scroll: None,
});
assert_matches!(
handler.clone().handle_unhandled_input_event(input_event).await.as_slice(),
[input_device::InputEvent {
device_event:
input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent { affected_buttons, pressed_buttons, ..}),
..
}] if *affected_buttons == input_buttons && *pressed_buttons == input_buttons
);
}
#[test_case(hashset! { }; "empty buttons")]
#[test_case(hashset! { 1}; "one button")]
#[test_case(hashset! {1, 2, 3}; "multiple buttons")]
#[fuchsia::test(allow_stalls = false)]
async fn preserves_buttons_wheel_event(input_buttons: HashSet<u8>) {
let inspector = fuchsia_inspect::Inspector::default();
let test_node = inspector.root().create_child("test_node");
let handler =
PointerDisplayScaleHandler::new(2.0, &test_node, metrics::MetricsLogger::default())
.expect("failed to make handler");
let input_event = make_unhandled_input_event(mouse_binding::MouseEvent {
location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position::zero(),
}),
wheel_delta_v: Some(mouse_binding::WheelDelta {
raw_data: mouse_binding::RawWheelDelta::Ticks(1),
physical_pixel: Some(1.0),
}),
wheel_delta_h: None,
phase: mouse_binding::MousePhase::Wheel,
affected_buttons: input_buttons.clone(),
pressed_buttons: input_buttons.clone(),
is_precision_scroll: None,
});
assert_matches!(
handler.clone().handle_unhandled_input_event(input_event).await.as_slice(),
[input_device::InputEvent {
device_event:
input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent { affected_buttons, pressed_buttons, ..}),
..
}] if *affected_buttons == input_buttons && *pressed_buttons == input_buttons
);
}
#[test_case(
mouse_binding::MouseEvent {
location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position {
x: 1.5 / COUNTS_PER_MM,
y: 4.5 / COUNTS_PER_MM },
}),
wheel_delta_v: None,
wheel_delta_h: None,
phase: mouse_binding::MousePhase::Move,
affected_buttons: hashset! {},
pressed_buttons: hashset! {},
is_precision_scroll: None,
}; "move event")]
#[test_case(
mouse_binding::MouseEvent {
location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position::zero(),
}),
wheel_delta_v: Some(mouse_binding::WheelDelta {
raw_data: mouse_binding::RawWheelDelta::Ticks(1),
physical_pixel: Some(1.0),
}),
wheel_delta_h: None,
phase: mouse_binding::MousePhase::Wheel,
affected_buttons: hashset! {},
pressed_buttons: hashset! {},
is_precision_scroll: None,
}; "wheel event")]
#[fuchsia::test(allow_stalls = false)]
async fn preserves_descriptor(event: mouse_binding::MouseEvent) {
let inspector = fuchsia_inspect::Inspector::default();
let test_node = inspector.root().create_child("test_node");
let handler =
PointerDisplayScaleHandler::new(2.0, &test_node, metrics::MetricsLogger::default())
.expect("failed to make handler");
let input_event = make_unhandled_input_event(event);
assert_matches!(
handler.clone().handle_unhandled_input_event(input_event).await.as_slice(),
[input_device::InputEvent { device_descriptor: DEVICE_DESCRIPTOR, .. }]
);
}
#[test_case(
mouse_binding::MouseEvent {
location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position {
x: 1.5 / COUNTS_PER_MM,
y: 4.5 / COUNTS_PER_MM },
}),
wheel_delta_v: None,
wheel_delta_h: None,
phase: mouse_binding::MousePhase::Move,
affected_buttons: hashset! {},
pressed_buttons: hashset! {},
is_precision_scroll: None,
}; "move event")]
#[test_case(
mouse_binding::MouseEvent {
location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position::zero(),
}),
wheel_delta_v: Some(mouse_binding::WheelDelta {
raw_data: mouse_binding::RawWheelDelta::Ticks(1),
physical_pixel: Some(1.0),
}),
wheel_delta_h: None,
phase: mouse_binding::MousePhase::Wheel,
affected_buttons: hashset! {},
pressed_buttons: hashset! {},
is_precision_scroll: None,
}; "wheel event")]
#[fuchsia::test(allow_stalls = false)]
async fn preserves_event_time(event: mouse_binding::MouseEvent) {
let inspector = fuchsia_inspect::Inspector::default();
let test_node = inspector.root().create_child("test_node");
let handler =
PointerDisplayScaleHandler::new(2.0, &test_node, metrics::MetricsLogger::default())
.expect("failed to make handler");
let mut input_event = make_unhandled_input_event(event);
const EVENT_TIME: zx::Time = zx::Time::from_nanos(42);
input_event.event_time = EVENT_TIME;
assert_matches!(
handler.clone().handle_unhandled_input_event(input_event).await.as_slice(),
[input_device::InputEvent { event_time: EVENT_TIME, .. }]
);
}
#[test_case(
mouse_binding::MouseEvent {
location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position::zero(),
}),
wheel_delta_v: Some(mouse_binding::WheelDelta {
raw_data: mouse_binding::RawWheelDelta::Ticks(1),
physical_pixel: Some(1.0),
}),
wheel_delta_h: None,
phase: mouse_binding::MousePhase::Wheel,
affected_buttons: hashset! {},
pressed_buttons: hashset! {},
is_precision_scroll: Some(mouse_binding::PrecisionScroll::No),
} => matches input_device::InputEvent {
device_event: input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent {
is_precision_scroll: Some(mouse_binding::PrecisionScroll::No),
..
}),
..
}; "no")]
#[test_case(
mouse_binding::MouseEvent {
location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position::zero(),
}),
wheel_delta_v: Some(mouse_binding::WheelDelta {
raw_data: mouse_binding::RawWheelDelta::Ticks(1),
physical_pixel: Some(1.0),
}),
wheel_delta_h: None,
phase: mouse_binding::MousePhase::Wheel,
affected_buttons: hashset! {},
pressed_buttons: hashset! {},
is_precision_scroll: Some(mouse_binding::PrecisionScroll::Yes),
} => matches input_device::InputEvent {
device_event: input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent {
is_precision_scroll: Some(mouse_binding::PrecisionScroll::Yes),
..
}),
..
}; "yes")]
#[fuchsia::test(allow_stalls = false)]
async fn preserves_is_precision_scroll(
event: mouse_binding::MouseEvent,
) -> input_device::InputEvent {
let inspector = fuchsia_inspect::Inspector::default();
let test_node = inspector.root().create_child("test_node");
let handler =
PointerDisplayScaleHandler::new(2.0, &test_node, metrics::MetricsLogger::default())
.expect("failed to make handler");
let input_event = make_unhandled_input_event(event);
handler.clone().handle_unhandled_input_event(input_event).await[0].clone()
}
#[test_case(
Some(mouse_binding::WheelDelta {
raw_data: mouse_binding::RawWheelDelta::Ticks(1),
physical_pixel: Some(1.0),
}),
None => (Some(2.0), None); "v tick h none"
)]
#[test_case(
None, Some(mouse_binding::WheelDelta {
raw_data: mouse_binding::RawWheelDelta::Ticks(1),
physical_pixel: Some(1.0),
}) => (None, Some(2.0)); "v none h tick"
)]
#[test_case(
Some(mouse_binding::WheelDelta {
raw_data: mouse_binding::RawWheelDelta::Millimeters(1.0),
physical_pixel: Some(1.0),
}),
None => (Some(2.0), None); "v mm h none"
)]
#[test_case(
None, Some(mouse_binding::WheelDelta {
raw_data: mouse_binding::RawWheelDelta::Millimeters(1.0),
physical_pixel: Some(1.0),
}) => (None, Some(2.0)); "v none h mm"
)]
#[fuchsia::test(allow_stalls = false)]
async fn applied_scale_scroll_event(
wheel_delta_v: Option<mouse_binding::WheelDelta>,
wheel_delta_h: Option<mouse_binding::WheelDelta>,
) -> (Option<f32>, Option<f32>) {
let inspector = fuchsia_inspect::Inspector::default();
let test_node = inspector.root().create_child("test_node");
let handler =
PointerDisplayScaleHandler::new(2.0, &test_node, metrics::MetricsLogger::default())
.expect("failed to make handler");
let input_event = make_unhandled_input_event(mouse_binding::MouseEvent {
location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position::zero(),
}),
wheel_delta_v,
wheel_delta_h,
phase: mouse_binding::MousePhase::Wheel,
affected_buttons: hashset! {},
pressed_buttons: hashset! {},
is_precision_scroll: None,
});
let events = handler.clone().handle_unhandled_input_event(input_event).await;
assert_matches!(
events.as_slice(),
[input_device::InputEvent {
device_event: input_device::InputDeviceEvent::Mouse(
mouse_binding::MouseEvent { .. }
),
..
}]
);
if let input_device::InputEvent {
device_event:
input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent {
wheel_delta_v,
wheel_delta_h,
..
}),
..
} = events[0].clone()
{
match (wheel_delta_v, wheel_delta_h) {
(None, None) => return (None, None),
(None, Some(delta_h)) => return (None, delta_h.physical_pixel),
(Some(delta_v), None) => return (delta_v.physical_pixel, None),
(Some(delta_v), Some(delta_h)) => {
return (delta_v.physical_pixel, delta_h.physical_pixel)
}
}
} else {
unreachable!();
}
}
#[fuchsia::test]
fn pointer_display_scale_handler_initialized_with_inspect_node() {
let inspector = fuchsia_inspect::Inspector::default();
let fake_handlers_node = inspector.root().create_child("input_handlers_node");
let _handler = PointerDisplayScaleHandler::new(
1.0,
&fake_handlers_node,
metrics::MetricsLogger::default(),
);
diagnostics_assertions::assert_data_tree!(inspector, root: {
input_handlers_node: {
pointer_display_scale_handler: {
events_received_count: 0u64,
events_handled_count: 0u64,
last_received_timestamp_ns: 0u64,
"fuchsia.inspect.Health": {
status: "STARTING_UP",
// Timestamp value is unpredictable and not relevant in this context,
// so we only assert that the property is present.
start_timestamp_nanos: diagnostics_assertions::AnyProperty
},
}
}
});
}
#[fasync::run_singlethreaded(test)]
async fn pointer_display_scale_handler_inspect_counts_events() {
let inspector = fuchsia_inspect::Inspector::default();
let fake_handlers_node = inspector.root().create_child("input_handlers_node");
let handler = PointerDisplayScaleHandler::new(
1.0,
&fake_handlers_node,
metrics::MetricsLogger::default(),
)
.expect("failed to make handler");
let event_time1 = zx::Time::get_monotonic();
let event_time2 = event_time1.add(fuchsia_zircon::Duration::from_micros(1));
let event_time3 = event_time2.add(fuchsia_zircon::Duration::from_micros(1));
let input_events = vec![
testing_utilities::create_mouse_event(
mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 }),
None, /* wheel_delta_v */
None, /* wheel_delta_h */
None, /* is_precision_scroll */
mouse_binding::MousePhase::Wheel,
hashset! {},
hashset! {},
event_time1,
&DEVICE_DESCRIPTOR,
),
testing_utilities::create_mouse_event(
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position { x: 1.5 / COUNTS_PER_MM, y: 4.5 / COUNTS_PER_MM },
}),
None, /* wheel_delta_v */
None, /* wheel_delta_h */
None, /* is_precision_scroll */
mouse_binding::MousePhase::Move,
hashset! {},
hashset! {},
event_time2,
&DEVICE_DESCRIPTOR,
),
// Should not count non-mouse input events.
testing_utilities::create_fake_input_event(event_time2),
// Should not count received events that have already been handled.
testing_utilities::create_mouse_event_with_handled(
mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 }),
None, /* wheel_delta_v */
None, /* wheel_delta_h */
None, /* is_precision_scroll */
mouse_binding::MousePhase::Wheel,
hashset! {},
hashset! {},
event_time3,
&DEVICE_DESCRIPTOR,
input_device::Handled::Yes,
),
];
for input_event in input_events {
let _ = handler.clone().handle_input_event(input_event).await;
}
let last_received_event_time: u64 = event_time2.into_nanos().try_into().unwrap();
diagnostics_assertions::assert_data_tree!(inspector, root: {
input_handlers_node: {
pointer_display_scale_handler: {
events_received_count: 2u64,
events_handled_count: 0u64,
last_received_timestamp_ns: last_received_event_time,
"fuchsia.inspect.Health": {
status: "STARTING_UP",
// Timestamp value is unpredictable and not relevant in this context,
// so we only assert that the property is present.
start_timestamp_nanos: diagnostics_assertions::AnyProperty
},
}
}
});
}
}