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