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