1use crate::input_handler::{InputHandlerStatus, UnhandledInputHandler};
6use crate::utils::Position;
7use crate::{input_device, metrics, mouse_binding};
8use anyhow::{format_err, Error};
9use async_trait::async_trait;
10use derivative::Derivative;
11use fuchsia_inspect::health::Reporter;
12use metrics_registry::*;
13use std::rc::Rc;
14
15#[derive(Derivative)]
17#[derivative(Debug, PartialEq)]
18pub struct PointerDisplayScaleHandler {
19 scale_factor: f32,
22
23 pub inspect_status: InputHandlerStatus,
25
26 #[derivative(Debug = "ignore", PartialEq = "ignore")]
28 metrics_logger: metrics::MetricsLogger,
29}
30
31#[async_trait(?Send)]
32impl UnhandledInputHandler for PointerDisplayScaleHandler {
33 async fn handle_unhandled_input_event(
34 self: Rc<Self>,
35 unhandled_input_event: input_device::UnhandledInputEvent,
36 ) -> Vec<input_device::InputEvent> {
37 match unhandled_input_event.clone() {
38 input_device::UnhandledInputEvent {
39 device_event:
40 input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent {
41 location:
42 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
43 millimeters: raw_mm,
44 }),
45 wheel_delta_v,
46 wheel_delta_h,
47 phase: phase @ mouse_binding::MousePhase::Move,
49 affected_buttons,
50 pressed_buttons,
51 is_precision_scroll,
52 }),
53 device_descriptor: device_descriptor @ input_device::InputDeviceDescriptor::Mouse(_),
54 event_time,
55 trace_id,
56 } => {
57 let tracing_id = trace_id.unwrap_or_else(|| 0.into());
58 fuchsia_trace::duration!(c"input", c"pointer_display_scale_handler");
59 fuchsia_trace::flow_step!(c"input", c"event_in_input_pipeline", tracing_id);
60
61 self.inspect_status
62 .count_received_event(input_device::InputEvent::from(unhandled_input_event));
63 let scaled_mm = self.scale_motion(raw_mm);
64 let input_event = input_device::InputEvent {
65 device_event: input_device::InputDeviceEvent::Mouse(
66 mouse_binding::MouseEvent {
67 location: mouse_binding::MouseLocation::Relative(
68 mouse_binding::RelativeLocation { millimeters: scaled_mm },
69 ),
70 wheel_delta_v,
71 wheel_delta_h,
72 phase,
73 affected_buttons,
74 pressed_buttons,
75 is_precision_scroll,
76 },
77 ),
78 device_descriptor,
79 event_time,
80 handled: input_device::Handled::No,
81 trace_id,
82 };
83 vec![input_event]
84 }
85 input_device::UnhandledInputEvent {
86 device_event:
87 input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent {
88 location,
89 wheel_delta_v,
90 wheel_delta_h,
91 phase: phase @ mouse_binding::MousePhase::Wheel,
92 affected_buttons,
93 pressed_buttons,
94 is_precision_scroll,
95 }),
96 device_descriptor: device_descriptor @ input_device::InputDeviceDescriptor::Mouse(_),
97 event_time,
98 trace_id,
99 } => {
100 fuchsia_trace::duration!(c"input", c"pointer_display_scale_handler");
101 if let Some(trace_id) = trace_id {
102 fuchsia_trace::flow_step!(
103 c"input",
104 c"event_in_input_pipeline",
105 trace_id.into()
106 );
107 }
108
109 self.inspect_status
110 .count_received_event(input_device::InputEvent::from(unhandled_input_event));
111 let scaled_wheel_delta_v = self.scale_wheel_delta(wheel_delta_v);
112 let scaled_wheel_delta_h = self.scale_wheel_delta(wheel_delta_h);
113 let input_event = input_device::InputEvent {
114 device_event: input_device::InputDeviceEvent::Mouse(
115 mouse_binding::MouseEvent {
116 location,
117 wheel_delta_v: scaled_wheel_delta_v,
118 wheel_delta_h: scaled_wheel_delta_h,
119 phase,
120 affected_buttons,
121 pressed_buttons,
122 is_precision_scroll,
123 },
124 ),
125 device_descriptor,
126 event_time,
127 handled: input_device::Handled::No,
128 trace_id,
129 };
130 vec![input_event]
131 }
132 _ => vec![input_device::InputEvent::from(unhandled_input_event)],
133 }
134 }
135
136 fn set_handler_healthy(self: std::rc::Rc<Self>) {
137 self.inspect_status.health_node.borrow_mut().set_ok();
138 }
139
140 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
141 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
142 }
143}
144
145impl PointerDisplayScaleHandler {
146 pub fn new(
152 scale_factor: f32,
153 input_handlers_node: &fuchsia_inspect::Node,
154 metrics_logger: metrics::MetricsLogger,
155 ) -> Result<Rc<Self>, Error> {
156 log::debug!("scale_factor={}", scale_factor);
157 use std::num::FpCategory;
158 let inspect_status = InputHandlerStatus::new(
159 input_handlers_node,
160 "pointer_display_scale_handler",
161 false,
162 );
163 match scale_factor.classify() {
164 FpCategory::Nan | FpCategory::Infinite | FpCategory::Zero | FpCategory::Subnormal => {
165 Err(format_err!(
166 "scale_factor {} is not a `Normal` floating-point value",
167 scale_factor
168 ))
169 }
170 FpCategory::Normal => {
171 if scale_factor < 0.0 {
172 Err(format_err!("Inverting motion is not supported"))
173 } else if scale_factor < 1.0 {
174 Err(format_err!("Down-scaling motion is not supported"))
175 } else {
176 Ok(Rc::new(Self { scale_factor, inspect_status, metrics_logger }))
177 }
178 }
179 }
180 }
181
182 fn scale_motion(self: &Rc<Self>, motion: Position) -> Position {
184 motion * self.scale_factor
185 }
186
187 fn scale_wheel_delta(
189 self: &Rc<Self>,
190 wheel_delta: Option<mouse_binding::WheelDelta>,
191 ) -> Option<mouse_binding::WheelDelta> {
192 match wheel_delta {
193 None => None,
194 Some(delta) => Some(mouse_binding::WheelDelta {
195 raw_data: delta.raw_data,
196 physical_pixel: match delta.physical_pixel {
197 None => {
198 self.metrics_logger.log_error(
201 InputPipelineErrorMetricDimensionEvent::PointerDisplayScaleNoPhysicalPixel,
202 "physical_pixel is none",
203 );
204 None
205 }
206 Some(pixel) => Some(self.scale_factor * pixel),
207 },
208 }),
209 }
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216 use crate::input_handler::InputHandler;
217 use crate::testing_utilities;
218 use assert_matches::assert_matches;
219 use fuchsia_async as fasync;
220 use maplit::hashset;
221 use std::cell::Cell;
222 use std::collections::HashSet;
223 use std::ops::Add;
224 use test_case::test_case;
225
226 const COUNTS_PER_MM: f32 = 12.0;
227 const DEVICE_DESCRIPTOR: input_device::InputDeviceDescriptor =
228 input_device::InputDeviceDescriptor::Mouse(mouse_binding::MouseDeviceDescriptor {
229 device_id: 0,
230 absolute_x_range: None,
231 absolute_y_range: None,
232 wheel_v_range: None,
233 wheel_h_range: None,
234 buttons: None,
235 counts_per_mm: COUNTS_PER_MM as u32,
236 });
237
238 std::thread_local! {static NEXT_EVENT_TIME: Cell<i64> = Cell::new(0)}
239
240 fn make_unhandled_input_event(
241 mouse_event: mouse_binding::MouseEvent,
242 ) -> input_device::UnhandledInputEvent {
243 let event_time = NEXT_EVENT_TIME.with(|t| {
244 let old = t.get();
245 t.set(old + 1);
246 old
247 });
248 input_device::UnhandledInputEvent {
249 device_event: input_device::InputDeviceEvent::Mouse(mouse_event),
250 device_descriptor: DEVICE_DESCRIPTOR.clone(),
251 event_time: zx::MonotonicInstant::from_nanos(event_time),
252 trace_id: None,
253 }
254 }
255
256 #[test_case(f32::NAN => matches Err(_); "yields err for NaN scale")]
257 #[test_case(f32::INFINITY => matches Err(_); "yields err for pos infinite scale")]
258 #[test_case(f32::NEG_INFINITY => matches Err(_); "yields err for neg infinite scale")]
259 #[test_case( -1.0 => matches Err(_); "yields err for neg scale")]
260 #[test_case( 0.0 => matches Err(_); "yields err for pos zero scale")]
261 #[test_case( -0.0 => matches Err(_); "yields err for neg zero scale")]
262 #[test_case( 0.5 => matches Err(_); "yields err for downscale")]
263 #[test_case( 1.0 => matches Ok(_); "yields handler for unit scale")]
264 #[test_case( 1.5 => matches Ok(_); "yields handler for upscale")]
265 fn new(scale_factor: f32) -> Result<Rc<PointerDisplayScaleHandler>, Error> {
266 let inspector = fuchsia_inspect::Inspector::default();
267 let test_node = inspector.root().create_child("test_node");
268 PointerDisplayScaleHandler::new(scale_factor, &test_node, metrics::MetricsLogger::default())
269 }
270
271 #[fuchsia::test(allow_stalls = false)]
272 async fn applies_scale_mm() {
273 let inspector = fuchsia_inspect::Inspector::default();
274 let test_node = inspector.root().create_child("test_node");
275 let handler =
276 PointerDisplayScaleHandler::new(2.0, &test_node, metrics::MetricsLogger::default())
277 .expect("failed to make handler");
278 let input_event = make_unhandled_input_event(mouse_binding::MouseEvent {
279 location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
280 millimeters: Position { x: 1.5, y: 4.5 },
281 }),
282 wheel_delta_v: None,
283 wheel_delta_h: None,
284 phase: mouse_binding::MousePhase::Move,
285 affected_buttons: hashset! {},
286 pressed_buttons: hashset! {},
287 is_precision_scroll: None,
288 });
289 assert_matches!(
290 handler.clone().handle_unhandled_input_event(input_event).await.as_slice(),
291 [input_device::InputEvent {
292 device_event:
293 input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent {
294 location:
295 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {millimeters: Position { x, y }}),
296 ..
297 }),
298 ..
299 }] if *x == 3.0 && *y == 9.0
300 );
301 }
302
303 #[test_case(
304 mouse_binding::MouseEvent {
305 location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
306 millimeters: Position {
307 x: 1.5 / COUNTS_PER_MM,
308 y: 4.5 / COUNTS_PER_MM },
309 }),
310 wheel_delta_v: None,
311 wheel_delta_h: None,
312 phase: mouse_binding::MousePhase::Move,
313 affected_buttons: hashset! {},
314 pressed_buttons: hashset! {},
315 is_precision_scroll: None,
316 }; "move event")]
317 #[test_case(
318 mouse_binding::MouseEvent {
319 location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
320 millimeters: Position::zero(),
321 }),
322 wheel_delta_v: Some(mouse_binding::WheelDelta {
323 raw_data: mouse_binding::RawWheelDelta::Ticks(1),
324 physical_pixel: Some(1.0),
325 }),
326 wheel_delta_h: None,
327 phase: mouse_binding::MousePhase::Wheel,
328 affected_buttons: hashset! {},
329 pressed_buttons: hashset! {},
330 is_precision_scroll: None,
331 }; "wheel event")]
332 #[fuchsia::test(allow_stalls = false)]
333 async fn does_not_consume(event: mouse_binding::MouseEvent) {
334 let inspector = fuchsia_inspect::Inspector::default();
335 let test_node = inspector.root().create_child("test_node");
336 let handler =
337 PointerDisplayScaleHandler::new(2.0, &test_node, metrics::MetricsLogger::default())
338 .expect("failed to make handler");
339 let input_event = make_unhandled_input_event(event);
340 assert_matches!(
341 handler.clone().handle_unhandled_input_event(input_event).await.as_slice(),
342 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
343 );
344 }
345
346 #[test_case(hashset! { }; "empty buttons")]
347 #[test_case(hashset! { 1}; "one button")]
348 #[test_case(hashset! {1, 2, 3}; "multiple buttons")]
349 #[fuchsia::test(allow_stalls = false)]
350 async fn preserves_buttons_move_event(input_buttons: HashSet<u8>) {
351 let inspector = fuchsia_inspect::Inspector::default();
352 let test_node = inspector.root().create_child("test_node");
353 let handler =
354 PointerDisplayScaleHandler::new(2.0, &test_node, metrics::MetricsLogger::default())
355 .expect("failed to make handler");
356 let input_event = make_unhandled_input_event(mouse_binding::MouseEvent {
357 location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
358 millimeters: Position { x: 1.5 / COUNTS_PER_MM, y: 4.5 / COUNTS_PER_MM },
359 }),
360 wheel_delta_v: None,
361 wheel_delta_h: None,
362 phase: mouse_binding::MousePhase::Move,
363 affected_buttons: input_buttons.clone(),
364 pressed_buttons: input_buttons.clone(),
365 is_precision_scroll: None,
366 });
367 assert_matches!(
368 handler.clone().handle_unhandled_input_event(input_event).await.as_slice(),
369 [input_device::InputEvent {
370 device_event:
371 input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent { affected_buttons, pressed_buttons, ..}),
372 ..
373 }] if *affected_buttons == input_buttons && *pressed_buttons == input_buttons
374 );
375 }
376
377 #[test_case(hashset! { }; "empty buttons")]
378 #[test_case(hashset! { 1}; "one button")]
379 #[test_case(hashset! {1, 2, 3}; "multiple buttons")]
380 #[fuchsia::test(allow_stalls = false)]
381 async fn preserves_buttons_wheel_event(input_buttons: HashSet<u8>) {
382 let inspector = fuchsia_inspect::Inspector::default();
383 let test_node = inspector.root().create_child("test_node");
384 let handler =
385 PointerDisplayScaleHandler::new(2.0, &test_node, metrics::MetricsLogger::default())
386 .expect("failed to make handler");
387 let input_event = make_unhandled_input_event(mouse_binding::MouseEvent {
388 location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
389 millimeters: Position::zero(),
390 }),
391 wheel_delta_v: Some(mouse_binding::WheelDelta {
392 raw_data: mouse_binding::RawWheelDelta::Ticks(1),
393 physical_pixel: Some(1.0),
394 }),
395 wheel_delta_h: None,
396 phase: mouse_binding::MousePhase::Wheel,
397 affected_buttons: input_buttons.clone(),
398 pressed_buttons: input_buttons.clone(),
399 is_precision_scroll: None,
400 });
401 assert_matches!(
402 handler.clone().handle_unhandled_input_event(input_event).await.as_slice(),
403 [input_device::InputEvent {
404 device_event:
405 input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent { affected_buttons, pressed_buttons, ..}),
406 ..
407 }] if *affected_buttons == input_buttons && *pressed_buttons == input_buttons
408 );
409 }
410
411 #[test_case(
412 mouse_binding::MouseEvent {
413 location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
414 millimeters: Position {
415 x: 1.5 / COUNTS_PER_MM,
416 y: 4.5 / COUNTS_PER_MM },
417 }),
418 wheel_delta_v: None,
419 wheel_delta_h: None,
420 phase: mouse_binding::MousePhase::Move,
421 affected_buttons: hashset! {},
422 pressed_buttons: hashset! {},
423 is_precision_scroll: None,
424 }; "move event")]
425 #[test_case(
426 mouse_binding::MouseEvent {
427 location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
428 millimeters: Position::zero(),
429 }),
430 wheel_delta_v: Some(mouse_binding::WheelDelta {
431 raw_data: mouse_binding::RawWheelDelta::Ticks(1),
432 physical_pixel: Some(1.0),
433 }),
434 wheel_delta_h: None,
435 phase: mouse_binding::MousePhase::Wheel,
436 affected_buttons: hashset! {},
437 pressed_buttons: hashset! {},
438 is_precision_scroll: None,
439 }; "wheel event")]
440 #[fuchsia::test(allow_stalls = false)]
441 async fn preserves_descriptor(event: mouse_binding::MouseEvent) {
442 let inspector = fuchsia_inspect::Inspector::default();
443 let test_node = inspector.root().create_child("test_node");
444 let handler =
445 PointerDisplayScaleHandler::new(2.0, &test_node, metrics::MetricsLogger::default())
446 .expect("failed to make handler");
447 let input_event = make_unhandled_input_event(event);
448 assert_matches!(
449 handler.clone().handle_unhandled_input_event(input_event).await.as_slice(),
450 [input_device::InputEvent { device_descriptor: DEVICE_DESCRIPTOR, .. }]
451 );
452 }
453
454 #[test_case(
455 mouse_binding::MouseEvent {
456 location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
457 millimeters: Position {
458 x: 1.5 / COUNTS_PER_MM,
459 y: 4.5 / COUNTS_PER_MM },
460 }),
461 wheel_delta_v: None,
462 wheel_delta_h: None,
463 phase: mouse_binding::MousePhase::Move,
464 affected_buttons: hashset! {},
465 pressed_buttons: hashset! {},
466 is_precision_scroll: None,
467 }; "move event")]
468 #[test_case(
469 mouse_binding::MouseEvent {
470 location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
471 millimeters: Position::zero(),
472 }),
473 wheel_delta_v: Some(mouse_binding::WheelDelta {
474 raw_data: mouse_binding::RawWheelDelta::Ticks(1),
475 physical_pixel: Some(1.0),
476 }),
477 wheel_delta_h: None,
478 phase: mouse_binding::MousePhase::Wheel,
479 affected_buttons: hashset! {},
480 pressed_buttons: hashset! {},
481 is_precision_scroll: None,
482 }; "wheel event")]
483 #[fuchsia::test(allow_stalls = false)]
484 async fn preserves_event_time(event: mouse_binding::MouseEvent) {
485 let inspector = fuchsia_inspect::Inspector::default();
486 let test_node = inspector.root().create_child("test_node");
487 let handler =
488 PointerDisplayScaleHandler::new(2.0, &test_node, metrics::MetricsLogger::default())
489 .expect("failed to make handler");
490 let mut input_event = make_unhandled_input_event(event);
491 const EVENT_TIME: zx::MonotonicInstant = zx::MonotonicInstant::from_nanos(42);
492 input_event.event_time = EVENT_TIME;
493
494 let events = handler.clone().handle_unhandled_input_event(input_event).await;
495 assert_eq!(events.len(), 1, "{events:?} should be 1 element");
496 assert_eq!(events[0].event_time, EVENT_TIME);
497 }
498
499 #[test_case(
500 mouse_binding::MouseEvent {
501 location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
502 millimeters: Position::zero(),
503 }),
504 wheel_delta_v: Some(mouse_binding::WheelDelta {
505 raw_data: mouse_binding::RawWheelDelta::Ticks(1),
506 physical_pixel: Some(1.0),
507 }),
508 wheel_delta_h: None,
509 phase: mouse_binding::MousePhase::Wheel,
510 affected_buttons: hashset! {},
511 pressed_buttons: hashset! {},
512 is_precision_scroll: Some(mouse_binding::PrecisionScroll::No),
513 } => matches input_device::InputEvent {
514 device_event: input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent {
515 is_precision_scroll: Some(mouse_binding::PrecisionScroll::No),
516 ..
517 }),
518 ..
519 }; "no")]
520 #[test_case(
521 mouse_binding::MouseEvent {
522 location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
523 millimeters: Position::zero(),
524 }),
525 wheel_delta_v: Some(mouse_binding::WheelDelta {
526 raw_data: mouse_binding::RawWheelDelta::Ticks(1),
527 physical_pixel: Some(1.0),
528 }),
529 wheel_delta_h: None,
530 phase: mouse_binding::MousePhase::Wheel,
531 affected_buttons: hashset! {},
532 pressed_buttons: hashset! {},
533 is_precision_scroll: Some(mouse_binding::PrecisionScroll::Yes),
534 } => matches input_device::InputEvent {
535 device_event: input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent {
536 is_precision_scroll: Some(mouse_binding::PrecisionScroll::Yes),
537 ..
538 }),
539 ..
540 }; "yes")]
541 #[fuchsia::test(allow_stalls = false)]
542 async fn preserves_is_precision_scroll(
543 event: mouse_binding::MouseEvent,
544 ) -> input_device::InputEvent {
545 let inspector = fuchsia_inspect::Inspector::default();
546 let test_node = inspector.root().create_child("test_node");
547 let handler =
548 PointerDisplayScaleHandler::new(2.0, &test_node, metrics::MetricsLogger::default())
549 .expect("failed to make handler");
550 let input_event = make_unhandled_input_event(event);
551
552 handler.clone().handle_unhandled_input_event(input_event).await[0].clone()
553 }
554
555 #[test_case(
556 Some(mouse_binding::WheelDelta {
557 raw_data: mouse_binding::RawWheelDelta::Ticks(1),
558 physical_pixel: Some(1.0),
559 }),
560 None => (Some(2.0), None); "v tick h none"
561 )]
562 #[test_case(
563 None, Some(mouse_binding::WheelDelta {
564 raw_data: mouse_binding::RawWheelDelta::Ticks(1),
565 physical_pixel: Some(1.0),
566 }) => (None, Some(2.0)); "v none h tick"
567 )]
568 #[test_case(
569 Some(mouse_binding::WheelDelta {
570 raw_data: mouse_binding::RawWheelDelta::Millimeters(1.0),
571 physical_pixel: Some(1.0),
572 }),
573 None => (Some(2.0), None); "v mm h none"
574 )]
575 #[test_case(
576 None, Some(mouse_binding::WheelDelta {
577 raw_data: mouse_binding::RawWheelDelta::Millimeters(1.0),
578 physical_pixel: Some(1.0),
579 }) => (None, Some(2.0)); "v none h mm"
580 )]
581 #[fuchsia::test(allow_stalls = false)]
582 async fn applied_scale_scroll_event(
583 wheel_delta_v: Option<mouse_binding::WheelDelta>,
584 wheel_delta_h: Option<mouse_binding::WheelDelta>,
585 ) -> (Option<f32>, Option<f32>) {
586 let inspector = fuchsia_inspect::Inspector::default();
587 let test_node = inspector.root().create_child("test_node");
588 let handler =
589 PointerDisplayScaleHandler::new(2.0, &test_node, metrics::MetricsLogger::default())
590 .expect("failed to make handler");
591 let input_event = make_unhandled_input_event(mouse_binding::MouseEvent {
592 location: mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
593 millimeters: Position::zero(),
594 }),
595 wheel_delta_v,
596 wheel_delta_h,
597 phase: mouse_binding::MousePhase::Wheel,
598 affected_buttons: hashset! {},
599 pressed_buttons: hashset! {},
600 is_precision_scroll: None,
601 });
602 let events = handler.clone().handle_unhandled_input_event(input_event).await;
603 assert_matches!(
604 events.as_slice(),
605 [input_device::InputEvent {
606 device_event: input_device::InputDeviceEvent::Mouse(
607 mouse_binding::MouseEvent { .. }
608 ),
609 ..
610 }]
611 );
612 if let input_device::InputEvent {
613 device_event:
614 input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent {
615 wheel_delta_v,
616 wheel_delta_h,
617 ..
618 }),
619 ..
620 } = events[0].clone()
621 {
622 match (wheel_delta_v, wheel_delta_h) {
623 (None, None) => return (None, None),
624 (None, Some(delta_h)) => return (None, delta_h.physical_pixel),
625 (Some(delta_v), None) => return (delta_v.physical_pixel, None),
626 (Some(delta_v), Some(delta_h)) => {
627 return (delta_v.physical_pixel, delta_h.physical_pixel)
628 }
629 }
630 } else {
631 unreachable!();
632 }
633 }
634
635 #[fuchsia::test]
636 fn pointer_display_scale_handler_initialized_with_inspect_node() {
637 let inspector = fuchsia_inspect::Inspector::default();
638 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
639 let _handler = PointerDisplayScaleHandler::new(
640 1.0,
641 &fake_handlers_node,
642 metrics::MetricsLogger::default(),
643 );
644 diagnostics_assertions::assert_data_tree!(inspector, root: {
645 input_handlers_node: {
646 pointer_display_scale_handler: {
647 events_received_count: 0u64,
648 events_handled_count: 0u64,
649 last_received_timestamp_ns: 0u64,
650 "fuchsia.inspect.Health": {
651 status: "STARTING_UP",
652 start_timestamp_nanos: diagnostics_assertions::AnyProperty
655 },
656 }
657 }
658 });
659 }
660
661 #[fasync::run_singlethreaded(test)]
662 async fn pointer_display_scale_handler_inspect_counts_events() {
663 let inspector = fuchsia_inspect::Inspector::default();
664 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
665 let handler = PointerDisplayScaleHandler::new(
666 1.0,
667 &fake_handlers_node,
668 metrics::MetricsLogger::default(),
669 )
670 .expect("failed to make handler");
671
672 let event_time1 = zx::MonotonicInstant::get();
673 let event_time2 = event_time1.add(zx::MonotonicDuration::from_micros(1));
674 let event_time3 = event_time2.add(zx::MonotonicDuration::from_micros(1));
675
676 let input_events = vec![
677 testing_utilities::create_mouse_event(
678 mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 }),
679 None, None, None, mouse_binding::MousePhase::Wheel,
683 hashset! {},
684 hashset! {},
685 event_time1,
686 &DEVICE_DESCRIPTOR,
687 ),
688 testing_utilities::create_mouse_event(
689 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
690 millimeters: Position { x: 1.5 / COUNTS_PER_MM, y: 4.5 / COUNTS_PER_MM },
691 }),
692 None, None, None, mouse_binding::MousePhase::Move,
696 hashset! {},
697 hashset! {},
698 event_time2,
699 &DEVICE_DESCRIPTOR,
700 ),
701 testing_utilities::create_fake_input_event(event_time2),
703 testing_utilities::create_mouse_event_with_handled(
705 mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 }),
706 None, None, None, mouse_binding::MousePhase::Wheel,
710 hashset! {},
711 hashset! {},
712 event_time3,
713 &DEVICE_DESCRIPTOR,
714 input_device::Handled::Yes,
715 ),
716 ];
717
718 for input_event in input_events {
719 let _ = handler.clone().handle_input_event(input_event).await;
720 }
721
722 let last_received_event_time: u64 = event_time2.into_nanos().try_into().unwrap();
723
724 diagnostics_assertions::assert_data_tree!(inspector, root: {
725 input_handlers_node: {
726 pointer_display_scale_handler: {
727 events_received_count: 2u64,
728 events_handled_count: 0u64,
729 last_received_timestamp_ns: last_received_event_time,
730 "fuchsia.inspect.Health": {
731 status: "STARTING_UP",
732 start_timestamp_nanos: diagnostics_assertions::AnyProperty
735 },
736 }
737 }
738 });
739 }
740}