1use crate::InputFile;
6use crate::input_event_relay::{DeviceId, OpenedFiles};
7use futures::FutureExt;
8use starnix_core::device::kobject::DeviceMetadata;
9use starnix_core::device::{DeviceMode, DeviceOps};
10use starnix_core::task::CurrentTask;
11use starnix_core::vfs::{FileOps, FsString, NamespaceNode};
12#[cfg(test)]
13use starnix_sync::Unlocked;
14use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Mutex};
15use starnix_uapi::device_type::{DeviceType, INPUT_MAJOR};
16use starnix_uapi::errors::Errno;
17use starnix_uapi::open_flags::OpenFlags;
18use starnix_uapi::{BUS_VIRTUAL, input_id};
19use std::sync::Arc;
20use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
21
22const FUCHSIA_VENDOR_ID: u16 = 0xfc1a;
27
28const FUCHSIA_TOUCH_PRODUCT_ID: u16 = 0x2;
30
31const FUCHSIA_KEYBOARD_PRODUCT_ID: u16 = 0x1;
33
34const FUCHSIA_MOUSE_PRODUCT_ID: u16 = 0x3;
36
37const TOUCH_INPUT_ID: input_id = input_id {
54 bustype: BUS_VIRTUAL as u16,
55 vendor: FUCHSIA_VENDOR_ID,
58 product: FUCHSIA_TOUCH_PRODUCT_ID,
59 version: 0,
62};
63const KEYBOARD_INPUT_ID: input_id = input_id {
64 bustype: BUS_VIRTUAL as u16,
65 vendor: FUCHSIA_VENDOR_ID,
68 product: FUCHSIA_KEYBOARD_PRODUCT_ID,
69 version: 1,
70};
71
72const MOUSE_INPUT_ID: input_id = input_id {
73 bustype: BUS_VIRTUAL as u16,
74 vendor: FUCHSIA_VENDOR_ID,
77 product: FUCHSIA_MOUSE_PRODUCT_ID,
78 version: 1,
79};
80
81#[derive(Clone)]
82enum InputDeviceType {
83 Touch(i32, i32),
85
86 Keyboard,
88
89 Mouse,
91}
92
93pub struct InputDeviceStatus {
100 pub node: fuchsia_inspect::Node,
102
103 pub file_nodes: Mutex<Vec<fuchsia_inspect::Node>>,
106
107 pub total_fidl_events_received_count: AtomicU64,
118
119 pub total_fidl_events_ignored_count: AtomicU64,
122
123 pub total_fidl_events_unexpected_count: AtomicU64,
127
128 pub total_fidl_events_converted_count: AtomicU64,
131
132 pub total_uapi_events_generated_count: AtomicU64,
134
135 pub last_generated_uapi_event_timestamp_ns: AtomicI64,
137}
138
139impl InputDeviceStatus {
140 fn new(node: fuchsia_inspect::Node) -> Arc<Self> {
141 let status = Arc::new(Self {
142 node,
143 file_nodes: Mutex::new(vec![]),
144 total_fidl_events_received_count: AtomicU64::new(0),
145 total_fidl_events_ignored_count: AtomicU64::new(0),
146 total_fidl_events_unexpected_count: AtomicU64::new(0),
147 total_fidl_events_converted_count: AtomicU64::new(0),
148 total_uapi_events_generated_count: AtomicU64::new(0),
149 last_generated_uapi_event_timestamp_ns: AtomicI64::new(0),
150 });
151
152 let weak_status = Arc::downgrade(&status);
153 status.node.record_lazy_values("status", move || {
154 let status = weak_status.upgrade();
155 async move {
156 let inspector = fuchsia_inspect::Inspector::default();
157 if let Some(status) = status {
158 let root = inspector.root();
159 root.record_uint(
160 "total_fidl_events_received_count",
161 status.total_fidl_events_received_count.load(Ordering::Relaxed),
162 );
163 root.record_uint(
164 "total_fidl_events_ignored_count",
165 status.total_fidl_events_ignored_count.load(Ordering::Relaxed),
166 );
167 root.record_uint(
168 "total_fidl_events_unexpected_count",
169 status.total_fidl_events_unexpected_count.load(Ordering::Relaxed),
170 );
171 root.record_uint(
172 "total_fidl_events_converted_count",
173 status.total_fidl_events_converted_count.load(Ordering::Relaxed),
174 );
175 root.record_uint(
176 "total_uapi_events_generated_count",
177 status.total_uapi_events_generated_count.load(Ordering::Relaxed),
178 );
179 root.record_int(
180 "last_generated_uapi_event_timestamp_ns",
181 status.last_generated_uapi_event_timestamp_ns.load(Ordering::Relaxed),
182 );
183 }
184 Ok(inspector)
185 }
186 .boxed()
187 });
188
189 status
190 }
191
192 pub fn count_total_received_events(&self, count: u64) {
193 self.total_fidl_events_received_count.fetch_add(count, Ordering::Relaxed);
194 }
195
196 pub fn count_total_ignored_events(&self, count: u64) {
197 self.total_fidl_events_ignored_count.fetch_add(count, Ordering::Relaxed);
198 }
199
200 pub fn count_total_unexpected_events(&self, count: u64) {
201 self.total_fidl_events_unexpected_count.fetch_add(count, Ordering::Relaxed);
202 }
203
204 pub fn count_total_converted_events(&self, count: u64) {
205 self.total_fidl_events_converted_count.fetch_add(count, Ordering::Relaxed);
206 }
207
208 pub fn count_total_generated_events(&self, count: u64, event_time_ns: i64) {
209 self.total_uapi_events_generated_count.fetch_add(count, Ordering::Relaxed);
210 self.last_generated_uapi_event_timestamp_ns.store(event_time_ns, Ordering::Relaxed);
211 }
212}
213
214#[derive(Clone)]
215pub struct InputDevice {
216 device_type: InputDeviceType,
217
218 pub open_files: OpenedFiles,
219
220 pub inspect_status: Arc<InputDeviceStatus>,
221}
222
223impl InputDevice {
224 pub fn new_touch(
225 display_width: i32,
226 display_height: i32,
227 inspect_node: &fuchsia_inspect::Node,
228 ) -> Self {
229 let node = inspect_node.create_child("touch_device");
230 InputDevice {
231 device_type: InputDeviceType::Touch(display_width, display_height),
232 open_files: Default::default(),
233 inspect_status: InputDeviceStatus::new(node),
234 }
235 }
236
237 pub fn new_keyboard(inspect_node: &fuchsia_inspect::Node) -> Self {
238 let node = inspect_node.create_child("keyboard_device");
239 InputDevice {
240 device_type: InputDeviceType::Keyboard,
241 open_files: Default::default(),
242 inspect_status: InputDeviceStatus::new(node),
243 }
244 }
245
246 pub fn new_mouse(inspect_node: &fuchsia_inspect::Node) -> Self {
247 let node = inspect_node.create_child("mouse_device");
248 InputDevice {
249 device_type: InputDeviceType::Mouse,
250 open_files: Default::default(),
251 inspect_status: InputDeviceStatus::new(node),
252 }
253 }
254
255 pub fn register<L>(
256 self,
257 locked: &mut Locked<L>,
258 system_task: &CurrentTask,
259 device_id: DeviceId,
260 ) -> Result<(), Errno>
261 where
262 L: LockEqualOrBefore<FileOpsCore>,
263 {
264 let kernel = system_task.kernel();
265 let registry = &kernel.device_registry;
266
267 let input_class = registry.objects.input_class();
268 registry.register_device(
269 locked,
270 system_task,
271 FsString::from(format!("event{}", device_id)).as_ref(),
272 DeviceMetadata::new(
273 format!("input/event{}", device_id).into(),
274 DeviceType::new(INPUT_MAJOR, device_id),
275 DeviceMode::Char,
276 ),
277 input_class,
278 self,
279 )?;
280 Ok(())
281 }
282
283 pub fn open_internal(&self) -> Box<dyn FileOps> {
284 let input_file = match self.device_type {
285 InputDeviceType::Touch(display_width, display_height) => {
286 let mut file_nodes = self.inspect_status.file_nodes.lock();
287 let child_node = self
288 .inspect_status
289 .node
290 .create_child(format!("touch_file_{}", file_nodes.len()));
291 let file = Arc::new(InputFile::new_touch(
292 TOUCH_INPUT_ID,
293 display_width,
294 display_height,
295 Some(&child_node),
296 ));
297 file_nodes.push(child_node);
298 file
299 }
300 InputDeviceType::Keyboard => {
301 let mut file_nodes = self.inspect_status.file_nodes.lock();
302 let child_node = self
303 .inspect_status
304 .node
305 .create_child(format!("keyboard_file_{}", file_nodes.len()));
306 let file = Arc::new(InputFile::new_keyboard(KEYBOARD_INPUT_ID, Some(&child_node)));
307 file_nodes.push(child_node);
308 file
309 }
310 InputDeviceType::Mouse => {
311 let mut file_nodes = self.inspect_status.file_nodes.lock();
312 let child_node = self
313 .inspect_status
314 .node
315 .create_child(format!("mouse_file_{}", file_nodes.len()));
316 let file = Arc::new(InputFile::new_mouse(MOUSE_INPUT_ID, Some(&child_node)));
317 file_nodes.push(child_node);
318 file
319 }
320 };
321 self.open_files.lock().push(Arc::downgrade(&input_file));
322 Box::new(input_file)
323 }
324
325 #[cfg(test)]
326 pub fn open_test(
327 &self,
328 locked: &mut Locked<Unlocked>,
329 current_task: &CurrentTask,
330 ) -> Result<starnix_core::vfs::FileHandle, Errno> {
331 let input_file = self.open_internal();
332 let root_namespace_node = current_task
333 .lookup_path_from_root(locked, ".".into())
334 .expect("failed to get namespace node for root");
335
336 let file_object = starnix_core::vfs::FileObject::new(
337 locked,
338 current_task,
339 input_file,
340 root_namespace_node,
341 OpenFlags::empty(),
342 )
343 .expect("FileObject::new failed");
344 Ok(file_object)
345 }
346}
347
348impl DeviceOps for InputDevice {
349 fn open(
350 &self,
351 _locked: &mut Locked<FileOpsCore>,
352 _current_task: &CurrentTask,
353 _id: DeviceType,
354 _node: &NamespaceNode,
355 _flags: OpenFlags,
356 ) -> Result<Box<dyn FileOps>, Errno> {
357 let input_file = self.open_internal();
358 Ok(input_file)
359 }
360}
361
362#[cfg(test)]
363mod test {
364 #![allow(clippy::unused_unit)] use super::*;
367 use crate::input_event_relay::{self, EventProxyMode};
368 use anyhow::anyhow;
369 use assert_matches::assert_matches;
370 use diagnostics_assertions::{AnyProperty, assert_data_tree};
371 use fidl_fuchsia_ui_input::MediaButtonsEvent;
372 use fidl_fuchsia_ui_input3 as fuiinput;
373 use fidl_fuchsia_ui_pointer as fuipointer;
374 use fidl_fuchsia_ui_policy as fuipolicy;
375 use fuipointer::{
376 EventPhase, TouchEvent, TouchInteractionId, TouchPointerSample, TouchResponse,
377 TouchSourceMarker, TouchSourceRequest,
378 };
379 use futures::StreamExt as _;
380 use pretty_assertions::assert_eq;
381 use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
382 use starnix_core::task::{EventHandler, Waiter};
383 #[allow(deprecated, reason = "pre-existing usage")]
384 use starnix_core::testing::create_kernel_task_and_unlocked;
385 use starnix_core::vfs::FileHandle;
386 use starnix_core::vfs::buffers::VecOutputBuffer;
387 use starnix_types::time::timeval_from_time;
388 use starnix_uapi::errors::EAGAIN;
389 use starnix_uapi::uapi;
390 use starnix_uapi::vfs::FdEvents;
391 use test_case::test_case;
392 use test_util::assert_near;
393 use zerocopy::FromBytes as _;
394
395 const INPUT_EVENT_SIZE: usize = std::mem::size_of::<uapi::input_event>();
396
397 async fn start_touch_input(
398 locked: &mut Locked<Unlocked>,
399 current_task: &CurrentTask,
400 ) -> (InputDevice, FileHandle, fuipointer::TouchSourceRequestStream) {
401 let inspector = fuchsia_inspect::Inspector::default();
402 start_touch_input_inspect_and_dimensions(locked, current_task, 700, 1200, &inspector).await
403 }
404
405 async fn start_touch_input_inspect(
406 locked: &mut Locked<Unlocked>,
407 current_task: &CurrentTask,
408 inspector: &fuchsia_inspect::Inspector,
409 ) -> (InputDevice, FileHandle, fuipointer::TouchSourceRequestStream) {
410 start_touch_input_inspect_and_dimensions(locked, current_task, 700, 1200, &inspector).await
411 }
412
413 async fn init_keyboard_listener(
414 keyboard_stream: &mut fuiinput::KeyboardRequestStream,
415 ) -> fuiinput::KeyboardListenerProxy {
416 let keyboard_listener = match keyboard_stream.next().await {
417 Some(Ok(fuiinput::KeyboardRequest::AddListener {
418 view_ref: _,
419 listener,
420 responder,
421 })) => {
422 let _ = responder.send();
423 listener.into_proxy()
424 }
425 _ => {
426 panic!("Failed to get event");
427 }
428 };
429
430 keyboard_listener
431 }
432
433 async fn init_button_listeners(
434 device_listener_stream: &mut fuipolicy::DeviceListenerRegistryRequestStream,
435 ) -> (fuipolicy::MediaButtonsListenerProxy, fuipolicy::TouchButtonsListenerProxy) {
436 let media_buttons_listener = match device_listener_stream.next().await {
437 Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterListener {
438 listener,
439 responder,
440 })) => {
441 let _ = responder.send();
442 listener.into_proxy()
443 }
444 _ => {
445 panic!("Failed to get event");
446 }
447 };
448
449 let touch_buttons_listener = match device_listener_stream.next().await {
450 Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterTouchButtonsListener {
451 listener,
452 responder,
453 })) => {
454 let _ = responder.send();
455 listener.into_proxy()
456 }
457 _ => {
458 panic!("Failed to get event");
459 }
460 };
461
462 (media_buttons_listener, touch_buttons_listener)
463 }
464
465 async fn start_touch_input_inspect_and_dimensions(
466 locked: &mut Locked<Unlocked>,
467 current_task: &CurrentTask,
468 x_max: i32,
469 y_max: i32,
470 inspector: &fuchsia_inspect::Inspector,
471 ) -> (InputDevice, FileHandle, fuipointer::TouchSourceRequestStream) {
472 let input_device = InputDevice::new_touch(x_max, y_max, inspector.root());
473 let input_file =
474 input_device.open_test(locked, current_task).expect("Failed to create input file");
475
476 let (touch_source_client_end, touch_source_stream) =
477 fidl::endpoints::create_request_stream::<TouchSourceMarker>();
478
479 let (mouse_source_client_end, _mouse_source_stream) =
480 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceMarker>();
481
482 let (keyboard_proxy, mut keyboard_stream) =
483 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
484 let view_ref_pair =
485 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
486
487 let (device_registry_proxy, mut device_listener_stream) =
488 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
489 );
490
491 let (relay, _relay_handle) = input_event_relay::new_input_relay();
492 relay.start_relays(
493 ¤t_task.kernel(),
494 EventProxyMode::None,
495 touch_source_client_end,
496 keyboard_proxy,
497 mouse_source_client_end,
498 view_ref_pair.view_ref,
499 device_registry_proxy,
500 input_device.open_files.clone(),
501 Default::default(),
502 Default::default(),
503 Some(input_device.inspect_status.clone()),
504 None,
505 None,
506 );
507
508 let _ = init_keyboard_listener(&mut keyboard_stream).await;
509 let _ = init_button_listeners(&mut device_listener_stream).await;
510
511 (input_device, input_file, touch_source_stream)
512 }
513
514 async fn start_keyboard_input(
515 locked: &mut Locked<Unlocked>,
516 current_task: &CurrentTask,
517 ) -> (InputDevice, FileHandle, fuiinput::KeyboardListenerProxy) {
518 let inspector = fuchsia_inspect::Inspector::default();
519 let input_device = InputDevice::new_keyboard(inspector.root());
520 let input_file =
521 input_device.open_test(locked, current_task).expect("Failed to create input file");
522 let (keyboard_proxy, mut keyboard_stream) =
523 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
524 let view_ref_pair =
525 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
526
527 let (device_registry_proxy, mut device_listener_stream) =
528 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
529 );
530
531 let (touch_source_client_end, _touch_source_stream) =
532 fidl::endpoints::create_request_stream::<TouchSourceMarker>();
533
534 let (mouse_source_client_end, _mouse_source_stream) =
535 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceMarker>();
536
537 let (relay, _relay_handle) = input_event_relay::new_input_relay();
538 relay.start_relays(
539 current_task.kernel(),
540 EventProxyMode::None,
541 touch_source_client_end,
542 keyboard_proxy,
543 mouse_source_client_end,
544 view_ref_pair.view_ref,
545 device_registry_proxy,
546 Default::default(),
547 input_device.open_files.clone(),
548 Default::default(),
549 None,
550 Some(input_device.inspect_status.clone()),
551 None,
552 );
553
554 let keyboad_listener = init_keyboard_listener(&mut keyboard_stream).await;
555 let _ = init_button_listeners(&mut device_listener_stream).await;
556
557 (input_device, input_file, keyboad_listener)
558 }
559
560 async fn start_button_input(
561 locked: &mut Locked<Unlocked>,
562 current_task: &CurrentTask,
563 ) -> (InputDevice, FileHandle, fuipolicy::MediaButtonsListenerProxy) {
564 let inspector = fuchsia_inspect::Inspector::default();
565 start_button_input_inspect(locked, current_task, &inspector).await
566 }
567
568 async fn start_button_input_inspect(
569 locked: &mut Locked<Unlocked>,
570 current_task: &CurrentTask,
571 inspector: &fuchsia_inspect::Inspector,
572 ) -> (InputDevice, FileHandle, fuipolicy::MediaButtonsListenerProxy) {
573 let input_device = InputDevice::new_keyboard(inspector.root());
574 let input_file =
575 input_device.open_test(locked, current_task).expect("Failed to create input file");
576 let (device_registry_proxy, mut device_listener_stream) =
577 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
578 );
579
580 let (touch_source_client_end, _touch_source_stream) =
581 fidl::endpoints::create_request_stream::<TouchSourceMarker>();
582 let (mouse_source_client_end, _mouse_source_stream) =
583 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceMarker>();
584 let (keyboard_proxy, mut keyboard_stream) =
585 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
586 let view_ref_pair =
587 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
588
589 let (relay, _relay_handle) = input_event_relay::new_input_relay();
590 relay.start_relays(
591 current_task.kernel(),
592 EventProxyMode::None,
593 touch_source_client_end,
594 keyboard_proxy,
595 mouse_source_client_end,
596 view_ref_pair.view_ref,
597 device_registry_proxy,
598 Default::default(),
599 input_device.open_files.clone(),
600 Default::default(),
601 None,
602 Some(input_device.inspect_status.clone()),
603 None,
604 );
605
606 let _ = init_keyboard_listener(&mut keyboard_stream).await;
607 let (button_listener, _) = init_button_listeners(&mut device_listener_stream).await;
608
609 (input_device, input_file, button_listener)
610 }
611
612 async fn start_mouse_input(
613 locked: &mut Locked<Unlocked>,
614 current_task: &CurrentTask,
615 ) -> (InputDevice, FileHandle, fuipointer::MouseSourceRequestStream) {
616 let inspector = fuchsia_inspect::Inspector::default();
617 start_mouse_input_inspect(locked, current_task, &inspector).await
618 }
619
620 async fn start_mouse_input_inspect(
621 locked: &mut Locked<Unlocked>,
622 current_task: &CurrentTask,
623 inspector: &fuchsia_inspect::Inspector,
624 ) -> (InputDevice, FileHandle, fuipointer::MouseSourceRequestStream) {
625 let input_device = InputDevice::new_mouse(inspector.root());
626 let input_file =
627 input_device.open_test(locked, current_task).expect("Failed to create input file");
628
629 let (touch_source_client_end, _touch_source_stream) =
630 fidl::endpoints::create_request_stream::<TouchSourceMarker>();
631
632 let (mouse_source_client_end, mouse_source_stream) =
633 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceMarker>();
634
635 let (keyboard_proxy, mut keyboard_stream) =
636 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
637 let view_ref_pair =
638 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
639
640 let (device_registry_proxy, mut device_listener_stream) =
641 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
642 );
643
644 let (relay, _relay_handle) = input_event_relay::new_input_relay();
645 relay.start_relays(
646 ¤t_task.kernel(),
647 EventProxyMode::None,
648 touch_source_client_end,
649 keyboard_proxy,
650 mouse_source_client_end,
651 view_ref_pair.view_ref,
652 device_registry_proxy,
653 Default::default(),
654 Default::default(),
655 input_device.open_files.clone(),
656 None,
657 None,
658 Some(input_device.inspect_status.clone()),
659 );
660
661 let _ = init_keyboard_listener(&mut keyboard_stream).await;
662 let _ = init_button_listeners(&mut device_listener_stream).await;
663
664 (input_device, input_file, mouse_source_stream)
665 }
666
667 fn make_touch_event(pointer_id: u32) -> fuipointer::TouchEvent {
668 make_touch_event_with_phase(EventPhase::Change, pointer_id)
670 }
671
672 fn make_touch_event_with_phase(phase: EventPhase, pointer_id: u32) -> fuipointer::TouchEvent {
673 make_touch_event_with_coords_phase(0.0, 0.0, phase, pointer_id)
674 }
675
676 fn make_touch_event_with_coords_phase(
677 x: f32,
678 y: f32,
679 phase: EventPhase,
680 pointer_id: u32,
681 ) -> fuipointer::TouchEvent {
682 make_touch_event_with_coords_phase_timestamp(x, y, phase, pointer_id, 0)
683 }
684
685 fn make_touch_event_with_coords(x: f32, y: f32, pointer_id: u32) -> fuipointer::TouchEvent {
686 make_touch_event_with_coords_phase(x, y, EventPhase::Change, pointer_id)
687 }
688
689 fn make_touch_event_with_coords_phase_timestamp(
690 x: f32,
691 y: f32,
692 phase: EventPhase,
693 pointer_id: u32,
694 time_nanos: i64,
695 ) -> fuipointer::TouchEvent {
696 make_touch_event_with_coords_phase_timestamp_device_id(
697 x, y, phase, pointer_id, time_nanos, 0,
698 )
699 }
700
701 fn make_empty_touch_event() -> fuipointer::TouchEvent {
702 TouchEvent {
703 pointer_sample: Some(TouchPointerSample {
704 interaction: Some(TouchInteractionId {
705 pointer_id: 0,
706 device_id: 0,
707 interaction_id: 0,
708 }),
709 ..Default::default()
710 }),
711 ..Default::default()
712 }
713 }
714
715 fn make_touch_event_with_coords_phase_timestamp_device_id(
716 x: f32,
717 y: f32,
718 phase: EventPhase,
719 pointer_id: u32,
720 time_nanos: i64,
721 device_id: u32,
722 ) -> fuipointer::TouchEvent {
723 TouchEvent {
724 timestamp: Some(time_nanos),
725 pointer_sample: Some(TouchPointerSample {
726 position_in_viewport: Some([x, y]),
727 phase: Some(phase),
729 interaction: Some(TouchInteractionId { pointer_id, device_id, interaction_id: 0 }),
730 ..Default::default()
731 }),
732 ..Default::default()
733 }
734 }
735
736 fn make_mouse_wheel_event(ticks: i64) -> fuipointer::MouseEvent {
737 make_mouse_wheel_event_with_timestamp(ticks, 0)
738 }
739
740 fn make_mouse_wheel_event_with_timestamp(ticks: i64, timestamp: i64) -> fuipointer::MouseEvent {
741 fuipointer::MouseEvent {
742 timestamp: Some(timestamp),
743 pointer_sample: Some(fuipointer::MousePointerSample {
744 device_id: Some(0),
745 scroll_v: Some(ticks),
746 ..Default::default()
747 }),
748 ..Default::default()
749 }
750 }
751
752 fn read_uapi_events<L>(
753 locked: &mut Locked<L>,
754 file: &FileHandle,
755 current_task: &CurrentTask,
756 ) -> Vec<uapi::input_event>
757 where
758 L: LockEqualOrBefore<FileOpsCore>,
759 {
760 std::iter::from_fn(|| {
761 let locked = locked.cast_locked::<FileOpsCore>();
762 let mut event_bytes = VecOutputBuffer::new(INPUT_EVENT_SIZE);
763 match file.read(locked, current_task, &mut event_bytes) {
764 Ok(INPUT_EVENT_SIZE) => Some(
765 uapi::input_event::read_from_bytes(Vec::from(event_bytes).as_slice())
766 .map_err(|_| anyhow!("failed to read input_event from buffer")),
767 ),
768 Ok(other_size) => {
769 Some(Err(anyhow!("got {} bytes (expected {})", other_size, INPUT_EVENT_SIZE)))
770 }
771 Err(Errno { code: EAGAIN, .. }) => None,
772 Err(other_error) => Some(Err(anyhow!("read failed: {:?}", other_error))),
773 }
774 })
775 .enumerate()
776 .map(|(i, read_res)| match read_res {
777 Ok(event) => event,
778 Err(e) => panic!("unexpected result {:?} on iteration {}", e, i),
779 })
780 .collect()
781 }
782
783 async fn answer_next_touch_watch_request(
786 request_stream: &mut fuipointer::TouchSourceRequestStream,
787 touch_events: Vec<TouchEvent>,
788 ) -> Vec<TouchResponse> {
789 match request_stream.next().await {
790 Some(Ok(TouchSourceRequest::Watch { responses, responder })) => {
791 responder.send(touch_events).expect("failure sending Watch reply");
792 responses
793 }
794 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
795 }
796 }
797
798 async fn answer_next_mouse_watch_request(
801 request_stream: &mut fuipointer::MouseSourceRequestStream,
802 mouse_events: Vec<fuipointer::MouseEvent>,
803 ) {
804 match request_stream.next().await {
805 Some(Ok(fuipointer::MouseSourceRequest::Watch { responder })) => {
806 responder.send(mouse_events).expect("failure sending Watch reply");
807 }
808 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
809 }
810 }
811
812 #[::fuchsia::test()]
813 async fn initial_watch_request_has_empty_responses_arg() {
814 #[allow(deprecated, reason = "pre-existing usage")]
815 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
816 let (_input_device, _input_file, mut touch_source_stream) =
818 start_touch_input(locked, ¤t_task).await;
819
820 assert_matches!(
822 touch_source_stream.next().await,
823 Some(Ok(TouchSourceRequest::Watch { responses, .. }))
824 => assert_eq!(responses.as_slice(), [])
825 );
826 }
827
828 #[::fuchsia::test]
829 async fn later_watch_requests_have_responses_arg_matching_earlier_watch_replies() {
830 #[allow(deprecated, reason = "pre-existing usage")]
832 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
833 let (_input_device, _input_file, mut touch_source_stream) =
834 start_touch_input(locked, ¤t_task).await;
835
836 match touch_source_stream.next().await {
838 Some(Ok(TouchSourceRequest::Watch { responder, .. })) => responder
839 .send(vec![make_empty_touch_event(), make_empty_touch_event()])
840 .expect("failure sending Watch reply"),
841 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
842 }
843
844 match touch_source_stream.next().await {
847 Some(Ok(TouchSourceRequest::Watch { responses, responder })) => {
848 assert_matches!(responses.as_slice(), [_, _]);
849 responder
850 .send(vec![
851 make_empty_touch_event(),
852 make_empty_touch_event(),
853 make_empty_touch_event(),
854 make_empty_touch_event(),
855 make_empty_touch_event(),
856 ])
857 .expect("failure sending Watch reply")
858 }
859 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
860 }
861
862 match touch_source_stream.next().await {
864 Some(Ok(TouchSourceRequest::Watch { responses, .. })) => {
865 assert_matches!(responses.as_slice(), [_, _, _, _, _]);
866 }
867 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
868 }
869 }
870
871 #[::fuchsia::test]
872 async fn notifies_polling_waiters_of_new_data() {
873 #[allow(deprecated, reason = "pre-existing usage")]
875 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
876 let (_input_device, input_file, mut touch_source_stream) =
877 start_touch_input(locked, ¤t_task).await;
878 let waiter1 = Waiter::new();
879 let waiter2 = Waiter::new();
880
881 [&waiter1, &waiter2].iter().for_each(|waiter| {
883 input_file.wait_async(
884 locked,
885 ¤t_task,
886 waiter,
887 FdEvents::POLLIN,
888 EventHandler::None,
889 );
890 });
891 assert_matches!(
892 waiter1.wait_until(locked, ¤t_task, zx::MonotonicInstant::ZERO),
893 Err(_)
894 );
895 assert_matches!(
896 waiter2.wait_until(locked, ¤t_task, zx::MonotonicInstant::ZERO),
897 Err(_)
898 );
899
900 answer_next_touch_watch_request(
902 &mut touch_source_stream,
903 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
904 )
905 .await;
906 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)]).await;
907
908 assert_eq!(waiter1.wait_until(locked, ¤t_task, zx::MonotonicInstant::ZERO), Ok(()));
912 assert_eq!(waiter2.wait_until(locked, ¤t_task, zx::MonotonicInstant::ZERO), Ok(()));
913 }
914
915 #[::fuchsia::test]
916 async fn notifies_blocked_waiter_of_new_data() {
917 #[allow(deprecated, reason = "pre-existing usage")]
919 let (kernel, current_task, locked) = create_kernel_task_and_unlocked();
920 let (_input_device, input_file, mut touch_source_stream) =
921 start_touch_input(locked, ¤t_task).await;
922 let waiter = Waiter::new();
923
924 input_file.wait_async(locked, ¤t_task, &waiter, FdEvents::POLLIN, EventHandler::None);
926
927 let closure =
928 move |locked: &mut Locked<Unlocked>, task: &CurrentTask| waiter.wait(locked, &task);
929
930 let (waiter_thread, req) = SpawnRequestBuilder::new()
931 .with_debug_name("input-device-waiter")
932 .with_sync_closure(closure)
933 .build_with_async_result();
934 kernel.kthreads.spawner().spawn_from_request(req);
935
936 let mut waiter_thread = Box::pin(waiter_thread);
937 assert_matches!(futures::poll!(&mut waiter_thread), futures::task::Poll::Pending);
938
939 answer_next_touch_watch_request(
941 &mut touch_source_stream,
942 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
943 )
944 .await;
945
946 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)]).await;
952 }
953
954 #[::fuchsia::test]
955 async fn does_not_notify_polling_waiters_without_new_data() {
956 #[allow(deprecated, reason = "pre-existing usage")]
958 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
959 let (_input_device, input_file, mut touch_source_stream) =
960 start_touch_input(locked, ¤t_task).await;
961 let waiter1 = Waiter::new();
962 let waiter2 = Waiter::new();
963
964 [&waiter1, &waiter2].iter().for_each(|waiter| {
966 input_file.wait_async(
967 locked,
968 ¤t_task,
969 waiter,
970 FdEvents::POLLIN,
971 EventHandler::None,
972 );
973 });
974 assert_matches!(
975 waiter1.wait_until(locked, ¤t_task, zx::MonotonicInstant::ZERO),
976 Err(_)
977 );
978 assert_matches!(
979 waiter2.wait_until(locked, ¤t_task, zx::MonotonicInstant::ZERO),
980 Err(_)
981 );
982
983 answer_next_touch_watch_request(&mut touch_source_stream, vec![]).await;
985
986 assert_matches!(
990 waiter1.wait_until(locked, ¤t_task, zx::MonotonicInstant::ZERO),
991 Err(_)
992 );
993 assert_matches!(
994 waiter2.wait_until(locked, ¤t_task, zx::MonotonicInstant::ZERO),
995 Err(_)
996 );
997 }
998
999 #[::fuchsia::test]
1012 async fn honors_wait_cancellation() {
1013 #[allow(deprecated, reason = "pre-existing usage")]
1015 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1016 let (_input_device, input_file, mut touch_source_stream) =
1017 start_touch_input(locked, ¤t_task).await;
1018 let waiter1 = Waiter::new();
1019 let waiter2 = Waiter::new();
1020
1021 let waitkeys = [&waiter1, &waiter2]
1023 .iter()
1024 .map(|waiter| {
1025 input_file
1026 .wait_async(locked, ¤t_task, waiter, FdEvents::POLLIN, EventHandler::None)
1027 .expect("wait_async")
1028 })
1029 .collect::<Vec<_>>();
1030
1031 waitkeys.into_iter().next().expect("failed to get first waitkey").cancel();
1033
1034 answer_next_touch_watch_request(
1036 &mut touch_source_stream,
1037 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1038 )
1039 .await;
1040 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)]).await;
1042
1043 assert_matches!(
1047 waiter1.wait_until(locked, ¤t_task, zx::MonotonicInstant::ZERO),
1048 Err(_)
1049 );
1050 assert_eq!(waiter2.wait_until(locked, ¤t_task, zx::MonotonicInstant::ZERO), Ok(()));
1051 }
1052
1053 #[::fuchsia::test]
1054 async fn query_events() {
1055 #[allow(deprecated, reason = "pre-existing usage")]
1057 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1058 let (_input_device, input_file, mut touch_source_stream) =
1059 start_touch_input(locked, ¤t_task).await;
1060
1061 assert_eq!(
1063 input_file.query_events(locked, ¤t_task).expect("query_events"),
1064 FdEvents::empty(),
1065 "events should be empty before data arrives"
1066 );
1067
1068 answer_next_touch_watch_request(
1070 &mut touch_source_stream,
1071 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1072 )
1073 .await;
1074
1075 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)]).await;
1077
1078 assert_eq!(
1080 input_file.query_events(locked, ¤t_task).expect("query_events"),
1081 FdEvents::POLLIN | FdEvents::POLLRDNORM,
1082 "events should be POLLIN after data arrives"
1083 );
1084 }
1085
1086 fn make_uapi_input_event(ty: u32, code: u32, value: i32) -> uapi::input_event {
1087 make_uapi_input_event_with_timestamp(ty, code, value, 0)
1088 }
1089
1090 fn make_uapi_input_event_with_timestamp(
1091 ty: u32,
1092 code: u32,
1093 value: i32,
1094 time_nanos: i64,
1095 ) -> uapi::input_event {
1096 uapi::input_event {
1097 time: timeval_from_time(zx::MonotonicInstant::from_nanos(time_nanos)),
1098 type_: ty as u16,
1099 code: code as u16,
1100 value,
1101 }
1102 }
1103
1104 #[::fuchsia::test]
1105 async fn touch_event_ignored() {
1106 let inspector = fuchsia_inspect::Inspector::default();
1108 #[allow(deprecated, reason = "pre-existing usage")]
1109 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1110 let (_input_device, input_file, mut touch_source_stream) =
1111 start_touch_input_inspect(locked, ¤t_task, &inspector).await;
1112
1113 answer_next_touch_watch_request(
1116 &mut touch_source_stream,
1117 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1118 )
1119 .await;
1120
1121 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_empty_touch_event()])
1125 .await;
1126
1127 let events = read_uapi_events(locked, &input_file, ¤t_task);
1129
1130 assert_eq!(events.len(), 6);
1131
1132 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_empty_touch_event()])
1135 .await;
1136
1137 match touch_source_stream.next().await {
1139 Some(Ok(TouchSourceRequest::Watch { responses, .. })) => {
1140 assert_matches!(responses.as_slice(), [_])
1141 }
1142 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1143 }
1144
1145 let events = read_uapi_events(locked, &input_file, ¤t_task);
1146 assert_eq!(events, vec![]);
1147 assert_data_tree!(inspector, root: {
1148 touch_device: {
1149 total_fidl_events_received_count: 3u64,
1150 total_fidl_events_ignored_count: 2u64,
1151 total_fidl_events_unexpected_count: 0u64,
1152 total_fidl_events_converted_count: 1u64,
1153 total_uapi_events_generated_count: 6u64,
1154 last_generated_uapi_event_timestamp_ns: 0i64,
1155 touch_file_0: {
1156 fidl_events_received_count: 3u64,
1157 fidl_events_ignored_count: 2u64,
1158 fidl_events_unexpected_count: 0u64,
1159 fidl_events_converted_count: 1u64,
1160 uapi_events_generated_count: 6u64,
1161 uapi_events_read_count: 6u64,
1162 last_generated_uapi_event_timestamp_ns: 0i64,
1163 last_read_uapi_event_timestamp_ns: 0i64,
1164 },
1165 }
1166 });
1167 }
1168
1169 #[test_case(make_touch_event_with_phase(EventPhase::Add, 1); "touch add for pointer already added")]
1170 #[test_case(make_touch_event_with_phase(EventPhase::Change, 2); "touch change for pointer not added")]
1171 #[test_case(make_touch_event_with_phase(EventPhase::Remove, 2); "touch remove for pointer not added")]
1172 #[test_case(make_touch_event_with_phase(EventPhase::Cancel, 1); "touch cancel")]
1173 #[::fuchsia::test]
1174 async fn touch_event_unexpected(event: TouchEvent) {
1175 let inspector = fuchsia_inspect::Inspector::default();
1177 #[allow(deprecated, reason = "pre-existing usage")]
1178 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1179 let (_input_device, input_file, mut touch_source_stream) =
1180 start_touch_input_inspect(locked, ¤t_task, &inspector).await;
1181
1182 answer_next_touch_watch_request(
1185 &mut touch_source_stream,
1186 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1187 )
1188 .await;
1189
1190 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_empty_touch_event()])
1194 .await;
1195
1196 let events = read_uapi_events(locked, &input_file, ¤t_task);
1198
1199 assert_eq!(events.len(), 6);
1200
1201 answer_next_touch_watch_request(&mut touch_source_stream, vec![event]).await;
1204
1205 match touch_source_stream.next().await {
1207 Some(Ok(TouchSourceRequest::Watch { responses, .. })) => {
1208 assert_matches!(responses.as_slice(), [_])
1209 }
1210 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1211 }
1212
1213 let events = read_uapi_events(locked, &input_file, ¤t_task);
1214 assert_eq!(events, vec![]);
1215 assert_data_tree!(inspector, root: {
1216 touch_device: {
1217 total_fidl_events_received_count: 3u64,
1218 total_fidl_events_ignored_count: 1u64,
1219 total_fidl_events_unexpected_count: 1u64,
1220 total_fidl_events_converted_count: 1u64,
1221 total_uapi_events_generated_count: 6u64,
1222 last_generated_uapi_event_timestamp_ns: 0i64,
1223 touch_file_0: {
1224 fidl_events_received_count: 3u64,
1225 fidl_events_ignored_count: 1u64,
1226 fidl_events_unexpected_count: 1u64,
1227 fidl_events_converted_count: 1u64,
1228 uapi_events_generated_count: 6u64,
1229 uapi_events_read_count: 6u64,
1230 last_generated_uapi_event_timestamp_ns: 0i64,
1231 last_read_uapi_event_timestamp_ns: 0i64,
1232 },
1233 }
1234 });
1235 }
1236
1237 #[::fuchsia::test]
1238 async fn translates_touch_add() {
1239 #[allow(deprecated, reason = "pre-existing usage")]
1241 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1242 let (_input_device, input_file, mut touch_source_stream) =
1243 start_touch_input(locked, ¤t_task).await;
1244
1245 answer_next_touch_watch_request(
1247 &mut touch_source_stream,
1248 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1249 )
1250 .await;
1251
1252 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_empty_touch_event()])
1256 .await;
1257
1258 let events = read_uapi_events(locked, &input_file, ¤t_task);
1260
1261 assert_eq!(
1262 events,
1263 vec![
1264 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1265 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1),
1266 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 0),
1267 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 0),
1268 make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 1),
1269 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1270 ]
1271 );
1272 }
1273
1274 #[::fuchsia::test]
1275 async fn translates_touch_change() {
1276 #[allow(deprecated, reason = "pre-existing usage")]
1278 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1279 let (_input_device, input_file, mut touch_source_stream) =
1280 start_touch_input(locked, ¤t_task).await;
1281
1282 answer_next_touch_watch_request(
1284 &mut touch_source_stream,
1285 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1286 )
1287 .await;
1288
1289 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1293 .await;
1294
1295 let events = read_uapi_events(locked, &input_file, ¤t_task);
1297
1298 assert_eq!(events.len(), 6);
1299
1300 answer_next_touch_watch_request(
1302 &mut touch_source_stream,
1303 vec![make_touch_event_with_coords(10.0, 20.0, 1)],
1304 )
1305 .await;
1306
1307 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1309 .await;
1310
1311 let events = read_uapi_events(locked, &input_file, ¤t_task);
1312 assert_eq!(
1313 events,
1314 vec![
1315 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1316 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
1317 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
1318 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1319 ]
1320 );
1321 }
1322
1323 #[::fuchsia::test]
1324 async fn translates_touch_remove() {
1325 #[allow(deprecated, reason = "pre-existing usage")]
1327 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1328 let (_input_device, input_file, mut touch_source_stream) =
1329 start_touch_input(locked, ¤t_task).await;
1330
1331 answer_next_touch_watch_request(
1333 &mut touch_source_stream,
1334 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1335 )
1336 .await;
1337
1338 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1342 .await;
1343
1344 let events = read_uapi_events(locked, &input_file, ¤t_task);
1346
1347 assert_eq!(events.len(), 6);
1348
1349 answer_next_touch_watch_request(
1351 &mut touch_source_stream,
1352 vec![make_touch_event_with_phase(EventPhase::Remove, 1)],
1353 )
1354 .await;
1355
1356 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1358 .await;
1359
1360 let events = read_uapi_events(locked, &input_file, ¤t_task);
1361 assert_eq!(
1362 events,
1363 vec![
1364 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1365 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1366 make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 0),
1367 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1368 ]
1369 );
1370 }
1371
1372 #[::fuchsia::test]
1373 async fn multi_touch_event_sequence() {
1374 #[allow(deprecated, reason = "pre-existing usage")]
1376 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1377 let (_input_device, input_file, mut touch_source_stream) =
1378 start_touch_input(locked, ¤t_task).await;
1379
1380 answer_next_touch_watch_request(
1382 &mut touch_source_stream,
1383 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1384 )
1385 .await;
1386
1387 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1389 .await;
1390 let events = read_uapi_events(locked, &input_file, ¤t_task);
1391
1392 assert_eq!(events.len(), 6);
1393
1394 answer_next_touch_watch_request(
1396 &mut touch_source_stream,
1397 vec![
1398 make_touch_event_with_coords(10.0, 20.0, 1),
1399 make_touch_event_with_phase(EventPhase::Add, 2),
1400 ],
1401 )
1402 .await;
1403
1404 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1406 .await;
1407 let events = read_uapi_events(locked, &input_file, ¤t_task);
1408
1409 assert_eq!(
1410 events,
1411 vec![
1412 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1413 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
1414 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
1415 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1416 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 2),
1417 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 0),
1418 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 0),
1419 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1420 ]
1421 );
1422
1423 answer_next_touch_watch_request(
1425 &mut touch_source_stream,
1426 vec![
1427 make_touch_event_with_coords(11.0, 21.0, 1),
1428 make_touch_event_with_coords(101.0, 201.0, 2),
1429 ],
1430 )
1431 .await;
1432
1433 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1435 .await;
1436 let events = read_uapi_events(locked, &input_file, ¤t_task);
1437
1438 assert_eq!(
1439 events,
1440 vec![
1441 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1442 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 11),
1443 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 21),
1444 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1445 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 101),
1446 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 201),
1447 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1448 ]
1449 );
1450
1451 answer_next_touch_watch_request(
1453 &mut touch_source_stream,
1454 vec![
1455 make_touch_event_with_phase(EventPhase::Remove, 1),
1456 make_touch_event_with_coords(102.0, 202.0, 2),
1457 ],
1458 )
1459 .await;
1460
1461 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1463 .await;
1464 let events = read_uapi_events(locked, &input_file, ¤t_task);
1465
1466 assert_eq!(
1467 events,
1468 vec![
1469 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1470 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1471 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1472 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 102),
1473 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 202),
1474 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1475 ]
1476 );
1477
1478 answer_next_touch_watch_request(
1480 &mut touch_source_stream,
1481 vec![make_touch_event_with_phase(EventPhase::Remove, 2)],
1482 )
1483 .await;
1484
1485 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1487 .await;
1488 let events = read_uapi_events(locked, &input_file, ¤t_task);
1489
1490 assert_eq!(
1491 events,
1492 vec![
1493 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1494 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1495 make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 0),
1496 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1497 ]
1498 );
1499 }
1500
1501 #[::fuchsia::test]
1502 async fn multi_event_sequence_unsorted_in_one_watch() {
1503 #[allow(deprecated, reason = "pre-existing usage")]
1505 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1506 let (_input_device, input_file, mut touch_source_stream) =
1507 start_touch_input(locked, ¤t_task).await;
1508
1509 answer_next_touch_watch_request(
1511 &mut touch_source_stream,
1512 vec![
1513 make_touch_event_with_coords_phase_timestamp(
1514 10.0,
1515 20.0,
1516 EventPhase::Change,
1517 1,
1518 100,
1519 ),
1520 make_touch_event_with_coords_phase_timestamp(0.0, 0.0, EventPhase::Add, 1, 1),
1521 ],
1522 )
1523 .await;
1524
1525 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1527 .await;
1528 let events = read_uapi_events(locked, &input_file, ¤t_task);
1529
1530 assert_eq!(
1531 events,
1532 vec![
1533 make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0, 1),
1534 make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1, 1),
1535 make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 0, 1),
1536 make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 0, 1),
1537 make_uapi_input_event_with_timestamp(uapi::EV_KEY, uapi::BTN_TOUCH, 1, 1),
1538 make_uapi_input_event_with_timestamp(uapi::EV_SYN, uapi::SYN_REPORT, 0, 1),
1539 make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0, 100),
1540 make_uapi_input_event_with_timestamp(
1541 uapi::EV_ABS,
1542 uapi::ABS_MT_POSITION_X,
1543 10,
1544 100
1545 ),
1546 make_uapi_input_event_with_timestamp(
1547 uapi::EV_ABS,
1548 uapi::ABS_MT_POSITION_Y,
1549 20,
1550 100
1551 ),
1552 make_uapi_input_event_with_timestamp(uapi::EV_SYN, uapi::SYN_REPORT, 0, 100),
1553 ]
1554 );
1555 }
1556
1557 #[test_case((0.0, 0.0); "origin")]
1558 #[test_case((100.7, 200.7); "above midpoint")]
1559 #[test_case((100.3, 200.3); "below midpoint")]
1560 #[test_case((100.5, 200.5); "midpoint")]
1561 #[::fuchsia::test]
1562 async fn sends_acceptable_coordinates((x, y): (f32, f32)) {
1563 #[allow(deprecated, reason = "pre-existing usage")]
1565 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1566 let (_input_device, input_file, mut touch_source_stream) =
1567 start_touch_input(locked, ¤t_task).await;
1568
1569 answer_next_touch_watch_request(
1571 &mut touch_source_stream,
1572 vec![make_touch_event_with_coords_phase(x, y, EventPhase::Add, 1)],
1573 )
1574 .await;
1575
1576 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1578 .await;
1579 let events = read_uapi_events(locked, &input_file, ¤t_task);
1580
1581 const ACCEPTABLE_ERROR: f32 = 1.0;
1584 let actual_x = events
1585 .iter()
1586 .find(|event| {
1587 event.type_ == uapi::EV_ABS as u16 && event.code == uapi::ABS_MT_POSITION_X as u16
1588 })
1589 .unwrap_or_else(|| panic!("did not find `ABS_X` event in {:?}", events))
1590 .value;
1591 let actual_y = events
1592 .iter()
1593 .find(|event| {
1594 event.type_ == uapi::EV_ABS as u16 && event.code == uapi::ABS_MT_POSITION_Y as u16
1595 })
1596 .unwrap_or_else(|| panic!("did not find `ABS_Y` event in {:?}", events))
1597 .value;
1598 assert_near!(x, actual_x as f32, ACCEPTABLE_ERROR);
1599 assert_near!(y, actual_y as f32, ACCEPTABLE_ERROR);
1600 }
1601
1602 #[test_case(
1607 make_touch_event_with_phase(EventPhase::Add, 2)
1608 => matches Some(TouchResponse { response_type: Some(_), ..});
1609 "event_with_sample_yields_some_response_type")]
1610 #[test_case(
1611 TouchEvent::default() => matches Some(TouchResponse { response_type: None, ..});
1612 "event_without_sample_yields_no_response_type")]
1613 #[::fuchsia::test]
1614 async fn sends_appropriate_reply_to_touch_source_server(
1615 event: TouchEvent,
1616 ) -> Option<TouchResponse> {
1617 #[allow(deprecated, reason = "pre-existing usage")]
1619 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1620 let (_input_device, _input_file, mut touch_source_stream) =
1621 start_touch_input(locked, ¤t_task).await;
1622
1623 answer_next_touch_watch_request(&mut touch_source_stream, vec![event]).await;
1625
1626 let responses =
1628 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1629 .await;
1630
1631 responses.get(0).cloned()
1633 }
1634
1635 #[test_case(fidl_fuchsia_input::Key::Escape, uapi::KEY_POWER; "Esc maps to Power")]
1636 #[test_case(fidl_fuchsia_input::Key::A, uapi::KEY_A; "A maps to A")]
1637 #[::fuchsia::test]
1638 async fn sends_keyboard_events(fkey: fidl_fuchsia_input::Key, lkey: u32) {
1639 #[allow(deprecated, reason = "pre-existing usage")]
1640 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1641 let (_keyboard_device, keyboard_file, keyboard_listener) =
1642 start_keyboard_input(locked, ¤t_task).await;
1643
1644 let key_event = fuiinput::KeyEvent {
1645 timestamp: Some(0),
1646 type_: Some(fuiinput::KeyEventType::Pressed),
1647 key: Some(fkey),
1648 ..Default::default()
1649 };
1650
1651 let _ = keyboard_listener.on_key_event(&key_event).await;
1652 std::mem::drop(keyboard_listener); let events = read_uapi_events(locked, &keyboard_file, ¤t_task);
1654 assert_eq!(events.len(), 2);
1655 assert_eq!(events[0].code, lkey as u16);
1656 }
1657
1658 #[::fuchsia::test]
1659 async fn skips_unknown_keyboard_events() {
1660 #[allow(deprecated, reason = "pre-existing usage")]
1661 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1662 let (_keyboard_device, keyboard_file, keyboard_listener) =
1663 start_keyboard_input(locked, ¤t_task).await;
1664
1665 let key_event = fuiinput::KeyEvent {
1666 timestamp: Some(0),
1667 type_: Some(fuiinput::KeyEventType::Pressed),
1668 key: Some(fidl_fuchsia_input::Key::AcRefresh),
1669 ..Default::default()
1670 };
1671
1672 let _ = keyboard_listener.on_key_event(&key_event).await;
1673 std::mem::drop(keyboard_listener); let events = read_uapi_events(locked, &keyboard_file, ¤t_task);
1675 assert_eq!(events.len(), 0);
1676 }
1677
1678 #[::fuchsia::test]
1679 async fn sends_power_button_events() {
1680 #[allow(deprecated, reason = "pre-existing usage")]
1681 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1682 let (_input_device, input_file, buttons_listener) =
1683 start_button_input(locked, ¤t_task).await;
1684
1685 let power_event = MediaButtonsEvent {
1686 volume: Some(0),
1687 mic_mute: Some(false),
1688 pause: Some(false),
1689 camera_disable: Some(false),
1690 power: Some(true),
1691 function: Some(false),
1692 ..Default::default()
1693 };
1694
1695 let _ = buttons_listener.on_event(power_event).await;
1696 std::mem::drop(buttons_listener); let events = read_uapi_events(locked, &input_file, ¤t_task);
1699 assert_eq!(events.len(), 2);
1700 assert_eq!(events[0].code, uapi::KEY_POWER as u16);
1701 assert_eq!(events[0].value, 1);
1702 }
1703
1704 #[::fuchsia::test]
1705 async fn sends_function_button_events() {
1706 #[allow(deprecated, reason = "pre-existing usage")]
1707 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1708 let (_input_device, input_file, buttons_listener) =
1709 start_button_input(locked, ¤t_task).await;
1710
1711 let function_event = MediaButtonsEvent {
1712 volume: Some(0),
1713 mic_mute: Some(false),
1714 pause: Some(false),
1715 camera_disable: Some(false),
1716 power: Some(false),
1717 function: Some(true),
1718 ..Default::default()
1719 };
1720
1721 let _ = buttons_listener.on_event(function_event).await;
1722 std::mem::drop(buttons_listener); let events = read_uapi_events(locked, &input_file, ¤t_task);
1725 assert_eq!(events.len(), 2);
1726 assert_eq!(events[0].code, uapi::KEY_VOLUMEDOWN as u16);
1727 assert_eq!(events[0].value, 1);
1728 }
1729
1730 #[::fuchsia::test]
1731 async fn sends_overlapping_button_events() {
1732 #[allow(deprecated, reason = "pre-existing usage")]
1733 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1734 let (_input_device, input_file, buttons_listener) =
1735 start_button_input(locked, ¤t_task).await;
1736
1737 let power_event = MediaButtonsEvent {
1738 volume: Some(0),
1739 mic_mute: Some(false),
1740 pause: Some(false),
1741 camera_disable: Some(false),
1742 power: Some(true),
1743 function: Some(false),
1744 ..Default::default()
1745 };
1746
1747 let function_event = MediaButtonsEvent {
1748 volume: Some(0),
1749 mic_mute: Some(false),
1750 pause: Some(false),
1751 camera_disable: Some(false),
1752 power: Some(true),
1753 function: Some(true),
1754 ..Default::default()
1755 };
1756
1757 let function_release_event = MediaButtonsEvent {
1758 volume: Some(0),
1759 mic_mute: Some(false),
1760 pause: Some(false),
1761 camera_disable: Some(false),
1762 power: Some(true),
1763 function: Some(false),
1764 ..Default::default()
1765 };
1766
1767 let power_release_event = MediaButtonsEvent {
1768 volume: Some(0),
1769 mic_mute: Some(false),
1770 pause: Some(false),
1771 camera_disable: Some(false),
1772 power: Some(false),
1773 function: Some(false),
1774 ..Default::default()
1775 };
1776
1777 let _ = buttons_listener.on_event(power_event).await;
1778 let _ = buttons_listener.on_event(function_event).await;
1779 let _ = buttons_listener.on_event(function_release_event).await;
1780 let _ = buttons_listener.on_event(power_release_event).await;
1781 std::mem::drop(buttons_listener); let events = read_uapi_events(locked, &input_file, ¤t_task);
1784 assert_eq!(events.len(), 8);
1785 assert_eq!(events[0].code, uapi::KEY_POWER as u16);
1786 assert_eq!(events[0].value, 1);
1787 assert_eq!(events[2].code, uapi::KEY_VOLUMEDOWN as u16);
1788 assert_eq!(events[2].value, 1);
1789 assert_eq!(events[4].code, uapi::KEY_VOLUMEDOWN as u16);
1790 assert_eq!(events[4].value, 0);
1791 assert_eq!(events[6].code, uapi::KEY_POWER as u16);
1792 assert_eq!(events[6].value, 0);
1793 }
1794
1795 #[::fuchsia::test]
1796 async fn sends_simultaneous_button_events() {
1797 #[allow(deprecated, reason = "pre-existing usage")]
1798 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1799 let (_input_device, input_file, buttons_listener) =
1800 start_button_input(locked, ¤t_task).await;
1801
1802 let power_and_function_event = MediaButtonsEvent {
1803 volume: Some(0),
1804 mic_mute: Some(false),
1805 pause: Some(false),
1806 camera_disable: Some(false),
1807 power: Some(true),
1808 function: Some(true),
1809 ..Default::default()
1810 };
1811
1812 let _ = buttons_listener.on_event(power_and_function_event).await;
1813 std::mem::drop(buttons_listener); let events = read_uapi_events(locked, &input_file, ¤t_task);
1816 assert_eq!(events.len(), 4);
1817 assert_eq!(events[0].code, uapi::KEY_POWER as u16);
1818 assert_eq!(events[0].value, 1);
1819 assert_eq!(events[2].code, uapi::KEY_VOLUMEDOWN as u16);
1820 assert_eq!(events[2].value, 1);
1821 }
1822
1823 #[test_case(1; "Scroll up")]
1824 #[test_case(-1; "Scroll down")]
1825 #[::fuchsia::test]
1826 async fn sends_mouse_wheel_events(ticks: i64) {
1827 let time = 100;
1828 let uapi_event = uapi::input_event {
1829 time: timeval_from_time(zx::MonotonicInstant::from_nanos(time)),
1830 type_: uapi::EV_REL as u16,
1831 code: uapi::REL_WHEEL as u16,
1832 value: ticks as i32,
1833 };
1834 #[allow(deprecated, reason = "pre-existing usage")]
1835 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1836 let (_mouse_device, mouse_file, mut mouse_stream) =
1837 start_mouse_input(locked, ¤t_task).await;
1838
1839 answer_next_mouse_watch_request(
1840 &mut mouse_stream,
1841 vec![make_mouse_wheel_event_with_timestamp(ticks, time)],
1842 )
1843 .await;
1844
1845 answer_next_mouse_watch_request(&mut mouse_stream, vec![]).await;
1848
1849 let events = read_uapi_events(locked, &mouse_file, ¤t_task);
1850 assert_eq!(events.len(), 2);
1851 assert_eq!(events[0], uapi_event);
1852 }
1853
1854 #[::fuchsia::test]
1855 async fn ignore_mouse_non_wheel_events() {
1856 let mouse_move_event = fuipointer::MouseEvent {
1857 timestamp: Some(0),
1858 pointer_sample: Some(fuipointer::MousePointerSample {
1859 device_id: Some(0),
1860 position_in_viewport: Some([50.0, 50.0]),
1861 ..Default::default()
1862 }),
1863 ..Default::default()
1864 };
1865 let mouse_click_event = fuipointer::MouseEvent {
1866 timestamp: Some(0),
1867 pointer_sample: Some(fuipointer::MousePointerSample {
1868 device_id: Some(0),
1869 pressed_buttons: Some(vec![1]),
1870 ..Default::default()
1871 }),
1872 ..Default::default()
1873 };
1874 #[allow(deprecated, reason = "pre-existing usage")]
1875 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1876 let (_mouse_device, mouse_file, mut mouse_stream) =
1877 start_mouse_input(locked, ¤t_task).await;
1878
1879 answer_next_mouse_watch_request(&mut mouse_stream, vec![mouse_move_event]).await;
1881 answer_next_mouse_watch_request(&mut mouse_stream, vec![mouse_click_event]).await;
1882
1883 answer_next_mouse_watch_request(&mut mouse_stream, vec![]).await;
1886
1887 let events = read_uapi_events(locked, &mouse_file, ¤t_task);
1888 assert_eq!(events.len(), 0);
1889 }
1890
1891 #[::fuchsia::test]
1892 async fn touch_input_initialized_with_inspect_node() {
1893 #[allow(deprecated, reason = "pre-existing usage")]
1894 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1895 let inspector = fuchsia_inspect::Inspector::default();
1896 let touch_device = InputDevice::new_touch(
1897 1200, 720, &inspector.root(),
1900 );
1901 let _file_obj = touch_device.open_test(locked, ¤t_task);
1902
1903 assert_data_tree!(inspector, root: {
1904 touch_device: {
1905 total_fidl_events_received_count: 0u64,
1906 total_fidl_events_ignored_count: 0u64,
1907 total_fidl_events_unexpected_count: 0u64,
1908 total_fidl_events_converted_count: 0u64,
1909 total_uapi_events_generated_count: 0u64,
1910 last_generated_uapi_event_timestamp_ns: 0i64,
1911 touch_file_0: {
1912 fidl_events_received_count: 0u64,
1913 fidl_events_ignored_count: 0u64,
1914 fidl_events_unexpected_count: 0u64,
1915 fidl_events_converted_count: 0u64,
1916 uapi_events_generated_count: 0u64,
1917 uapi_events_read_count: 0u64,
1918 last_generated_uapi_event_timestamp_ns: 0i64,
1919 last_read_uapi_event_timestamp_ns: 0i64,
1920 }
1921 }
1922 });
1923 }
1924
1925 #[::fuchsia::test]
1926 async fn touch_relay_updates_touch_inspect_status() {
1927 let inspector = fuchsia_inspect::Inspector::default();
1928 #[allow(deprecated, reason = "pre-existing usage")]
1929 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1930 let (_input_device, input_file, mut touch_source_stream) =
1931 start_touch_input_inspect(locked, ¤t_task, &inspector).await;
1932
1933 match touch_source_stream.next().await {
1936 Some(Ok(TouchSourceRequest::Watch { responder, .. })) => responder
1937 .send(vec![make_empty_touch_event(), make_empty_touch_event()])
1938 .expect("failure sending Watch reply"),
1939 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1940 }
1941
1942 match touch_source_stream.next().await {
1945 Some(Ok(TouchSourceRequest::Watch { responses, responder })) => {
1946 assert_matches!(responses.as_slice(), [_, _]);
1947 responder
1948 .send(vec![
1949 make_touch_event_with_coords_phase_timestamp(
1950 0.0,
1951 0.0,
1952 EventPhase::Add,
1953 1,
1954 1000,
1955 ),
1956 make_touch_event_with_coords_phase_timestamp(
1957 1.0,
1958 1.0,
1959 EventPhase::Change,
1960 1,
1961 2000,
1962 ),
1963 make_touch_event_with_coords_phase_timestamp(
1964 2.0,
1965 2.0,
1966 EventPhase::Change,
1967 1,
1968 3000,
1969 ),
1970 make_touch_event_with_coords_phase_timestamp(
1971 3.0,
1972 3.0,
1973 EventPhase::Change,
1974 1,
1975 4000,
1976 ),
1977 make_touch_event_with_coords_phase_timestamp(
1978 3.0,
1979 3.0,
1980 EventPhase::Remove,
1981 1,
1982 5000,
1983 ),
1984 ])
1985 .expect("failure sending Watch reply");
1986 }
1987 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1988 }
1989
1990 match touch_source_stream.next().await {
1992 Some(Ok(TouchSourceRequest::Watch { responses, .. })) => {
1993 assert_matches!(responses.as_slice(), [_, _, _, _, _])
1994 }
1995 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1996 }
1997
1998 let _events = read_uapi_events(locked, &input_file, ¤t_task);
1999 assert_data_tree!(inspector, root: {
2000 touch_device: {
2001 total_fidl_events_received_count: 7u64,
2002 total_fidl_events_ignored_count: 2u64,
2003 total_fidl_events_unexpected_count: 0u64,
2004 total_fidl_events_converted_count: 5u64,
2005 total_uapi_events_generated_count: 22u64,
2006 last_generated_uapi_event_timestamp_ns: 5000i64,
2007 touch_file_0: {
2008 fidl_events_received_count: 7u64,
2009 fidl_events_ignored_count: 2u64,
2010 fidl_events_unexpected_count: 0u64,
2011 fidl_events_converted_count: 5u64,
2012 uapi_events_generated_count: 22u64,
2013 uapi_events_read_count: 22u64,
2014 last_generated_uapi_event_timestamp_ns: 5000i64,
2015 last_read_uapi_event_timestamp_ns: 5000i64,
2016 },
2017 }
2018 });
2019 }
2020
2021 #[::fuchsia::test]
2022 async fn new_file_updates_inspect_status() {
2023 let inspector = fuchsia_inspect::Inspector::default();
2024 #[allow(deprecated, reason = "pre-existing usage")]
2025 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
2026
2027 let input_device = InputDevice::new_touch(700, 700, inspector.root());
2028 let input_file_0 =
2029 input_device.open_test(locked, ¤t_task).expect("Failed to create input file");
2030
2031 let (touch_source_client_end, mut touch_source_stream) =
2032 fidl::endpoints::create_request_stream::<TouchSourceMarker>();
2033 let (mouse_source_client_end, _mouse_source_stream) =
2034 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceMarker>();
2035 let (keyboard_proxy, mut keyboard_stream) =
2036 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
2037 let view_ref_pair =
2038 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
2039 let (device_registry_proxy, mut device_listener_stream) =
2040 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
2041 );
2042
2043 let (relay, relay_handle) = input_event_relay::new_input_relay();
2044 relay.start_relays(
2045 ¤t_task.kernel(),
2046 EventProxyMode::None,
2047 touch_source_client_end,
2048 keyboard_proxy,
2049 mouse_source_client_end,
2050 view_ref_pair.view_ref,
2051 device_registry_proxy,
2052 input_device.open_files.clone(),
2053 Default::default(),
2054 Default::default(),
2055 Some(input_device.inspect_status.clone()),
2056 None,
2057 None,
2058 );
2059
2060 let _ = init_keyboard_listener(&mut keyboard_stream).await;
2061 let _ = init_button_listeners(&mut device_listener_stream).await;
2062
2063 relay_handle.add_touch_device(
2064 0,
2065 input_device.open_files.clone(),
2066 Some(input_device.inspect_status.clone()),
2067 );
2068
2069 match touch_source_stream.next().await {
2072 Some(Ok(TouchSourceRequest::Watch { responder, .. })) => responder
2073 .send(vec![make_empty_touch_event(), make_empty_touch_event()])
2074 .expect("failure sending Watch reply"),
2075 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2076 }
2077
2078 match touch_source_stream.next().await {
2080 Some(Ok(TouchSourceRequest::Watch { responses, responder })) => {
2081 assert_matches!(responses.as_slice(), [_, _]);
2082 responder.send(vec![]).expect("failure sending Watch reply");
2083 }
2084 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2085 }
2086
2087 input_device.open_files.lock().clear();
2089 drop(input_file_0);
2090
2091 let input_file_1 =
2093 input_device.open_test(locked, ¤t_task).expect("Failed to create input file");
2094
2095 match touch_source_stream.next().await {
2098 Some(Ok(TouchSourceRequest::Watch { responder, .. })) => {
2099 responder
2100 .send(vec![
2101 make_touch_event_with_coords_phase_timestamp(
2102 0.0,
2103 0.0,
2104 EventPhase::Add,
2105 1,
2106 1000,
2107 ),
2108 make_touch_event_with_coords_phase_timestamp(
2109 1.0,
2110 1.0,
2111 EventPhase::Change,
2112 1,
2113 2000,
2114 ),
2115 make_touch_event_with_coords_phase_timestamp(
2116 2.0,
2117 2.0,
2118 EventPhase::Change,
2119 1,
2120 3000,
2121 ),
2122 make_touch_event_with_coords_phase_timestamp(
2123 3.0,
2124 3.0,
2125 EventPhase::Change,
2126 1,
2127 4000,
2128 ),
2129 make_touch_event_with_coords_phase_timestamp(
2130 3.0,
2131 3.0,
2132 EventPhase::Remove,
2133 1,
2134 5000,
2135 ),
2136 ])
2137 .expect("failure sending Watch reply");
2138 }
2139 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2140 }
2141
2142 match touch_source_stream.next().await {
2144 Some(Ok(TouchSourceRequest::Watch { responses, .. })) => {
2145 assert_matches!(responses.as_slice(), [_, _, _, _, _])
2146 }
2147 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2148 }
2149
2150 let _events = read_uapi_events(locked, &input_file_1, ¤t_task);
2151
2152 input_device.open_files.lock().clear();
2154 drop(input_file_1);
2155
2156 assert_data_tree!(inspector, root: {
2157 touch_device: {
2158 total_fidl_events_received_count: 7u64,
2159 total_fidl_events_ignored_count: 2u64,
2160 total_fidl_events_unexpected_count: 0u64,
2161 total_fidl_events_converted_count: 5u64,
2162 total_uapi_events_generated_count: 22u64,
2163 last_generated_uapi_event_timestamp_ns: 5000i64,
2164 touch_file_0: {
2165 fidl_events_received_count: 2u64,
2166 fidl_events_ignored_count: 2u64,
2167 fidl_events_unexpected_count: 0u64,
2168 fidl_events_converted_count: 0u64,
2169 uapi_events_generated_count: 0u64,
2170 uapi_events_read_count: 0u64,
2171 last_generated_uapi_event_timestamp_ns: 0i64,
2172 last_read_uapi_event_timestamp_ns: 0i64,
2173 },
2174 touch_file_1: {
2175 fidl_events_received_count: 5u64,
2176 fidl_events_ignored_count: 0u64,
2177 fidl_events_unexpected_count: 0u64,
2178 fidl_events_converted_count: 5u64,
2179 uapi_events_generated_count: 22u64,
2180 uapi_events_read_count: 22u64,
2181 last_generated_uapi_event_timestamp_ns: 5000i64,
2182 last_read_uapi_event_timestamp_ns: 5000i64,
2183 },
2184 }
2185 });
2186 }
2187
2188 #[::fuchsia::test]
2189 async fn keyboard_input_initialized_with_inspect_node() {
2190 #[allow(deprecated, reason = "pre-existing usage")]
2191 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
2192 let inspector = fuchsia_inspect::Inspector::default();
2193 let keyboard_device = InputDevice::new_keyboard(&inspector.root());
2194 let _file_obj = keyboard_device.open_test(locked, ¤t_task);
2195
2196 assert_data_tree!(inspector, root: {
2197 keyboard_device: {
2198 total_fidl_events_received_count: 0u64,
2199 total_fidl_events_ignored_count: 0u64,
2200 total_fidl_events_unexpected_count: 0u64,
2201 total_fidl_events_converted_count: 0u64,
2202 total_uapi_events_generated_count: 0u64,
2203 last_generated_uapi_event_timestamp_ns: 0i64,
2204 keyboard_file_0: {
2205 fidl_events_received_count: 0u64,
2206 fidl_events_ignored_count: 0u64,
2207 fidl_events_unexpected_count: 0u64,
2208 fidl_events_converted_count: 0u64,
2209 uapi_events_generated_count: 0u64,
2210 uapi_events_read_count: 0u64,
2211 last_generated_uapi_event_timestamp_ns: 0i64,
2212 last_read_uapi_event_timestamp_ns: 0i64,
2213 }
2214 }
2215 });
2216 }
2217
2218 #[::fuchsia::test]
2219 async fn button_relay_updates_keyboard_inspect_status() {
2220 let inspector = fuchsia_inspect::Inspector::default();
2221 #[allow(deprecated, reason = "pre-existing usage")]
2222 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
2223 let (_input_device, input_file, buttons_listener) =
2224 start_button_input_inspect(locked, ¤t_task, &inspector).await;
2225
2226 let power_event = MediaButtonsEvent {
2229 volume: Some(0),
2230 mic_mute: Some(false),
2231 pause: Some(false),
2232 camera_disable: Some(false),
2233 power: Some(true),
2234 function: Some(false),
2235 ..Default::default()
2236 };
2237
2238 let power_release_event = MediaButtonsEvent {
2239 volume: Some(0),
2240 mic_mute: Some(false),
2241 pause: Some(false),
2242 camera_disable: Some(false),
2243 power: Some(false),
2244 function: Some(false),
2245 ..Default::default()
2246 };
2247
2248 let _ = buttons_listener.on_event(power_event).await;
2249 let _ = buttons_listener.on_event(power_release_event).await;
2250
2251 let events = read_uapi_events(locked, &input_file, ¤t_task);
2252 assert_eq!(events.len(), 4);
2253 assert_eq!(events[0].code, uapi::KEY_POWER as u16);
2254 assert_eq!(events[0].value, 1);
2255 assert_eq!(events[2].code, uapi::KEY_POWER as u16);
2256 assert_eq!(events[2].value, 0);
2257
2258 let _events = read_uapi_events(locked, &input_file, ¤t_task);
2259
2260 assert_data_tree!(inspector, root: {
2261 keyboard_device: {
2262 total_fidl_events_received_count: 2u64,
2263 total_fidl_events_ignored_count: 0u64,
2264 total_fidl_events_unexpected_count: 0u64,
2265 total_fidl_events_converted_count: 2u64,
2266 total_uapi_events_generated_count: 4u64,
2267 last_generated_uapi_event_timestamp_ns: AnyProperty,
2269 keyboard_file_0: {
2270 fidl_events_received_count: 2u64,
2271 fidl_events_ignored_count: 0u64,
2272 fidl_events_unexpected_count: 0u64,
2273 fidl_events_converted_count: 2u64,
2274 uapi_events_generated_count: 4u64,
2275 uapi_events_read_count: 4u64,
2276 last_generated_uapi_event_timestamp_ns: AnyProperty,
2278 last_read_uapi_event_timestamp_ns: AnyProperty,
2279 },
2280 }
2281 });
2282 }
2283
2284 #[::fuchsia::test]
2285 async fn mouse_input_initialized_with_inspect_node() {
2286 #[allow(deprecated, reason = "pre-existing usage")]
2287 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
2288 let inspector = fuchsia_inspect::Inspector::default();
2289 let mouse_device = InputDevice::new_mouse(&inspector.root());
2290 let _file_obj = mouse_device.open_test(locked, ¤t_task);
2291
2292 assert_data_tree!(inspector, root: {
2293 mouse_device: {
2294 total_fidl_events_received_count: 0u64,
2295 total_fidl_events_ignored_count: 0u64,
2296 total_fidl_events_unexpected_count: 0u64,
2297 total_fidl_events_converted_count: 0u64,
2298 total_uapi_events_generated_count: 0u64,
2299 last_generated_uapi_event_timestamp_ns: 0i64,
2300 mouse_file_0: {
2301 fidl_events_received_count: 0u64,
2302 fidl_events_ignored_count: 0u64,
2303 fidl_events_unexpected_count: 0u64,
2304 fidl_events_converted_count: 0u64,
2305 uapi_events_generated_count: 0u64,
2306 uapi_events_read_count: 0u64,
2307 last_generated_uapi_event_timestamp_ns: 0i64,
2308 last_read_uapi_event_timestamp_ns: 0i64,
2309 }
2310 }
2311 });
2312 }
2313
2314 #[::fuchsia::test]
2315 async fn mouse_relay_updates_mouse_inspect_status() {
2316 let inspector = fuchsia_inspect::Inspector::default();
2317 #[allow(deprecated, reason = "pre-existing usage")]
2318 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
2319 let (_input_device, input_file, mut mouse_source_stream) =
2320 start_mouse_input_inspect(locked, ¤t_task, &inspector).await;
2321
2322 let mouse_move_event = fuipointer::MouseEvent {
2323 timestamp: Some(0),
2324 pointer_sample: Some(fuipointer::MousePointerSample {
2325 device_id: Some(0),
2326 position_in_viewport: Some([50.0, 50.0]),
2327 scroll_v: Some(0),
2328 ..Default::default()
2329 }),
2330 ..Default::default()
2331 };
2332 let mouse_click_event = fuipointer::MouseEvent {
2333 timestamp: Some(0),
2334 pointer_sample: Some(fuipointer::MousePointerSample {
2335 device_id: Some(0),
2336 scroll_v: Some(0),
2337 pressed_buttons: Some(vec![1]),
2338 ..Default::default()
2339 }),
2340 ..Default::default()
2341 };
2342
2343 answer_next_mouse_watch_request(
2346 &mut mouse_source_stream,
2347 vec![mouse_move_event, mouse_click_event],
2348 )
2349 .await;
2350
2351 answer_next_mouse_watch_request(
2354 &mut mouse_source_stream,
2355 (0..5).map(|_| make_mouse_wheel_event(1)).collect(),
2356 )
2357 .await;
2358
2359 answer_next_mouse_watch_request(
2362 &mut mouse_source_stream,
2363 vec![make_mouse_wheel_event_with_timestamp(-1, 5000)],
2364 )
2365 .await;
2366
2367 answer_next_mouse_watch_request(&mut mouse_source_stream, vec![]).await;
2370
2371 let _events = read_uapi_events(locked, &input_file, ¤t_task);
2372 assert_data_tree!(inspector, root: {
2373 mouse_device: {
2374 total_fidl_events_received_count: 8u64,
2375 total_fidl_events_ignored_count: 2u64,
2376 total_fidl_events_unexpected_count: 0u64,
2377 total_fidl_events_converted_count: 6u64,
2378 total_uapi_events_generated_count: 8u64,
2379 last_generated_uapi_event_timestamp_ns: 5000i64,
2380 mouse_file_0: {
2381 fidl_events_received_count: 8u64,
2382 fidl_events_ignored_count: 2u64,
2383 fidl_events_unexpected_count: 0u64,
2384 fidl_events_converted_count: 6u64,
2385 uapi_events_generated_count: 8u64,
2386 uapi_events_read_count: 8u64,
2387 last_generated_uapi_event_timestamp_ns: 5000i64,
2388 last_read_uapi_event_timestamp_ns: 5000i64,
2389 },
2390 }
2391 });
2392 }
2393}