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