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